diff --git a/lang/chs/lang_functions.php b/lang/chs/lang_functions.php
index 1eefac95..50456d18 100644
--- a/lang/chs/lang_functions.php
+++ b/lang/chs/lang_functions.php
@@ -312,8 +312,11 @@ $lang_functions = array
'text_tag_hdr' => 'HDR',
'text_required' => '不能为空',
'text_invalid' => '非法',
- 'text_technical_info' => 'MediaInfo',
- 'text_technical_info_help_text' => 'MediaInfo 来自软件 MediaInfo,用该软件打开文件,语言选择英语,点击菜单视图(View)->文件(Text),在框中右键->全选,再右键->复制,粘贴到这里来。',
+ 'text_technical_info' => 'MediaInfo/BDInfo',
+ 'text_technical_info_help_text' => '· MediaInfo 来自软件 MediaInfo,用该软件打开文件,语言选择英语,点击菜单视图(View)->文件(Text),在框中右键->全选,再右键->复制,粘贴到这里来。
+
+ · BDInfo 来自软件 BDInfo,用该软件打开文件,点击扫描比特率(Scan Bitrates) 查看报告(View Report...),在框中右键->全选,再右键->复制,粘贴到这里来。
+ ',
'text_management_system' => '管理系统',
'text_seed_points' => '做种积分',
'spoiler_expand_collapse' => '点击展开/收缩',
diff --git a/lang/chs/lang_settings.php b/lang/chs/lang_settings.php
index 716b1c2a..53504478 100644
--- a/lang/chs/lang_settings.php
+++ b/lang/chs/lang_settings.php
@@ -707,8 +707,11 @@ $lang_settings = array
'text_login_secret_lifetime_unit' => '分钟',
'row_login_secret_lifetime' => '登录密钥有效期',
'text_login_secret_lifetime_deadline' => '当前密钥有效期至',
- 'row_enable_technical_info' => '启用 MediaInfo',
- 'text_enable_technical_info' => "默认'否'。MediaInfo 来自软件 MediaInfo Text 视图的结果",
+ 'row_enable_technical_info' => '启用 MediaInfo/BDInfo',
+ 'text_enable_technical_info' => '默认\'否\'。
+ MediaInfo 来自软件 MediaInfo Text 视图的结果
+ BDInfo 来自软件 BDInfo View Reports的结果
+ ',
'row_sticky_first_level_background_color' => '一级置顶背景颜色',
'text_sticky_first_level_background_color_note' => '一级置顶背景颜色,不设置则无背景色。',
'row_sticky_second_level_background_color' => '二级置顶背景颜色',
diff --git a/lang/cht/lang_functions.php b/lang/cht/lang_functions.php
index 2b6f5ca3..fd361bcb 100644
--- a/lang/cht/lang_functions.php
+++ b/lang/cht/lang_functions.php
@@ -319,8 +319,11 @@ $lang_functions = array
'text_tag_hdr' => 'HDR',
'text_required' => '不能為空',
'text_invalid' => '非法',
- 'text_technical_info' => 'MediaInfo',
- 'text_technical_info_help_text' => 'MediaInfo 來自軟件 MediaInfo,用該軟件打開文件,語言選擇英語,點擊菜單視圖(View)->文件(Text),在框中右鍵->全選,再右鍵->復制,粘貼到這裏來。',
+ 'text_technical_info' => 'MediaInfo/BDInfo',
+ 'text_technical_info_help_text' => '· MediaInfo 来自軟體 MediaInfo,用該軟體打開文件,語言選擇英語,點擊菜單視圖(View)->文件(Text),在框中右鍵->全選,再右鍵->複製,粘貼到這裡來。
+
+ · BDInfo 来自軟體 BDInfo,用該軟體打開,點擊掃描比特率(Scan Bitrates) 查看報告(View Report...),在框中右鍵->全選,再右鍵->複製,粘貼到這裡來。
+ ',
'text_management_system' => '管理系統',
'text_seed_points' => '做種積分',
'spoiler_expand_collapse' => '點擊展開/收縮',
diff --git a/lang/cht/lang_settings.php b/lang/cht/lang_settings.php
index e50e42ef..94bacd86 100644
--- a/lang/cht/lang_settings.php
+++ b/lang/cht/lang_settings.php
@@ -707,8 +707,11 @@ $lang_settings = array
'text_login_secret_lifetime_unit' => '分鐘',
'row_login_secret_lifetime' => '登錄密鑰有效期',
'text_login_secret_lifetime_deadline' => '當前密鑰有效期至',
- 'row_enable_technical_info' => '啟用 MediaInfo',
- 'text_enable_technical_info' => "默認'否'。MediaInfo 來自軟件 MediaInfo Text 視圖的結果",
+ 'row_enable_technical_info' => '啓用 MediaInfo/BDInfo',
+ 'text_enable_technical_info' => '默认\'否\'。
+ MediaInfo 来自軟體 MediaInfo Text 視圖的結果
+ BDInfo 来自軟體 BDInfo View Reports的結果
+ ',
'row_sticky_first_level_background_color' => '一級置頂背景顏色',
'text_sticky_first_level_background_color_note' => '一級置頂背景顏色,不設置則無背景色。',
'row_sticky_second_level_background_color' => '二級置頂背景顏色',
diff --git a/lang/en/lang_functions.php b/lang/en/lang_functions.php
index 27559fb9..f936a931 100644
--- a/lang/en/lang_functions.php
+++ b/lang/en/lang_functions.php
@@ -320,8 +320,11 @@ $lang_functions = array
'text_tag_hdr' => 'HDR',
'text_required' => 'Required',
'text_invalid' => 'Invalid',
- 'text_technical_info' => 'MediaInfo',
- 'text_technical_info_help_text' => 'MediaInfo comes from software MediaInfo,open file, language select english, click the view menu > text > right click in the box > select all > copy > past into this box.',
+ 'text_technical_info' => 'MediaInfo/BDInfo',
+ 'text_technical_info_help_text' => '· MediaInfo comes from software MediaInfo,open file, language select english, click the view menu > text > right click in the box > select all > copy > past into this box.
+
+ · BDInfo comes from software BDInfo,open the file with this software, click Scan Bitrates to view the report, right-click in the box > select all, right-click > copy, and paste it here.
+ ',
'text_management_system' => 'Management',
'text_seed_points' => 'Seed points',
'spoiler_expand_collapse' => 'Click to expand/collapse',
diff --git a/lang/en/lang_settings.php b/lang/en/lang_settings.php
index f6b4e405..bdd2c05b 100644
--- a/lang/en/lang_settings.php
+++ b/lang/en/lang_settings.php
@@ -707,9 +707,12 @@ $lang_settings = array
'text_login_secret_lifetime_unit' => 'minute',
'row_login_secret_lifetime' => 'Login secret lifetime',
'text_login_secret_lifetime_deadline' => 'Current login secret deadline',
- 'row_enable_technical_info' => 'Enable MediaInfo',
- 'text_enable_technical_info' => "Default 'No'. MediaInfo comes from software MediaInfo Text view",
- 'row_sticky_first_level_background_color' => 'Sticky first level bg color',
+ 'row_enable_technical_info' => 'Enable MediaInfo/BDInfo',
+ 'text_enable_technical_info' => 'Default \'No\'.
+ MediaInfo comes from software MediaInfo the result of "Text view"
+ BDInfo comes from software BDInfo the result of "View Reports"
+ ',
+ 'row_sticky_first_level_background_color' => 'Sticky first level bg color',
'text_sticky_first_level_background_color_note' => 'Sticky first level bg color, it will be none if not set.',
'row_sticky_second_level_background_color' => 'Sticky second level bg color',
'text_sticky_second_level_background_color_note' => 'Sticky second level bg color, it will be none if not set.',
diff --git a/nexus/Torrent/BdInfoExtra.php b/nexus/Torrent/BdInfoExtra.php
new file mode 100644
index 00000000..13ddad9e
--- /dev/null
+++ b/nexus/Torrent/BdInfoExtra.php
@@ -0,0 +1,1093 @@
+bdInfo = $bdInfo;
+ $this->bdInfoArr = $this->parseBdInfo($bdInfo);
+ }
+
+ /**
+ * 解析BDINFO文本为结构化数组
+ */
+ private function parseBdInfo(string $bdInfo): array
+ {
+ $lines = preg_split('/[\r\n]+/', $bdInfo);
+
+ // 检测是否为Summary格式(无章节标题的格式)
+ $isSummaryFormat = $this->isSummaryFormat($lines);
+
+ if ($isSummaryFormat) {
+ $result = [
+ 'disc_info' => [],
+ 'playlist_report' => [],
+ 'video' => [],
+ 'audio' => [],
+ 'subtitles' => []
+ ];
+ return $this->summaryFormat($lines, $result);
+ } else {
+ return $this->normalFormat($lines);
+ }
+ }
+
+ /**
+ * 检测是否为Summary格式
+ */
+ private function isSummaryFormat(array $lines): bool
+ {
+ foreach ($lines as $line) {
+ $line = $this->trim($line);
+ if (strpos($line, 'DISC INFO:') !== false ||
+ strpos($line, 'PLAYLIST REPORT:') !== false ||
+ strpos($line, 'VIDEO:') !== false ||
+ strpos($line, 'AUDIO:') !== false ||
+ strpos($line, 'SUBTITLES:') !== false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 解析格式1(有章节标题的格式)
+ */
+ private function normalFormat(array $lines): array
+ {
+ $discs = [];
+ $currentDisc = null;
+ $currentSection = '';
+ $audioIndex = 0;
+ $subtitleIndex = 0;
+
+ foreach ($lines as $line) {
+ $line = $this->trim($line);
+ if (empty($line)) {
+ continue;
+ }
+
+ // 检测新的DISC
+ if (strpos($line, 'DISC INFO:') !== false) {
+ // 保存之前的DISC(如果存在)
+ if ($currentDisc !== null) {
+ $discs[] = $currentDisc;
+ }
+
+ // 创建新的DISC
+ $currentDisc = [
+ 'disc_info' => [],
+ 'playlist_report' => [],
+ 'video' => [],
+ 'audio' => [],
+ 'subtitles' => []
+ ];
+ $currentSection = 'disc_info';
+ $audioIndex = 0;
+ $subtitleIndex = 0;
+ continue;
+ } elseif (strpos($line, 'PLAYLIST REPORT:') !== false) {
+ $currentSection = 'playlist_report';
+ continue;
+ } elseif (strpos($line, 'VIDEO:') !== false) {
+ $currentSection = 'video';
+ continue;
+ } elseif (strpos($line, 'AUDIO:') !== false) {
+ $currentSection = 'audio';
+ continue;
+ } elseif (strpos($line, 'SUBTITLES:') !== false) {
+ $currentSection = 'subtitles';
+ continue;
+ } elseif (strpos($line, 'CHAPTERS:') !== false || strpos($line, 'STREAM DIAGNOSTICS:') !== false) {
+ $currentSection = '';
+ continue;
+ }
+
+ // 解析各个章节的内容
+ if ($currentDisc !== null && !empty($currentSection)) {
+ switch ($currentSection) {
+ case 'disc_info':
+ $this->parseDiscInfo($line, $currentDisc['disc_info']);
+ break;
+ case 'playlist_report':
+ $this->parsePlaylistReport($line, $currentDisc['playlist_report']);
+ break;
+ case 'video':
+ $this->parseVideo($line, $currentDisc['video']);
+ break;
+ case 'audio':
+ $this->parseAudio($line, $currentDisc['audio'], $audioIndex);
+ break;
+ case 'subtitles':
+ $this->parseSubtitles($line, $currentDisc['subtitles'], $subtitleIndex);
+ break;
+ }
+ }
+ }
+
+ // 保存最后一个DISC
+ if ($currentDisc !== null) {
+ $discs[] = $currentDisc;
+ }
+
+ // 如果没有找到任何DISC,返回空结构
+ if (empty($discs)) {
+ return [
+ 'disc_info' => [],
+ 'playlist_report' => [],
+ 'video' => [],
+ 'audio' => [],
+ 'subtitles' => []
+ ];
+ }
+
+ // 返回第一个DISC的数据(保持向后兼容)
+ return $discs[0];
+ }
+
+ /**
+ * 解析Summary格式(无章节标题的格式)
+ */
+ private function summaryFormat(array $lines, array $result): array
+ {
+ $audioIndex = 0;
+ $subtitleIndex = 0;
+
+ foreach ($lines as $line) {
+ $line = $this->trim($line);
+ if (empty($line)) {
+ continue;
+ }
+
+ // 解析光盘信息
+ if (strpos($line, 'Disc Label:') !== false) {
+ $result['disc_info']['label'] = trim(substr($line, 11));
+ } elseif (strpos($line, 'Disc Size:') !== false) {
+ $result['disc_info']['size'] = trim(substr($line, 10));
+ } elseif (strpos($line, 'Protection:') !== false) {
+ $result['disc_info']['protection'] = trim(substr($line, 11));
+ } elseif (strpos($line, 'Playlist:') !== false) {
+ $result['playlist_report']['name'] = trim(substr($line, 9));
+ } elseif (strpos($line, 'Size:') !== false) {
+ $result['playlist_report']['size'] = trim(substr($line, 5));
+ } elseif (strpos($line, 'Length:') !== false) {
+ $result['playlist_report']['length'] = trim(substr($line, 7));
+ } elseif (strpos($line, 'Total Bitrate:') !== false) {
+ $result['playlist_report']['total_bitrate'] = trim(substr($line, 14));
+ } elseif (strpos($line, 'Video:') !== false) {
+ $this->summaryFormatVideo($line, $result['video']);
+ } elseif (strpos($line, 'Audio:') !== false) {
+ $this->summaryFormatAudio($line, $result['audio'], $audioIndex);
+ } elseif (strpos($line, 'Subtitle:') !== false) {
+ $this->summaryFormatSubtitle($line, $result['subtitles'], $subtitleIndex);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * 解析Summary格式的视频信息
+ */
+ private function summaryFormatVideo(string $line, array &$video): void
+ {
+ // 格式:Video: MPEG-4 AVC Video / 31943 kbps / 1080p / 23.976 fps / 16:9 / High Profile 4.1
+ if (preg_match('/Video:\s*(.+?)\s*\/\s*(\d+)\s*kbps\s*\/(.+)/', $line, $matches)) {
+ $video['codec'] = trim($matches[1]);
+ $video['bitrate'] = trim($matches[2]) . ' kbps';
+ $video['description'] = trim($matches[3]);
+ }
+ }
+
+ /**
+ * 解析Summary格式的音频信息
+ */
+ private function summaryFormatAudio(string $line, array &$audio, int &$audioIndex): void
+ {
+ // 格式:Audio: Chinese / DTS-HD Master Audio / 2.0 / 48 kHz / 914 kbps / 16-bit (DTS Core: 2.0 / 48 kHz / 768 kbps / 16-bit)
+ if (preg_match('/Audio:\s*([^*]+?)\s*\/\s*([^*]+?)\s*\/\s*([^*]+?)\s*\/\s*([^*]+?)\s*\/\s*([^*]+?)\s*\/\s*([^*]+?)(?:\s*\((.+)\))?/', $line, $matches)) {
+ $bitrate = trim($matches[5]);
+ // 如果码率已经包含kbps,就不重复添加
+ if (strpos($bitrate, 'kbps') === false) {
+ $bitrate .= ' kbps';
+ }
+
+ $audio[$audioIndex] = [
+ 'language' => trim($matches[1]),
+ 'codec' => trim($matches[2]),
+ 'channels' => trim($matches[3]),
+ 'sample_rate' => trim($matches[4]),
+ 'bitrate' => $bitrate,
+ 'bit_depth' => trim($matches[6]),
+ 'description' => isset($matches[7]) ? trim($matches[7]) : ''
+ ];
+ $audioIndex++;
+ }
+ }
+
+ /**
+ * 解析Summary格式的字幕信息
+ */
+ private function summaryFormatSubtitle(string $line, array &$subtitles, int &$subtitleIndex): void
+ {
+ // 格式:Subtitle: English / 38.300 kbps
+ if (preg_match('/Subtitle:\s*([^*]+?)\s*\/\s*([^*]+?)\s*kbps/', $line, $matches)) {
+ $subtitles[$subtitleIndex] = [
+ 'language' => trim($matches[1]),
+ 'bitrate' => trim($matches[2]) . ' kbps',
+ 'codec' => 'Presentation Graphics',
+ 'description' => ''
+ ];
+ $subtitleIndex++;
+ }
+ }
+
+ /**
+ * 解析光盘信息
+ */
+ private function parseDiscInfo(string $line, array &$discInfo): void
+ {
+ if (strpos($line, 'Disc Title:') !== false) {
+ $discInfo['title'] = trim(substr($line, 11));
+ } elseif (strpos($line, 'Disc Label:') !== false) {
+ $discInfo['label'] = trim(substr($line, 11));
+ } elseif (strpos($line, 'Disc Size:') !== false) {
+ $discInfo['size'] = trim(substr($line, 10));
+ } elseif (strpos($line, 'Protection:') !== false) {
+ $discInfo['protection'] = trim(substr($line, 11));
+ } elseif (strpos($line, 'Extras:') !== false) {
+ $discInfo['extras'] = trim(substr($line, 7));
+ }
+ }
+
+ /**
+ * 解析播放列表报告
+ */
+ private function parsePlaylistReport(string $line, array &$playlistReport): void
+ {
+ if (strpos($line, 'Name:') !== false) {
+ $playlistReport['name'] = trim(substr($line, 5));
+ } elseif (strpos($line, 'Length:') !== false) {
+ $playlistReport['length'] = trim(substr($line, 7));
+ } elseif (strpos($line, 'Size:') !== false) {
+ $playlistReport['size'] = trim(substr($line, 5));
+ } elseif (strpos($line, 'Total Bitrate:') !== false) {
+ $playlistReport['total_bitrate'] = trim(substr($line, 14));
+ }
+ }
+
+ /**
+ * 解析视频信息
+ */
+ private function parseVideo(string $line, array &$video): void
+ {
+ // 跳过表头和分隔线
+ if (strpos($line, 'Codec') !== false || strpos($line, '-----') !== false || strpos($line, 'Description') !== false) {
+ return;
+ }
+
+ // 解析视频行 - 包括隐藏视频流(带*号的)
+ if (preg_match('/^(\*?\s*)(.+?)\s+(\d+)\s+kbps\s+(.+)$/', $line, $matches)) {
+ $isHidden = strpos($matches[1], '*') !== false;
+
+ if (!$isHidden) {
+ // 主视频流 - 支持多个视频流
+ // 添加到数组末尾
+ $video[] = [
+ 'codec' => trim($matches[2]),
+ 'bitrate' => trim($matches[3]) . ' kbps',
+ 'description' => trim($matches[4])
+ ];
+ $videoIndex = count($video) - 1;
+
+ // 提取分辨率信息(对每个视频流都提取)
+ $description = trim($matches[4]);
+ if (preg_match('/(\d+)p/', $description, $resMatches)) {
+ $video['height'] = $resMatches[1];
+ }
+ // 只有当描述中包含aspect_ratio时才提取
+ if (preg_match('/(\d+:\d+)/', $description, $ratioMatches)) {
+ $video['aspect_ratio'] = $ratioMatches[1];
+ }
+ } else {
+ // 隐藏视频流,检查是否包含Dolby Vision信息
+ if (strpos($matches[4], 'Dolby Vision') !== false) {
+ // 将Dolby Vision信息添加到第一个视频流描述中
+ if (isset($video[0]['description'])) {
+ $video[0]['description'] .= ' / ' . trim($matches[4]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 提取括号内容(全角括号和半角括号)
+ */
+ private function extractChineseBrackets(string $text): array
+ {
+ $result = ['text' => $text, 'chinese_brackets' => []];
+
+ // 匹配全角括号内容(提取非英文内容)
+ if (preg_match_all('/(([^)]+))/u', $text, $matches)) {
+ foreach ($matches[1] as $match) {
+ // 检查是否包含非英文字符
+ if (preg_match('/[^\x{0000}-\x{007F}]/u', $match)) {
+ $result['chinese_brackets'][] = $match;
+ }
+ }
+
+ // 从原文本中移除包含非英文的全角括号内容
+ $result['text'] = preg_replace('/([^)]*[^\x{0000}-\x{007F}][^)]*)/u', '', $text);
+ }
+
+ // 匹配半角括号内容(提取非英文内容)
+ if (preg_match_all('/\(([^)]+)\)/', $text, $matches)) {
+ foreach ($matches[1] as $match) {
+ // 检查是否包含非英文字符
+ if (preg_match('/[^\x{0000}-\x{007F}]/u', $match)) {
+ $result['chinese_brackets'][] = $match;
+ }
+ }
+
+ // 从原文本中移除包含非英文的半角括号内容
+ $result['text'] = preg_replace('/\([^)]*[^\x{0000}-\x{007F}][^)]*\)/u', '', $result['text']);
+ }
+
+ $result['text'] = trim($result['text']);
+ return $result;
+ }
+
+ /**
+ * 解析音频信息
+ */
+ private function parseAudio(string $line, array &$audio, int &$audioIndex): void
+ {
+ // 跳过表头和分隔线
+ if (strpos($line, 'Codec') !== false || strpos($line, '-----') !== false || strpos($line, 'Language') !== false) {
+ return;
+ }
+
+ // 解析音频行 - 格式:DTS-HD Master Audio English 1564 kbps 2.0 / 48 kHz / 1564 kbps / 24-bit
+ // 也包含隐藏音频流(带*号的)
+ if (preg_match('/^(\*?\s*)(.+?)\s+([A-Za-z]+)\s+(\d+)\s+kbps\s+(.+)$/', $line, $matches)) {
+ $description = trim($matches[5]);
+
+ // 提取括号内容
+ $extracted = $this->extractChineseBrackets($description);
+ $chineseBrackets = $extracted['chinese_brackets'];
+ $cleanDescription = $extracted['text'];
+
+ $audio[$audioIndex] = [
+ 'codec' => trim($matches[2]),
+ 'language' => trim($matches[3]),
+ 'bitrate' => trim($matches[4]) . ' kbps',
+ 'description' => $cleanDescription,
+ 'chinese_brackets' => $chineseBrackets
+ ];
+ $audioIndex++;
+ }
+ }
+
+ /**
+ * 解析字幕信息
+ */
+ private function parseSubtitles(string $line, array &$subtitles, int &$subtitleIndex): void
+ {
+ // 跳过表头和分隔线
+ if (strpos($line, 'Codec') !== false || strpos($line, '-----') !== false || strpos($line, 'Language') !== false) {
+ return;
+ }
+
+ // 跳过FILES章节的内容
+ if (strpos($line, 'Name') !== false || strpos($line, 'Time In') !== false || strpos($line, 'Length') !== false || strpos($line, 'Size') !== false || strpos($line, 'Total Bitrate') !== false) {
+ return;
+ }
+
+ // 跳过文件行(如:00003.M2TS 0:00:00.000 2:00:29.416)
+ if (preg_match('/^\w+\.M2TS\s+/', $line)) {
+ return;
+ }
+
+ // 解析字幕行 - 格式:Presentation Graphics English 21.061 kbps
+ // 优先匹配"Presentation Graphics"开头的行
+ if (preg_match('/^(Presentation Graphics)\s+([^*]+?)\s+([^*]+?)\s+kbps\s*(.*)$/', $line, $matches)) {
+ $codec = trim($matches[1]);
+ $language = trim($matches[2]);
+ $bitrate = trim($matches[3]) . ' kbps';
+ $description = trim($matches[4]);
+
+ // 只有当语言不为空时才添加
+ if (!empty($language)) {
+ // 提取括号内容
+ $extracted = $this->extractChineseBrackets($description);
+ $chineseBrackets = $extracted['chinese_brackets'];
+ $cleanDescription = $extracted['text'];
+
+ $subtitles[$subtitleIndex] = [
+ 'codec' => $codec,
+ 'language' => $language,
+ 'bitrate' => $bitrate,
+ 'description' => $cleanDescription,
+ 'chinese_brackets' => $chineseBrackets
+ ];
+ $subtitleIndex++;
+ }
+ }
+ }
+
+ /**
+ * 获取时长
+ */
+ public function getDuration(): string
+ {
+ $length = $this->bdInfoArr['playlist_report']['length'] ?? '';
+ if (empty($length)) {
+ return '';
+ }
+
+ // 转换格式:1:55:22.123 -> 1h 55m 22s 123ms
+ if (preg_match('/(\d+):(\d+):(\d+)\.(\d+)/', $length, $matches)) {
+ $hours = intval($matches[1]);
+ $minutes = intval($matches[2]);
+ $seconds = intval($matches[3]);
+ $milliseconds = intval($matches[4]);
+
+ return sprintf('%dh %02dm %02ds %03dms', $hours, $minutes, $seconds, $milliseconds);
+ }
+
+ return $length;
+ }
+
+ /**
+ * 获取总码率
+ */
+ public function getTotalBitrate(): string
+ {
+ return $this->bdInfoArr['playlist_report']['total_bitrate'] ?? '';
+ }
+
+ /**
+ * 获取帧率
+ */
+ public function getFrameRate(): string
+ {
+ $description = $this->bdInfoArr['video']['description'] ?? '';
+ if (preg_match('/(\d+\.?\d*)\s+fps/', $description, $matches)) {
+ return $matches[1] . ' fps';
+ }
+ return '';
+ }
+
+ /**
+ * 获取视频配置文件
+ */
+ public function getProfile(): string
+ {
+ $profiles = [];
+
+ // 检查所有视频流
+ foreach ($this->bdInfoArr['video'] as $key => $video) {
+ if (is_array($video) && isset($video['description'])) {
+ $description = $video['description'];
+ if (preg_match('/([^\/]*?(?:profile|high|level|main)[^\/]*?)(?:\s*\/|$)/i', $description, $matches)) {
+ $profiles[] = trim($matches[1]);
+ }
+ }
+ }
+
+ // 如果没有找到profile,检查是否是summaryFormat格式(关联数组)
+ if (empty($profiles) && isset($this->bdInfoArr['video']['description'])) {
+ $description = $this->bdInfoArr['video']['description'];
+ if (preg_match('/([^\/]*?(?:profile|high|level|main)[^\/]*?)(?:\s*\/|$)/i', $description, $matches)) {
+ $profiles[] = trim($matches[1]);
+ }
+ }
+
+ return implode(' / ', $profiles);
+ }
+
+ public function getResolution(): string
+ {
+ $resolutions = [];
+
+ // 遍历所有视频流提取分辨率和宽高比
+ foreach ($this->bdInfoArr['video'] as $index => $video) {
+ // 处理数字索引的数组(多视频流格式)或关联数组(单视频流格式)
+ if (is_array($video) && isset($video['description'])) {
+ $description = $video['description'];
+ $resolutionItem = '';
+
+ // 提取"xxxp"格式的分辨率
+ if (preg_match('/(\d+p)/', $description, $matches)) {
+ $resolutionItem = $matches[1];
+ }
+
+ // 提取宽高比信息
+ if (preg_match('/(\d+:\d+)/', $description, $ratioMatches)) {
+ $resolutionItem .= "(" . $ratioMatches[1] . ")";
+ }
+
+ if (!empty($resolutionItem)) {
+ $resolutions[] = $resolutionItem;
+ }
+ }
+ }
+
+ // 如果没有找到分辨率,检查是否是summaryFormat格式(关联数组)
+ if (empty($resolutions) && isset($this->bdInfoArr['video']['description'])) {
+ $description = $this->bdInfoArr['video']['description'];
+ $resolutionItem = '';
+
+ // 提取"xxxp"格式的分辨率
+ if (preg_match('/(\d+p)/', $description, $matches)) {
+ $resolutionItem = $matches[1];
+ }
+
+ // 提取宽高比信息
+ if (preg_match('/(\d+:\d+)/', $description, $ratioMatches)) {
+ $resolutionItem .= "(" . $ratioMatches[1] . ")";
+ }
+
+ if (!empty($resolutionItem)) {
+ $resolutions[] = $resolutionItem;
+ }
+ }
+
+ return implode(' / ', $resolutions);
+ }
+
+ public function getBitDepth(): string
+ {
+ // 从第一个视频流获取位深度信息
+ $firstVideo = $this->bdInfoArr['video'][0] ?? null;
+ if ($firstVideo && isset($firstVideo['description'])) {
+ $description = $firstVideo['description'];
+ if (preg_match('/(\d+)\s+bits/', $description, $matches)) {
+ return $matches[1] . ' bits';
+ }
+ }
+
+ // 如果没有找到位深度,检查是否是summaryFormat格式(关联数组)
+ if (isset($this->bdInfoArr['video']['description'])) {
+ $description = $this->bdInfoArr['video']['description'];
+ if (preg_match('/(\d+)\s+bits/', $description, $matches)) {
+ return $matches[1] . ' bits';
+ }
+ }
+
+ return '';
+ }
+
+ public function getVideoFormat(): string
+ {
+ $formats = [];
+
+ // 检查所有视频流
+ foreach ($this->bdInfoArr['video'] as $key => $video) {
+ if (is_array($video) && isset($video['codec'])) {
+ $formats[] = $video['codec'];
+ }
+ }
+
+ // 如果没有找到格式,检查是否是summaryFormat格式(关联数组)
+ if (empty($formats) && isset($this->bdInfoArr['video']['codec'])) {
+ $formats[] = $this->bdInfoArr['video']['codec'];
+ }
+
+ return implode(' / ', $formats);
+ }
+
+ /**
+ * 获取宽高比
+ */
+ public function getAspectRatio(): string
+ {
+ return $this->bdInfoArr['video']['aspect_ratio'] ?? '';
+ }
+
+ /**
+ * 获取Extras信息
+ */
+ public function getExtras(): string
+ {
+ return $this->bdInfoArr['disc_info']['extras'] ?? '';
+ }
+
+
+ /**
+ * 获取HDR格式
+ */
+ public function getHDRFormat(): string
+ {
+ // 从第一个视频流获取HDR信息
+ $firstVideo = $this->bdInfoArr['video'][0] ?? null;
+ $description = $firstVideo['description'] ?? '';
+ $hdrFormats = [];
+
+ // 从VIDEO描述中提取HDR格式
+ if (preg_match('/\b(HDR10|HLG|Dolby Vision)\b/i', $description, $matches)) {
+ $hdrFormats[] = $matches[1];
+ }
+
+ // 检查比特深度
+ if (preg_match('/(\d+)\s+bits/', $description, $matches)) {
+ $hdrFormats[] = $matches[1] . ' bits';
+ }
+
+ // 检查亮度
+ if (preg_match('/(\d+)nits/', $description, $matches)) {
+ $hdrFormats[] = $matches[1] . 'nits';
+ }
+
+ return implode(' / ', $hdrFormats);
+ }
+
+ /**
+ * 获取音频信息
+ */
+ public function getAudios(): array
+ {
+ $result = [];
+ $audioIndex = 1;
+ foreach ($this->bdInfoArr['audio'] as $audio) {
+ $audioInfo = [];
+
+ // 语言
+ if (!empty($audio['language'])) {
+ $audioInfo[] = $audio['language'];
+ }
+
+ // 编解码器
+ if (!empty($audio['codec'])) {
+ $audioInfo[] = $audio['codec'];
+ }
+
+ // 声道信息
+ if (!empty($audio['channels'])) {
+ $audioInfo[] = $audio['channels'];
+ } elseif (!empty($audio['description'])) {
+ // 从描述中提取声道信息
+ if (preg_match('/(\d+\.\d+)/', $audio['description'], $matches)) {
+ $audioInfo[] = $matches[1];
+ }
+ }
+
+ // 码率
+ if (!empty($audio['bitrate'])) {
+ $audioInfo[] = $audio['bitrate'];
+ }
+
+ // 括号内容(添加到最后面)
+ if (!empty($audio['chinese_brackets'])) {
+ foreach ($audio['chinese_brackets'] as $chineseBracket) {
+ $audioInfo[] = $chineseBracket;
+ }
+ }
+
+ if (!empty($audioInfo)) {
+ $result[nexus_trans('torrent.technicalinfo_audio') . $audioIndex] = implode(' / ', $audioInfo);
+ $audioIndex++;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * 获取字幕信息
+ */
+ public function getSubtitles(): array
+ {
+ $result = [];
+ $subtitleIndex = 1;
+ foreach ($this->bdInfoArr['subtitles'] as $subtitle) {
+ if (!empty($subtitle['language'])) {
+ $subtitleInfo = [$subtitle['language']];
+
+ // 括号内容(添加到最后面)
+ if (!empty($subtitle['chinese_brackets'])) {
+ foreach ($subtitle['chinese_brackets'] as $chineseBracket) {
+ $subtitleInfo[] = $chineseBracket;
+ }
+ }
+
+ $result[nexus_trans('torrent.technicalinfo_subtitles') . $subtitleIndex] = implode(' / ', $subtitleInfo);
+ $subtitleIndex++;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * 生成extra格式的输出
+ */
+ public function generateExtraFormat(): string
+ {
+ $output = [];
+
+ // 时长
+ $duration = $this->getDuration();
+ if (!empty($duration)) {
+ $output[] = 'Runtime: ' . $duration;
+ }
+
+ // 分辨率
+ $resolution = $this->getResolution();
+ if (!empty($resolution)) {
+ $output[] = 'Resolution: ' . $resolution;
+ }
+
+ // 码率
+ $bitrate = $this->getTotalBitrate();
+ if (!empty($bitrate)) {
+ $output[] = 'Bitrate: ' . $bitrate;
+ }
+
+ // HDR格式
+ $hdrFormat = $this->getHDRFormat();
+ if (!empty($hdrFormat)) {
+ $output[] = 'HDR: ' . $hdrFormat;
+ }
+
+ // 位深度
+ $bitDepth = $this->getBitDepth();
+ if (!empty($bitDepth)) {
+ $output[] = 'Bit depth: ' . $bitDepth;
+ }
+
+ // 帧率
+ $frameRate = $this->getFrameRate();
+ if (!empty($frameRate)) {
+ $output[] = 'Frame rate: ' . $frameRate;
+ }
+
+ // 配置文件
+ $profile = $this->getProfile();
+ if (!empty($profile)) {
+ $output[] = 'Profile: ' . $profile;
+ }
+
+ // 视频格式
+ $videoFormat = $this->getVideoFormat();
+ if (!empty($videoFormat)) {
+ $output[] = 'Format: ' . $videoFormat;
+ }
+
+ // Extras信息
+ $extras = $this->getExtras();
+ if (!empty($extras)) {
+ $output[] = 'Extras: ' . $extras;
+ }
+
+ // 音频
+ $audios = $this->getAudios();
+ if (!empty($audios)) {
+ $output[] = 'Audio:';
+ foreach ($audios as $key => $audio) {
+ $output[] = $key . ': ' . $audio;
+ }
+ }
+
+ // 字幕
+ $subtitles = $this->getSubtitles();
+ if (!empty($subtitles)) {
+ $output[] = '';
+ $output[] = 'Subtitles:';
+ foreach ($subtitles as $key => $subtitle) {
+ $output[] = $key . ': ' . $subtitle;
+ }
+ }
+
+ return implode("\n", $output);
+ }
+
+ /**
+ * 获取所有DISC的数据
+ */
+ private function getAllDiscs(): array
+ {
+ $lines = preg_split('/[\r\n]+/', $this->bdInfo);
+ $discs = [];
+ $currentDisc = null;
+ $currentSection = '';
+ $audioIndex = 0;
+ $subtitleIndex = 0;
+
+ foreach ($lines as $line) {
+ $line = $this->trim($line);
+ if (empty($line)) {
+ continue;
+ }
+
+ // 检测新的DISC
+ if (strpos($line, 'DISC INFO:') !== false) {
+ // 保存之前的DISC(如果存在)
+ if ($currentDisc !== null) {
+ $discs[] = $currentDisc;
+ }
+
+ // 创建新的DISC
+ $currentDisc = [
+ 'disc_info' => [],
+ 'playlist_report' => [],
+ 'video' => [],
+ 'audio' => [],
+ 'subtitles' => []
+ ];
+ $currentSection = 'disc_info';
+ $audioIndex = 0;
+ $subtitleIndex = 0;
+ continue;
+ } elseif (strpos($line, 'PLAYLIST REPORT:') !== false) {
+ $currentSection = 'playlist_report';
+ continue;
+ } elseif (strpos($line, 'VIDEO:') !== false) {
+ $currentSection = 'video';
+ continue;
+ } elseif (strpos($line, 'AUDIO:') !== false) {
+ $currentSection = 'audio';
+ continue;
+ } elseif (strpos($line, 'SUBTITLES:') !== false) {
+ $currentSection = 'subtitles';
+ continue;
+ } elseif (strpos($line, 'CHAPTERS:') !== false || strpos($line, 'STREAM DIAGNOSTICS:') !== false) {
+ $currentSection = '';
+ continue;
+ }
+
+ // 解析各个章节的内容
+ if ($currentDisc !== null && !empty($currentSection)) {
+ switch ($currentSection) {
+ case 'disc_info':
+ $this->parseDiscInfo($line, $currentDisc['disc_info']);
+ break;
+ case 'playlist_report':
+ $this->parsePlaylistReport($line, $currentDisc['playlist_report']);
+ break;
+ case 'video':
+ $this->parseVideo($line, $currentDisc['video']);
+ break;
+ case 'audio':
+ $this->parseAudio($line, $currentDisc['audio'], $audioIndex);
+ break;
+ case 'subtitles':
+ $this->parseSubtitles($line, $currentDisc['subtitles'], $subtitleIndex);
+ break;
+ }
+ }
+ }
+
+ // 保存最后一个DISC
+ if ($currentDisc !== null) {
+ $discs[] = $currentDisc;
+ }
+
+ // 如果没有找到任何DISC(normalFormat格式),检查是否是summaryFormat格式
+ if (empty($discs)) {
+ // 检查bdInfoArr中是否有有效的媒体数据
+ if ((isset($this->bdInfoArr['video']) && !empty($this->bdInfoArr['video'])) ||
+ (isset($this->bdInfoArr['audio']) && !empty($this->bdInfoArr['audio']))) {
+ // 将bdInfoArr作为单个DISC返回
+ $discs[] = $this->bdInfoArr;
+ }
+ }
+
+ return $discs;
+ }
+
+ /**
+ * 获取汇总信息
+ */
+ public function getSummaryInfo(): array
+ {
+ $videos = [
+ nexus_trans('torrent.technicalinfo_duration') => $this->getDuration(),
+ nexus_trans('torrent.technicalinfo_resolution') => $this->getResolution(),
+ nexus_trans('torrent.technicalinfo_bit_rate') => $this->getTotalBitrate(),
+ 'HDR' => $this->getHDRFormat(),
+ nexus_trans('torrent.technicalinfo_bit_depth') => $this->getBitDepth(),
+ nexus_trans('torrent.technicalinfo_frame_rate') => $this->getFrameRate(),
+ nexus_trans('torrent.technicalinfo_profile') => $this->getProfile(),
+ nexus_trans('torrent.technicalinfo_format') => $this->getVideoFormat(),
+ nexus_trans('torrent.technicalinfo_extras') => $this->getExtras(),
+ ];
+ $videos = array_filter($videos) ?: null;
+ $audios = $this->getAudios() ?: null;
+ $subtitles = $this->getSubtitles() ?: null;
+ return compact('videos', 'audios', 'subtitles');
+ }
+
+ /**
+ * 在详情页面渲染
+ */
+ public function renderOnDetailsPage(): string
+ {
+ global $lang_functions;
+
+ // 获取所有DISC
+ $allDiscs = $this->getAllDiscs();
+
+ // 检查是否有有效的媒体数据(至少包含VIDEO或AUDIO)
+ $hasValidData = false;
+ if (!empty($allDiscs)) {
+ foreach ($allDiscs as $disc) {
+ if ((isset($disc['video']) && !empty($disc['video'])) ||
+ (isset($disc['audio']) && !empty($disc['audio']))) {
+ $hasValidData = true;
+ break;
+ }
+ }
+ }
+
+ // 如果没有有效数据,隐藏显示原始BDINFO
+ if (!$hasValidData) {
+ $rawBdInfo = sprintf('[spoiler=%s][raw]
%s
[/raw][/spoiler]', nexus_trans('torrent.show_hide_bd_info'), $this->bdInfo);
+ return sprintf('%s
', format_comment($rawBdInfo, false));
+ }
+
+ $result = '';
+
+ // 为每个DISC生成表格
+ foreach ($allDiscs as $discIndex => $disc) {
+ // 临时设置当前DISC数据
+ $originalBdInfoArr = $this->bdInfoArr;
+ $this->bdInfoArr = $disc;
+
+ $summaryInfo = $this->getSummaryInfo();
+ $videos = $summaryInfo['videos'] ?: [];
+ $audios = $summaryInfo['audios'] ?: [];
+ $subtitles = $summaryInfo['subtitles'] ?: [];
+
+ if (empty($videos) && empty($audios) && empty($subtitles)) {
+ continue;
+ }
+
+ // 添加DISC标题(如果有多个DISC)
+ if (count($allDiscs) > 1) {
+ $discTitle = $disc['disc_info']['title'] ?? "";
+ $result .= 'Disc #' . ($discIndex + 1) . ' : ' . htmlspecialchars($discTitle) . '
';
+ }
+
+ $result .= '';
+ $cols = 0;
+ if (!empty($videos)) {
+ $cols++;
+ $result .= $this->buildTdTable($videos);
+ }
+ if (!empty($audios)) {
+ $cols++;
+ $result .= $this->buildTdTable($audios);
+ }
+ if (!empty($subtitles)) {
+ $cols++;
+ $result .= $this->buildTdTable($subtitles);
+ }
+ $result .= '
';
+
+ // 恢复原始数据
+ $this->bdInfoArr = $originalBdInfoArr;
+
+ $result .= '
';
+
+ // 在DISC之间添加分隔线(除了最后一个)
+ if ($discIndex < count($allDiscs) - 1) {
+ $result .= '
';
+ }
+ }
+
+ // 添加原始BDINFO
+ $rawBdInfo = sprintf('[spoiler=%s][raw]%s
[/raw][/spoiler]', nexus_trans('torrent.show_hide_bd_info'), $this->bdInfo);
+ if (function_exists('format_comment')) {
+ $result .= sprintf('%s
', format_comment($rawBdInfo, false));
+ } else {
+ $result .= sprintf('%s
', $rawBdInfo);
+ }
+
+ return $result;
+ }
+
+ /**
+ * 构建表格单元格
+ */
+ private function buildTdTable(array $parts)
+ {
+ $table = '';
+
+ // 检查是否为音频或字幕数据
+ $isAudioOrSubtitle = false;
+ $audioOrSubtitleCount = 0;
+ $audioPrefix = nexus_trans('torrent.technicalinfo_audio');
+ $subtitlePrefix = nexus_trans('torrent.technicalinfo_subtitles');
+ foreach ($parts as $key => $value) {
+ if (strpos($key, $audioPrefix) === 0 || strpos($key, $subtitlePrefix) === 0) {
+ $isAudioOrSubtitle = true;
+ $audioOrSubtitleCount++;
+ }
+ }
+
+ $displayCount = 0;
+ $hiddenParts = [];
+
+ foreach ($parts as $key => $value) {
+ $displayCount++;
+
+ // 如果是音频或字幕,且超过3条,则隐藏多余的
+ if ($isAudioOrSubtitle && $audioOrSubtitleCount > 3) {
+ if ($displayCount <= 3) {
+ // 显示前3条
+ $table .= '';
+ $table .= sprintf('| %s: %s | ', $key, $value);
+ $table .= '
';
+ } else {
+ // 收集隐藏的部分
+ $hiddenParts[$key] = $value;
+ }
+ } else {
+ // 非音频/字幕数据,或数量不超过3条,正常显示
+ $table .= '';
+ $table .= sprintf('| %s: %s | ', $key, $value);
+ $table .= '
';
+ }
+ }
+
+ // 如果有隐藏的部分,添加spoiler
+ if (!empty($hiddenParts)) {
+ $hiddenContent = '';
+ foreach ($hiddenParts as $key => $value) {
+ $hiddenContent .= sprintf('%s: %s
', $key, $value);
+ }
+ $hiddenContent = rtrim($hiddenContent, '
');
+
+ $spoilerTitle = $isAudioOrSubtitle && strpos(array_keys($parts)[0], $audioPrefix) === 0
+ ? nexus_trans('torrent.collapse_show_more_audio')
+ : nexus_trans('torrent.collapse_show_more_subtitles');
+
+ $spoiler = sprintf('[spoiler=%s]%s[/spoiler]', $spoilerTitle, $hiddenContent);
+ $table .= '';
+ // 检查format_comment函数是否存在
+ if (function_exists('format_comment')) {
+ $table .= sprintf('| %s | ', format_comment($spoiler, false));
+ } else {
+ $table .= sprintf('%s | ', $spoiler);
+ }
+ $table .= '
';
+ }
+
+ $table .= '';
+ $table .= '
';
+ return sprintf('%s | ', $table);
+ }
+
+ /**
+ * 清理字符串
+ */
+ private function trim(string $value): string
+ {
+ return trim($value, " \n\r\t\v\0\u{A0}");
+ }
+}
diff --git a/nexus/Torrent/TechnicalInformation.php b/nexus/Torrent/TechnicalInformation.php
index 12cfe360..6555e796 100644
--- a/nexus/Torrent/TechnicalInformation.php
+++ b/nexus/Torrent/TechnicalInformation.php
@@ -95,6 +95,7 @@ class TechnicalInformation
public function getAudios()
{
$result = [];
+ $audioIndex = 1;
foreach ($this->mediaInfoArr as $parentKey => $values) {
if (strpos($parentKey, 'Audio') === false) {
continue;
@@ -116,7 +117,9 @@ class TechnicalInformation
$audioInfoArr[]= "@" . $values['Bit rate'];
}
if (!empty($audioInfoArr)) {
- $result[$parentKey] = implode(" ", $audioInfoArr);
+ // 使用多语言支持的键名
+ $result[nexus_trans('torrent.technicalinfo_audio') . $audioIndex] = implode(" ", $audioInfoArr);
+ $audioIndex++;
}
}
return $result;
@@ -125,6 +128,7 @@ class TechnicalInformation
public function getSubtitles()
{
$result = [];
+ $subtitleIndex = 1;
foreach ($this->mediaInfoArr as $parentKey => $values) {
if (strpos($parentKey, 'Text') === false) {
continue;
@@ -140,7 +144,9 @@ class TechnicalInformation
$subtitlesInfoArr[] = $values['Format'];
}
if (!empty($subtitlesInfoArr)) {
- $result[$parentKey] = implode(" ", $subtitlesInfoArr);
+ // 使用多语言支持的键名
+ $result[nexus_trans('torrent.technicalinfo_subtitles') . $subtitleIndex] = implode(" ", $subtitlesInfoArr);
+ $subtitleIndex++;
}
}
return $result;
@@ -183,8 +189,11 @@ class TechnicalInformation
$subtitles = $summaryInfo['subtitles'] ?: [];
// dd($summaryInfo, $videos, $audios, $subtitles);
- if (empty($videos) && empty($audios) && empty($subtitles)) {
- return sprintf('', $this->mediaInfo);
+ if (empty($this->mediaInfo)) { //为空不渲染mediainfo
+ return '';
+ } else if (empty($videos) && empty($audios) && empty($subtitles)) { // 信息不全显示点击展开
+ $rawmediaInfo = sprintf('[spoiler=%s][raw]%s
[/raw][/spoiler]', nexus_trans('torrent.show_hide_media_info'), $this->mediaInfo);
+ return sprintf('', format_comment($rawmediaInfo, false));
}
$result = '';
@@ -211,15 +220,15 @@ class TechnicalInformation
public function getSummaryInfo(): array
{
$videos = [
- 'Runtime' => $this->getRuntime(),
- 'Resolution' => $this->getResolution(),
- 'Bitrate' => $this->getBitrate(),
+ nexus_trans('torrent.technicalinfo_duration') => $this->getRuntime(),
+ nexus_trans('torrent.technicalinfo_resolution') => $this->getResolution(),
+ nexus_trans('torrent.technicalinfo_bit_rate') => $this->getBitrate(),
'HDR' => $this->getHDRFormat(),
- 'Bit depth' => $this->getBitDepth(),
- 'Frame rate' => $this->getFramerate(),
- 'Profile' => $this->getProfile(),
- 'Format' => $this->getVideoFormat(),
- 'Ref.Frames' => $this->getRefFrame(),
+ nexus_trans('torrent.technicalinfo_bit_depth') => $this->getBitDepth(),
+ nexus_trans('torrent.technicalinfo_frame_rate') => $this->getFramerate(),
+ nexus_trans('torrent.technicalinfo_profile') => $this->getProfile(),
+ nexus_trans('torrent.technicalinfo_format') => $this->getVideoFormat(),
+ nexus_trans('torrent.technicalinfo_ref_frames') => $this->getRefFrame(),
];
$videos = array_filter($videos) ?: null;
$audios = $this->getAudios() ?: null;
@@ -230,14 +239,70 @@ class TechnicalInformation
private function buildTdTable(array $parts)
{
$table = '';
+
+ // 检查是否为音频或字幕数据
+ $isAudioOrSubtitle = false;
+ $audioOrSubtitleCount = 0;
+ $audioPrefix = nexus_trans('torrent.technicalinfo_audio');
+ $subtitlePrefix = nexus_trans('torrent.technicalinfo_subtitles');
foreach ($parts as $key => $value) {
+ if (strpos($key, $audioPrefix) === 0 || strpos($key, $subtitlePrefix) === 0) {
+ $isAudioOrSubtitle = true;
+ $audioOrSubtitleCount++;
+ }
+ }
+
+ $displayCount = 0;
+ $hiddenParts = [];
+
+ foreach ($parts as $key => $value) {
+ $displayCount++;
+
+ // 如果是音频或字幕,且超过3条,则隐藏多余的
+ if ($isAudioOrSubtitle && $audioOrSubtitleCount > 3) {
+ if ($displayCount <= 3) {
+ // 显示前3条
+ $table .= '';
+ $table .= sprintf('| %s: %s | ', $key, $value);
+ $table .= '
';
+ } else {
+ // 收集隐藏的部分
+ $hiddenParts[$key] = $value;
+ }
+ } else {
+ // 非音频/字幕数据,或数量不超过3条,正常显示
+ $table .= '';
+ $table .= sprintf('| %s: %s | ', $key, $value);
+ $table .= '
';
+ }
+ }
+
+ // 如果有隐藏的部分,添加spoiler
+ if (!empty($hiddenParts)) {
+ $hiddenContent = '';
+ foreach ($hiddenParts as $key => $value) {
+ $hiddenContent .= sprintf('%s: %s
', $key, $value);
+ }
+ $hiddenContent = rtrim($hiddenContent, '
');
+
+ $spoilerTitle = $isAudioOrSubtitle && strpos(array_keys($parts)[0], $audioPrefix) === 0
+ ? nexus_trans('torrent.collapse_show_more_audio')
+ : nexus_trans('torrent.collapse_show_more_subtitles');
+
+ $spoiler = sprintf('[spoiler=%s]%s[/spoiler]', $spoilerTitle, $hiddenContent);
$table .= '';
- $table .= sprintf('| %s: %s | ', $key, $value);
+ // 检查format_comment函数是否存在
+ if (function_exists('format_comment')) {
+ $table .= sprintf('%s | ', format_comment($spoiler, false));
+ } else {
+ $table .= sprintf('%s | ', $spoiler);
+ }
$table .= '
';
}
+
$table .= '';
$table .= '
';
return sprintf('%s | ', $table);
}
-}
+}
\ No newline at end of file
diff --git a/public/details.php b/public/details.php
index 9bcd49da..0a49e836 100644
--- a/public/details.php
+++ b/public/details.php
@@ -308,7 +308,28 @@ JS;
//technical info
if ($settingMain['enable_technical_info'] == 'yes') {
- $technicalInfo = new \Nexus\Torrent\TechnicalInformation($row['technical_info'] ?? '');
+ $technicalData = $row['technical_info'] ?? '';
+
+ // 判断是否为BDINFO格式
+ $isBdInfo = false;
+ if (!empty($technicalData)) {
+ $firstLine = strtok($technicalData, "\n");
+ if (strpos($firstLine, 'DISC INFO') !== false
+ || strpos($firstLine, 'Disc Title') !== false
+ || strpos($firstLine, 'Disc Label') !== false
+ ) {
+ $isBdInfo = true;
+ }
+ }
+
+ if ($isBdInfo) {
+ // 使用BdInfoExtra处理BDINFO格式
+ $technicalInfo = new \Nexus\Torrent\BdInfoExtra($technicalData);
+ } else {
+ // 使用TechnicalInformation处理MediaInfo格式
+ $technicalInfo = new \Nexus\Torrent\TechnicalInformation($technicalData);
+ }
+
$technicalInfoResult = $technicalInfo->renderOnDetailsPage();
if (!empty($technicalInfoResult)) {
tr($lang_functions['text_technical_info'], $technicalInfoResult, 1);
diff --git a/resources/lang/en/torrent.php b/resources/lang/en/torrent.php
index d3a3ff3c..4d852a52 100644
--- a/resources/lang/en/torrent.php
+++ b/resources/lang/en/torrent.php
@@ -86,6 +86,20 @@ return [
'logs_label' => 'Approval logs'
],
'show_hide_media_info' => 'Show/Hide raw MediaInfo',
+ 'show_hide_bd_info' => 'Show/Hide raw BDInfo',
+ 'collapse_show_more_audio' => 'Collapse/Expand more audio tracks',
+ 'collapse_show_more_subtitles' => 'Collapse/Expand more subtitles',
+ 'technicalinfo_duration' => 'Duration',
+ 'technicalinfo_resolution' => 'Resolution',
+ 'technicalinfo_bit_rate' => 'Bit Rate',
+ 'technicalinfo_bit_depth' => 'Bit Depth',
+ 'technicalinfo_frame_rate' => 'Frame Rate',
+ 'technicalinfo_profile' => 'Profile',
+ 'technicalinfo_format' => 'Format',
+ 'technicalinfo_extras' => 'Extras',
+ 'technicalinfo_ref_frames' => 'Ref.Frames',
+ 'technicalinfo_audio' => 'Audio #',
+ 'technicalinfo_subtitles' => 'Subtitles #',
'promotion_time_types' => [
\App\Models\Torrent::PROMOTION_TIME_TYPE_GLOBAL => 'Global',
\App\Models\Torrent::PROMOTION_TIME_TYPE_PERMANENT => 'Permanent',
diff --git a/resources/lang/zh_CN/torrent.php b/resources/lang/zh_CN/torrent.php
index 4c438736..92714f62 100644
--- a/resources/lang/zh_CN/torrent.php
+++ b/resources/lang/zh_CN/torrent.php
@@ -86,6 +86,20 @@ return [
'logs_label' => '审核记录',
],
'show_hide_media_info' => '显示/隐藏原始 MediaInfo',
+ 'show_hide_bd_info' => '显示/隐藏原始 BDInfo',
+ 'collapse_show_more_audio' => '收起/展开更多音轨',
+ 'collapse_show_more_subtitles' => '收起/展开更多字幕',
+ 'technicalinfo_duration' => '时长',
+ 'technicalinfo_resolution' => '分辨率',
+ 'technicalinfo_bit_rate' => '比特率',
+ 'technicalinfo_bit_depth' => '位深',
+ 'technicalinfo_frame_rate' => '帧率',
+ 'technicalinfo_profile' => '档次',
+ 'technicalinfo_format' => '格式',
+ 'technicalinfo_extras' => '附加内容',
+ 'technicalinfo_ref_frames' => '关键帧',
+ 'technicalinfo_audio' => '音轨 #',
+ 'technicalinfo_subtitles' => '字幕 #',
'promotion_time_types' => [
\App\Models\Torrent::PROMOTION_TIME_TYPE_GLOBAL => '全局',
\App\Models\Torrent::PROMOTION_TIME_TYPE_PERMANENT => '永久',
diff --git a/resources/lang/zh_TW/torrent.php b/resources/lang/zh_TW/torrent.php
index 8591d31b..e4131700 100644
--- a/resources/lang/zh_TW/torrent.php
+++ b/resources/lang/zh_TW/torrent.php
@@ -86,6 +86,20 @@ return [
'logs_label' => '審核記錄'
],
'show_hide_media_info' => '顯示/隱藏原始 MediaInfo',
+ 'show_hide_bd_info' => '顯示/隱藏原始 BDInfo',
+ 'collapse_show_more_audio' => '收起/展開更多音軌',
+ 'collapse_show_more_subtitles' => '收起/展開更多字幕',
+ 'technicalinfo_duration' => '時長',
+ 'technicalinfo_resolution' => '分辨率',
+ 'technicalinfo_bit_rate' => '比特率',
+ 'technicalinfo_bit_depth' => '位深',
+ 'technicalinfo_frame_rate' => '幀率',
+ 'technicalinfo_profile' => '檔次',
+ 'technicalinfo_format' => '格式',
+ 'technicalinfo_extras' => '附加內容',
+ 'technicalinfo_ref_frames' => '關鍵幀',
+ 'technicalinfo_audio' => '音軌 #',
+ 'technicalinfo_subtitles' => '字幕 #',
'promotion_time_types' => [
\App\Models\Torrent::PROMOTION_TIME_TYPE_GLOBAL => '全局',
\App\Models\Torrent::PROMOTION_TIME_TYPE_PERMANENT => '永久',