diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 269aebe1..1d0b6377 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -71,7 +71,7 @@ }, "Transcode": { "Title": "Replay Transcode", - "Description": "Choose one or multiple .tar recording archive files for MP4 conversion.", + "Description": "Choose one or multiple RDP/VNC recording archive (.tar) files for MP4 conversion.", "SelectedArchives": "{count} archive(s) selected", "TotalProgress": "Overall {progress}%", "CompletedCount": "Completed {completed}/{total}", @@ -82,7 +82,7 @@ "ArchivesHint": "Single and multiple selection are both supported, and you can append more archives later.", "SelectArchives": "Add archives", "ClearArchives": "Clear list", - "EmptyArchives": "Add replay archives to transcode", + "EmptyArchives": "Add RDP/VNC recording archives to transcode", "OutputTitle": "Output directory", "OutputHint": "Generated MP4 files will be written to this directory.", "OutputDirectory": "Output directory", @@ -130,7 +130,7 @@ "Close": "Close", "Cancel": "Cancel", "Confirm": "Confirm", - "FilenameStyle": "Filename style", + "FilenameStyle": "Output filename style", "FilenameOriginal": "Original (UUID)", "FilenameFriendly": "Friendly name (user-asset-account)", "FilenameFriendlyUuid": "Friendly name + UUID", @@ -147,7 +147,10 @@ "PowerFull": "Full speed (all CPUs)", "PowerFast": "Fast (3/4 CPU)", "PowerMedium": "Medium (1/2 CPU)", - "PowerLow": "Low (1/4 CPU)" + "PowerLow": "Low (1/4 CPU)", + "ERR_ALREADY_TRANSCODED": "This recording has already been transcoded. Please extract and play the MP4 file directly.", + "ERR_REPLAY_JSON_NOT_FOUND": "replay.json not found. Please ensure the tar file is an RDP/VNC recording.", + "ERR_RECORDING_DATA_NOT_FOUND": "Recording data not found. Please ensure the tar file is an RDP/VNC recording." }, "VideoPlayer": { "ImportingFiles": "Importing {count} file(s)…", diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json index 55d42499..2a044905 100644 --- a/i18n/locales/zh.json +++ b/i18n/locales/zh.json @@ -71,7 +71,7 @@ }, "Transcode": { "Title": "录像转码", - "Description": "选择一个或多个录像压缩文件(.tar),将录像转换为 mp4 格式。", + "Description": "选择一个或多个录像压缩文件(.tar),将 RDP/VNC 原生录像转换为 mp4 格式。", "SelectedArchives": "已选择 {count} 个录像包", "TotalProgress": "总进度 {progress}%", "CompletedCount": "已完成 {completed}/{total}", @@ -82,7 +82,7 @@ "ArchivesHint": "支持单选或多选,可继续追加录像文件。", "SelectArchives": "添加文件", "ClearArchives": "清空列表", - "EmptyArchives": "请添加要转码的录像压缩文件", + "EmptyArchives": "请添加要转码的 RDP/VNC 录像压缩文件", "OutputTitle": "输出目录", "OutputHint": "转码后的 MP4 文件会输出到该目录下。", "OutputDirectory": "输出目录", @@ -130,7 +130,7 @@ "Close": "关闭", "Cancel": "取消", "Confirm": "确认", - "FilenameStyle": "文件名风格", + "FilenameStyle": "输出文件名风格", "FilenameOriginal": "原文件名(UUID)", "FilenameFriendly": "友好名称(用户-资产-账号)", "FilenameFriendlyUuid": "友好名称 + UUID", @@ -147,7 +147,10 @@ "PowerFull": "全速(所有CPU)", "PowerFast": "极速(3/4 CPU)", "PowerMedium": "中等(1/2 CPU)", - "PowerLow": "低速(1/4 CPU)" + "PowerLow": "低速(1/4 CPU)", + "ERR_ALREADY_TRANSCODED": "该录像已转码,请直接解压 tar 包后在播放器中播放", + "ERR_REPLAY_JSON_NOT_FOUND": "未找到 replay.json,请确认 tar 包为 RDP/VNC 录像文件", + "ERR_RECORDING_DATA_NOT_FOUND": "未找到录像数据文件,请确认 tar 包为 RDP/VNC 录像文件" }, "VideoPlayer": { "ImportingFiles": "正在导入 {count} 个文件…", diff --git a/src-tauri/src/transcode/mod.rs b/src-tauri/src/transcode/mod.rs index a0db7444..83b256d8 100644 --- a/src-tauri/src/transcode/mod.rs +++ b/src-tauri/src/transcode/mod.rs @@ -540,6 +540,7 @@ fn extract_tar_payloads( let mut replay_json: Option> = None; let mut parts: Vec<(usize, Vec)> = Vec::new(); + let mut mp4_files: Vec = Vec::new(); for entry_result in archive.entries().map_err(|e| { ( @@ -586,19 +587,33 @@ fn extract_tar_payloads( ) })?; parts.push((index, buf)); + } else if filename.ends_with(".mp4") { + mp4_files.push(filename); } } let replay_json = replay_json.ok_or_else(|| { + if !mp4_files.is_empty() { + return ( + fallback_session_id.to_string(), + "ERR_ALREADY_TRANSCODED".to_string(), + ); + } ( fallback_session_id.to_string(), - "replay.json not found in tar archive".to_string(), + "ERR_REPLAY_JSON_NOT_FOUND".to_string(), ) })?; if parts.is_empty() { + if !mp4_files.is_empty() { + return Err(( + fallback_session_id.to_string(), + "ERR_ALREADY_TRANSCODED".to_string(), + )); + } return Err(( fallback_session_id.to_string(), - ".part.gz file not found in tar archive".to_string(), + "ERR_RECORDING_DATA_NOT_FOUND".to_string(), )); } parts.sort_by_key(|(index, _)| *index); diff --git a/ui/store/modules/transcode.ts b/ui/store/modules/transcode.ts index 04737394..616a2c1b 100644 --- a/ui/store/modules/transcode.ts +++ b/ui/store/modules/transcode.ts @@ -57,6 +57,12 @@ export interface TranscodeTaskItem { duration?: number | null } +const transcodeErrorKeyMap: Record = { + ERR_ALREADY_TRANSCODED: "Transcode.ERR_ALREADY_TRANSCODED", + ERR_REPLAY_JSON_NOT_FOUND: "Transcode.ERR_REPLAY_JSON_NOT_FOUND", + ERR_RECORDING_DATA_NOT_FOUND: "Transcode.ERR_RECORDING_DATA_NOT_FOUND", +}; + let listenerRegistered = false; export const useTranscodeStore = defineStore( @@ -64,6 +70,10 @@ export const useTranscodeStore = defineStore( () => { const { t } = useI18n(); const toast = useToast(); + const resolveError = (raw: string): string => { + const key = transcodeErrorKeyMap[raw]; + return key ? t(key) : raw; + }; const archivePaths = ref([]); const outputDir = ref(""); @@ -299,7 +309,7 @@ export const useTranscodeStore = defineStore( const incomingDuration = payload.duration ?? null; if (successFlag === true || successFlag === false) { - const message = successFlag ? t("Transcode.Completed") : payload.message || t("Transcode.StatusFailed"); + const message = successFlag ? t("Transcode.Completed") : resolveError(payload.message) || t("Transcode.StatusFailed"); markTaskCompleted(targetIndex, successFlag, outputValue, message, incomingMetadata, incomingDuration); return; } @@ -353,7 +363,7 @@ export const useTranscodeStore = defineStore( targetIndex, result.success, result.output, - result.success ? t("Transcode.Completed") : result.error || t("Transcode.StatusFailed"), + result.success ? t("Transcode.Completed") : resolveError(result.error || "") || t("Transcode.StatusFailed"), result.metadata ?? null ); });