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('', $key, $value); + $table .= ''; + } else { + // 收集隐藏的部分 + $hiddenParts[$key] = $value; + } + } else { + // 非音频/字幕数据,或数量不超过3条,正常显示 + $table .= ''; + $table .= sprintf('', $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('', format_comment($spoiler, false)); + } else { + $table .= sprintf('', $spoiler); + } + $table .= ''; + } + + $table .= ''; + $table .= '
%s: %s
%s: %s
%s%s
'; + 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('
%s
', $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('
%s
', 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('', $key, $value); + $table .= ''; + } else { + // 收集隐藏的部分 + $hiddenParts[$key] = $value; + } + } else { + // 非音频/字幕数据,或数量不超过3条,正常显示 + $table .= ''; + $table .= sprintf('', $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('', $key, $value); + // 检查format_comment函数是否存在 + if (function_exists('format_comment')) { + $table .= sprintf('', format_comment($spoiler, false)); + } else { + $table .= sprintf('', $spoiler); + } $table .= ''; } + $table .= ''; $table .= '
%s: %s
%s: %s
%s: %s%s%s
'; 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 => '永久',