diff --git a/压缩包/au3脚本/视频转码-合并版.au3 b/压缩包/au3脚本/视频转码-合并版.au3 new file mode 100644 index 0000000..6330c28 --- /dev/null +++ b/压缩包/au3脚本/视频转码-合并版.au3 @@ -0,0 +1,536 @@ +#Region ;**** Directives created by AutoIt3Wrapper_GUI **** +#AutoIt3Wrapper_Icon=icon.ico +#AutoIt3Wrapper_Outfile=HLS_Converter_and_Renamer.exe +#AutoIt3Wrapper_Compression=4 +#AutoIt3Wrapper_UseUpx=y +#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** + +#include +#include +#include +#include +#include +#include +#include +#include + +; ========== 配置 ========== +Global Const $MAX_CONCURRENT = 8 + +; ========== 全局变量 - 转码模块 ========== +Global $g_sSourceDir = "" +Global $g_hLogEdit_Convert, $g_hBtnStart_Convert, $g_hBtnSelect_Convert, $g_hLabelStatus_Convert +Global $g_aPendingFiles[0] +Global $g_aRunningTasks[0][4] +Global $g_iSuccess = 0 +Global $g_bConverting = False +Global $g_iTotalFiles = 0 +Global $g_iCurrentIndex = 0 + +; ========== 全局变量 - 重命名模块 ========== +Global $g_sSelectedPath_Rename = "" +Global $g_aMP4Files[0] +Global $g_aResults[1][4] +Global $g_iResultCount = 0 +Global $g_bProcessing_Rename = False +Global $g_hLogEdit_Rename, $g_hBtnSelectDir_Rename, $g_hBtnStart_Rename + +; ========== 创建主窗口 ========== +Global $hMainGUI = GUICreate("HLS转码与文件夹重命名一体化工具 v1.0", 750, 600) + +; 创建TabControl +Global $hTabControl = GUICtrlCreateTab(10, 10, 730, 580) + +; ========== 标签页1:HLS批量转码 ========== +Global $hTab_Convert = GUICtrlCreateTabItem("HLS批量转码") +GUICtrlCreateLabel("1. 请选择包含视频文件的源目录:", 20, 50, 400, 20) +$g_hBtnSelect_Convert = GUICtrlCreateButton("选择目录...", 20, 75, 100, 30) +$g_hLabelStatus_Convert = GUICtrlCreateLabel("", 130, 80, 570, 20) +GUICtrlSetColor(-1, 0x0000FF) + +GUICtrlCreateLabel("2. 点击开始转码(最多 " & $MAX_CONCURRENT & " 个并发):", 20, 115, 400, 20) +$g_hBtnStart_Convert = GUICtrlCreateButton("开始转码", 20, 140, 100, 30) +GUICtrlSetState($g_hBtnStart_Convert, $GUI_DISABLE) + +GUICtrlCreateLabel("实时日志(格式: [序号/总数] [视频名] 日志内容):", 20, 185, 300, 20) +$g_hLogEdit_Convert = GUICtrlCreateEdit("", 20, 210, 700, 350, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $WS_VSCROLL)) +GUICtrlSetFont($g_hLogEdit_Convert, 9, 400, 0, "Consolas") + +; ========== 标签页2:文件夹重命名 ========== +Global $hTab_Rename = GUICtrlCreateTabItem("文件夹重命名 & Excel生成") +GUICtrlCreateLabel("1. 请选择包含视频文件的源目录:", 20, 50, 400, 20) +$g_hBtnSelectDir_Rename = GUICtrlCreateButton("📁 选择视频目录", 20, 75, 150, 40) +GUICtrlSetFont(-1, 10, 600) + +GUICtrlCreateLabel("2. 点击开始执行处理所有视频文件:", 20, 125, 400, 20) +$g_hBtnStart_Rename = GUICtrlCreateButton("▶️ 开始执行", 20, 150, 150, 40) +GUICtrlSetFont(-1, 10, 600) +GUICtrlSetState($g_hBtnStart_Rename, $GUI_DISABLE) + +GUICtrlCreateLabel("处理日志:", 20, 205, 300, 20) +$g_hLogEdit_Rename = GUICtrlCreateEdit("", 20, 230, 700, 330, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $WS_VSCROLL, $ES_MULTILINE)) +GUICtrlSetFont($g_hLogEdit_Rename, 9, 400, 0, "Consolas") +GUICtrlSetBkColor(-1, 0xFFFFFF) +GUICtrlSetLimit(-1, 100000) + +; 结束TabControl +GUICtrlCreateTabItem("") + +GUISetState(@SW_SHOW, $hMainGUI) + +; ========== 消息循环 ========== +While 1 + Switch GUIGetMsg() + Case $GUI_EVENT_CLOSE + _KillAllRunningTasks() + Exit + + ; ========== 转码模块事件 ========== + Case $g_hBtnSelect_Convert + _SelectSourceDir_Convert() + + Case $g_hBtnStart_Convert + _StartConversion() + + ; ========== 重命名模块事件 ========== + Case $g_hBtnSelectDir_Rename + _SelectSourceDir_Rename() + + Case $g_hBtnStart_Rename + _StartRenaming() + + EndSwitch + + ; 转码模块:定期检查任务状态 + If $g_bConverting Then + _PollRunningTasks() + Sleep(50) + EndIf + + ; 重命名模块:定期检查处理状态(如果有需要的话) + If $g_bProcessing_Rename Then + Sleep(10) + EndIf + +WEnd + +; ====================================================================== +; ========== 转码模块函数 ========== +; ====================================================================== + +Func _SelectSourceDir_Convert() + Local $sDir = FileSelectFolder("请选择视频所在的源目录", "") + If Not @error And $sDir <> "" Then + $g_sSourceDir = $sDir + GUICtrlSetData($g_hLabelStatus_Convert, "已选择: " & $g_sSourceDir) + GUICtrlSetState($g_hBtnStart_Convert, $GUI_ENABLE) + _Log_Convert("✅ 目录已选择:" & $g_sSourceDir) + Else + $g_sSourceDir = "" + GUICtrlSetData($g_hLabelStatus_Convert, "未选择目录") + GUICtrlSetState($g_hBtnStart_Convert, $GUI_DISABLE) + EndIf +EndFunc + +Func _StartConversion() + If $g_sSourceDir = "" Or $g_bConverting Then Return + + $g_bConverting = True + GUICtrlSetState($g_hBtnStart_Convert, $GUI_DISABLE) + GUICtrlSetState($g_hBtnSelect_Convert, $GUI_DISABLE) + _Log_Convert(@CRLF & "🚀 开始批量转码(最多 " & $MAX_CONCURRENT & " 个并发)..." & @CRLF) + + ; 初始化 + $g_iSuccess = 0 + $g_iCurrentIndex = 0 + Dim $g_aPendingFiles[0] + Dim $g_aRunningTasks[0][4] + + ; 递归获取所有子目录中的视频文件(返回完整路径) + Local $aFiles = _FileListToArrayRec($g_sSourceDir, "*.mp4;*.avi;*.mkv;*.mov;*.wmv;*.flv;*.mpeg;*.mpg;*.webm", $FLTAR_FILES, $FLTAR_RECUR, $FLTAR_SORT, $FLTAR_FULLPATH) + + If @error Then + _Log_Convert("❌ 目录中没有找到任何支持的视频文件。") + _FinishConversion() + Return + EndIf + + $g_iTotalFiles = $aFiles[0] + + ; 填充待处理队列 + For $i = 1 To $aFiles[0] + _ArrayAdd($g_aPendingFiles, $aFiles[$i]) + Next + + ; 启动第一批任务 + _LaunchNextTasks() +EndFunc + +Func _LaunchNextTasks() + While UBound($g_aRunningTasks, 1) < $MAX_CONCURRENT And UBound($g_aPendingFiles) > 0 + Local $sFilePath = $g_aPendingFiles[0] + _ArrayDelete($g_aPendingFiles, 0) + + $g_iCurrentIndex += 1 + + ; 提取文件名(不含路径和扩展名) + Local $sFileName = StringRegExpReplace($sFilePath, "^.*\\", "") + Local $sBaseName = StringRegExpReplace($sFileName, "\.[^.\\]+$", "") + + ; 获取视频所在目录 + Local $sVideoDir = StringLeft($sFilePath, StringInStr($sFilePath, "\", 0, -1) - 1) + + ; 输出目录 = 视频所在目录 \ 视频名(无扩展名) + Local $sOutputDir = $sVideoDir & "\" & $sBaseName + + If DirCreate($sOutputDir) = 0 Then + _Log_Convert("[系统] ⚠️ 无法创建目录: " & $sOutputDir) + ContinueLoop + EndIf + + Local $sM3U8 = $sOutputDir & "\playlist.m3u8" + Local $sTS = $sOutputDir & "\segment_%05d.ts" + + Local $sCmd = 'ffmpeg -i "' & $sFilePath & '" ' & _ + '-c:v libx264 -crf 23 -preset fast -profile:v baseline -level 3.0 ' & _ + '-c:a aac -b:a 128k ' & _ + '-hls_time 2 ' & _ + '-hls_playlist_type vod ' & _ + '-hls_segment_filename "' & $sTS & '" ' & _ + '"' & $sM3U8 & '"' + + ; 启动进程并捕获合并输出 + Local $iPID = Run($sCmd, "", @SW_HIDE, $STDERR_MERGED) + If @error Then + _Log_Convert("[" & $g_iCurrentIndex & "/" & $g_iTotalFiles & "] [" & $sBaseName & "] ❌ 启动失败") + ContinueLoop + EndIf + + ; 记录任务 + Local $iIndex = UBound($g_aRunningTasks, 1) + ReDim $g_aRunningTasks[$iIndex + 1][4] + $g_aRunningTasks[$iIndex][0] = $iPID + $g_aRunningTasks[$iIndex][1] = $sBaseName + $g_aRunningTasks[$iIndex][2] = $sOutputDir + $g_aRunningTasks[$iIndex][3] = "" + + ; 打印序号/总数 + 文件名 + 完整路径 + _Log_Convert("[" & $g_iCurrentIndex & "/" & $g_iTotalFiles & "] [" & $sBaseName & "] 🔄 开始转码...") + _Log_Convert(" 路径: " & $sFilePath) + WEnd +EndFunc + +Func _PollRunningTasks() + If UBound($g_aRunningTasks, 1) = 0 Then + If UBound($g_aPendingFiles) = 0 Then + _FinishConversion() + EndIf + Return + EndIf + + Local $aToRemove[0] + + For $i = 0 To UBound($g_aRunningTasks, 1) - 1 + Local $iPID = $g_aRunningTasks[$i][0] + Local $sName = $g_aRunningTasks[$i][1] + + ; 静默读取输出流(避免 ffmpeg 因缓冲区满而挂起) + Local $sDummy = StdoutRead($iPID) + + ; 检查进程是否结束 + If Not ProcessExists($iPID) Then + Local $iExitCode = @extended + If $iExitCode = 0 Then + $g_iSuccess += 1 + _Log_Convert("[" & $sName & "] ✅ 成功完成") + Else + _Log_Convert("[" & $sName & "] ❌ 失败 (退出码: " & $iExitCode & ")") + EndIf + _ArrayAdd($aToRemove, $i) + EndIf + Next + + ; 移除已完成的任务(从后往前删) + For $i = UBound($aToRemove) - 1 To 0 Step -1 + Local $iIndex = $aToRemove[$i] + _ArrayDelete($g_aRunningTasks, $iIndex) + Next + + ; 启动新任务填补空缺 + If UBound($aToRemove) > 0 Then + _LaunchNextTasks() + EndIf +EndFunc + +Func _FinishConversion() + $g_bConverting = False + _Log_Convert(@CRLF & "🎉 所有转码任务完成!成功处理 " & $g_iSuccess & " 个视频。") + GUICtrlSetState($g_hBtnStart_Convert, $GUI_ENABLE) + GUICtrlSetState($g_hBtnSelect_Convert, $GUI_ENABLE) +EndFunc + +Func _KillAllRunningTasks() + If Not IsArray($g_aRunningTasks) Then Return + For $i = 0 To UBound($g_aRunningTasks, 1) - 1 + ProcessClose($g_aRunningTasks[$i][0]) + Next +EndFunc + +Func _Log_Convert($sText) + Local $sCurrent = GUICtrlRead($g_hLogEdit_Convert) + Local $sNew = $sCurrent & $sText & @CRLF + If StringLen($sNew) > 2000 Then + $sNew = StringRight($sNew, 2000) + EndIf + GUICtrlSetData($g_hLogEdit_Convert, $sNew) + GUICtrlSendMsg($g_hLogEdit_Convert, $EM_LINESCROLL, 0, GUICtrlSendMsg($g_hLogEdit_Convert, $EM_GETLINECOUNT, 0, 0)) +EndFunc + +; ====================================================================== +; ========== 重命名模块函数 ========== +; ====================================================================== + +Func _SelectSourceDir_Rename() + Local $sPath = FileSelectFolder("请选择包含视频的文件夹", "") + If $sPath <> "" Then + $g_sSelectedPath_Rename = $sPath + _Log_Rename("✓ 已选择目录: " & $g_sSelectedPath_Rename) + GUICtrlSetState($g_hBtnStart_Rename, $GUI_ENABLE) + EndIf +EndFunc + +Func _StartRenaming() + If $g_sSelectedPath_Rename = "" Then + MsgBox($MB_ICONWARNING, "警告", "请先选择视频目录!") + Return + EndIf + + ; 禁用按钮,防止重复点击 + GUICtrlSetState($g_hBtnSelectDir_Rename, $GUI_DISABLE) + GUICtrlSetState($g_hBtnStart_Rename, $GUI_DISABLE) + $g_bProcessing_Rename = True + + ; 清空日志和结果 + GUICtrlSetData($g_hLogEdit_Rename, "") + $g_iResultCount = 0 + ReDim $g_aResults[1][4] + + ; 扫描视频文件 + _Log_Rename("🔍 正在扫描目录中的视频文件...") + Local $aTemp = _FileListToArrayRec($g_sSelectedPath_Rename, "*.mp4;*.avi;*.mkv;*.mov;*.wmv;*.flv;*.mpeg;*.mpg;*.webm", $FLTAR_FILES, $FLTAR_RECUR, $FLTAR_SORT, $FLTAR_FULLPATH) + + If Not @error And IsArray($aTemp) And UBound($aTemp) > 0 Then + $g_aMP4Files = $aTemp + Else + Dim $g_aMP4Files[0] + EndIf + + If UBound($g_aMP4Files) = 0 Or (UBound($g_aMP4Files) = 1 And $g_aMP4Files[0] = 0) Then + _Log_Rename("❌ 未找到任何视频文件!") + GUICtrlSetState($g_hBtnSelectDir_Rename, $GUI_ENABLE) + GUICtrlSetState($g_hBtnStart_Rename, $GUI_ENABLE) + $g_bProcessing_Rename = False + Return + EndIf + + _Log_Rename("✓ 找到 " & UBound($g_aMP4Files) & " 个视频文件") + _Log_Rename("") + + ; 处理每个视频文件 + Local $iSuccessCount = 0 + Local $iFailCount = 0 + Local $aFailedFiles[0] ; 用于存储失败的文件信息 "文件路径|失败原因" + + For $i = 0 To UBound($g_aMP4Files) - 1 + If Not $g_bProcessing_Rename Then ExitLoop + + Local $sMP4Path = $g_aMP4Files[$i] + Local $sMP4Name = StringRegExpReplace($sMP4Path, "^.+[\\/]", "") + Local $sMP4BaseName = StringRegExpReplace($sMP4Name, "(?i)\.(mp4|avi|mkv|mov|wmv|flv|mpeg|mpg|webm)$", "", 1) + Local $sMP4Dir = StringRegExpReplace($sMP4Path, "[\\/][^\\/]+$", "") + + ; 检查同名文件夹是否存在 + Local $sFolderName = $sMP4BaseName + Local $sFolderPath = $sMP4Dir & "\" & $sFolderName + + If DirGetSize($sFolderPath) = -1 Then + _Log_Rename("⚠️ [" & ($i + 1) & "/" & UBound($g_aMP4Files) & "] " & $sMP4Name & " - 未找到同名文件夹") + _Log_Rename(" 路径: " & $sMP4Path) + _ArrayAdd($aFailedFiles, $sMP4Path & "|" & "未找到同名文件夹") + $iFailCount += 1 + ContinueLoop + EndIf + + ; 检查playlist.m3u8是否存在 + Local $sM3U8Path = $sFolderPath & "\playlist.m3u8" + If Not FileExists($sM3U8Path) Then + _Log_Rename("⚠️ [" & ($i + 1) & "/" & UBound($g_aMP4Files) & "] " & $sMP4Name & " - 文件夹中不存在playlist.m3u8") + _Log_Rename(" 路径: " & $sMP4Path) + _ArrayAdd($aFailedFiles, $sMP4Path & "|" & "文件夹中不存在playlist.m3u8") + $iFailCount += 1 + ContinueLoop + EndIf + + ; 获取新的UUID + _Log_Rename("[" & ($i + 1) & "/" & UBound($g_aMP4Files) & "] " & $sMP4Name & " - 正在获取新UUID...") + Local $sNewUUID = _GetNewUUID() + + If $sNewUUID = "" Then + _Log_Rename("❌ [" & ($i + 1) & "/" & UBound($g_aMP4Files) & "] " & $sMP4Name & " - 获取UUID失败") + _Log_Rename(" 路径: " & $sMP4Path) + _ArrayAdd($aFailedFiles, $sMP4Path & "|" & "获取UUID失败") + $iFailCount += 1 + ContinueLoop + EndIf + + ; 重命名文件夹 + Local $sNewFolderPath = $sMP4Dir & "\" & $sNewUUID + + If DirMove($sFolderPath, $sNewFolderPath) Then + _Log_Rename("✓ [" & ($i + 1) & "/" & UBound($g_aMP4Files) & "] " & $sMP4Name & " - 重命名成功: " & $sFolderName & " → " & $sNewUUID) + _Log_Rename(" 路径: " & $sMP4Path) + + ; 扩容结果数组(如果需要) + If $g_iResultCount >= UBound($g_aResults) Then + ReDim $g_aResults[$g_iResultCount + 10][4] + EndIf + + ; 保存结果 + $g_aResults[$g_iResultCount][0] = $sMP4Name + $g_aResults[$g_iResultCount][1] = $sMP4Path + $g_aResults[$g_iResultCount][2] = $sFolderName + $g_aResults[$g_iResultCount][3] = $sNewUUID + $g_iResultCount += 1 + + $iSuccessCount += 1 + Else + _Log_Rename("❌ [" & ($i + 1) & "/" & UBound($g_aMP4Files) & "] " & $sMP4Name & " - 重命名失败") + _Log_Rename(" 路径: " & $sMP4Path) + _ArrayAdd($aFailedFiles, $sMP4Path & "|" & "重命名失败") + $iFailCount += 1 + EndIf + + ; 等待0.2秒 + Sleep(200) + Next + + ; 调整结果数组大小 + If $g_iResultCount > 0 Then + ReDim $g_aResults[$g_iResultCount][4] + EndIf + + ; 生成Excel报告 + If $g_iResultCount > 0 Then + _Log_Rename("") + _Log_Rename("📊 正在生成Excel报告...") + If _GenerateExcelReport() Then + _Log_Rename("✓ Excel报告已生成: " & @ScriptDir & "\VideoFolderMapping.xlsx") + Else + _Log_Rename("❌ Excel报告生成失败") + EndIf + EndIf + + ; 显示失败的文件详情 + If $iFailCount > 0 Then + _Log_Rename("") + _Log_Rename("========================================") + _Log_Rename("⚠️ 失败文件详情:") + For $i = 0 To UBound($aFailedFiles) - 1 + Local $aParts = StringSplit($aFailedFiles[$i], "|", $STR_ENTIRESPLIT) + If $aParts[0] >= 2 Then + _Log_Rename(" " & ($i + 1) & ". " & $aParts[1]) + _Log_Rename(" 原因: " & $aParts[2]) + EndIf + Next + _Log_Rename("========================================") + EndIf + + ; 显示统计信息 + _Log_Rename("") + _Log_Rename("========================================") + _Log_Rename("📊 处理完成!") + _Log_Rename(" ✓ 成功: " & $iSuccessCount & " 个") + _Log_Rename(" ⚠️ 失败: " & $iFailCount & " 个") + _Log_Rename(" 📁 总计: " & UBound($g_aMP4Files) & " 个视频文件") + _Log_Rename("========================================") + + ; 恢复按钮状态 + GUICtrlSetState($g_hBtnSelectDir_Rename, $GUI_ENABLE) + GUICtrlSetState($g_hBtnStart_Rename, $GUI_ENABLE) + $g_bProcessing_Rename = False +EndFunc + +Func _GetNewUUID() + Local $sURL = "https://api.yeomai.site/API/ApiGetShortUUID.php" + Local $sResponse = "" + + ; 尝试最多3次 + For $i = 1 To 3 + $sResponse = BinaryToString(InetRead($sURL, 10)) + + If @error = 0 And $sResponse <> "" Then + ; 清理响应数据,移除空白字符 + $sResponse = StringStripWS($sResponse, $STR_STRIPLEADING + $STR_STRIPTRAILING) + Return $sResponse + EndIf + + Sleep(500) + Next + + Return "" +EndFunc + +Func _Log_Rename($sText) + Local $sCurrent = GUICtrlRead($g_hLogEdit_Rename) + GUICtrlSetData($g_hLogEdit_Rename, $sCurrent & $sText & @CRLF) + + ; 滚动到最后一行 + Local $iLines = GUICtrlSendMsg($g_hLogEdit_Rename, $EM_GETLINECOUNT, 0, 0) + GUICtrlSendMsg($g_hLogEdit_Rename, $EM_LINESCROLL, 0, $iLines) +EndFunc + +Func _GenerateExcelReport() + If $g_iResultCount = 0 Then Return False + + Local $oExcel = _Excel_Open(False, False, False, False, True) + If @error Then Return SetError(1, 0, False) + + Local $oWorkbook = _Excel_BookNew($oExcel, 1) + If @error Then + _Excel_Close($oExcel) + Return SetError(2, 0, False) + EndIf + + ; 设置表头 + Local $aHeaders[1][4] = [["MP4文件名", "MP4完整路径", "原文件夹名", "新UUID文件夹名"]] + _Excel_RangeWrite($oWorkbook, $oWorkbook.Activesheet, $aHeaders, "A1") + + ; 写入数据 + _Excel_RangeWrite($oWorkbook, $oWorkbook.Activesheet, $g_aResults, "A2") + + ; 格式化表格 + With $oWorkbook.Activesheet + .Range("A1:D1").Font.Bold = True + .Range("A1:D1").Interior.Color = 0xD3D3D3 + .Columns("A:A").ColumnWidth = 30 + .Columns("B:B").ColumnWidth = 60 + .Columns("C:C").ColumnWidth = 30 + .Columns("D:D").ColumnWidth = 35 + .UsedRange.Borders.Weight = 2 + EndWith + + ; 保存文件 + Local $sSavePath = @ScriptDir & "\VideoFolderMapping.xlsx" + _Excel_BookSaveAs($oWorkbook, $sSavePath, $xlOpenXMLWorkbook, True) + + If @error Then + _Excel_BookClose($oWorkbook, False) + _Excel_Close($oExcel) + Return SetError(3, 0, False) + EndIf + + _Excel_BookClose($oWorkbook, False) + _Excel_Close($oExcel) + + Return True +EndFunc \ No newline at end of file diff --git a/压缩包/au3脚本/视频转码-合并版.exe b/压缩包/au3脚本/视频转码-合并版.exe new file mode 100644 index 0000000..b141a68 Binary files /dev/null and b/压缩包/au3脚本/视频转码-合并版.exe differ