视频转码序号版本
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
; HLS_Batch_Converter_Multi.au3
|
||||
; 支持最多 8 个并发转码 + 实时显示 ffmpeg 日志
|
||||
; ✅ 新增:递归处理子目录中的视频文件
|
||||
; ✅ 新增:日志中显示 [序号/总数] 和完整视频路径
|
||||
|
||||
#include <GUIConstantsEx.au3>
|
||||
#include <EditConstants.au3>
|
||||
#include <WindowsConstants.au3>
|
||||
#include <File.au3>
|
||||
#include <Array.au3>
|
||||
|
||||
; ========== 配置 ==========
|
||||
Global Const $MAX_CONCURRENT = 8
|
||||
|
||||
; ========== 全局变量 ==========
|
||||
Global $g_sSourceDir = ""
|
||||
Global $g_hLogEdit, $g_hBtnStart, $g_hBtnSelect, $g_hLabelStatus
|
||||
|
||||
; 任务队列和运行中任务
|
||||
Global $g_aPendingFiles[0] ; 待处理文件列表 (完整路径)
|
||||
Global $g_aRunningTasks[0][4] ; [PID, FileName, OutputDir, LogHandle]
|
||||
Global $g_iSuccess = 0
|
||||
Global $g_bConverting = False
|
||||
|
||||
; 新增:用于显示进度序号
|
||||
Global $g_iTotalFiles = 0
|
||||
Global $g_iCurrentIndex = 0
|
||||
|
||||
; ========== 创建主窗口 ==========
|
||||
GUICreate("HLS 批量转码工具 - 多线程版", 700, 550)
|
||||
GUICtrlCreateLabel("1. 请选择包含视频文件的源目录:", 10, 10, 400, 20)
|
||||
|
||||
$g_hBtnSelect = GUICtrlCreateButton("选择目录...", 10, 35, 100, 30)
|
||||
$g_hLabelStatus = GUICtrlCreateLabel("", 120, 40, 570, 20)
|
||||
GUICtrlSetColor(-1, 0x0000FF)
|
||||
|
||||
GUICtrlCreateLabel("2. 点击“开始转码”(最多 " & $MAX_CONCURRENT & " 个并发):", 10, 75, 400, 20)
|
||||
$g_hBtnStart = GUICtrlCreateButton("开始转码", 10, 100, 100, 30)
|
||||
GUICtrlSetState($g_hBtnStart, $GUI_DISABLE)
|
||||
|
||||
GUICtrlCreateLabel("实时日志(格式: [视频名] 日志内容):", 10, 140, 300, 20)
|
||||
$g_hLogEdit = GUICtrlCreateEdit("", 10, 165, 680, 370, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $WS_VSCROLL))
|
||||
GUICtrlSetFont($g_hLogEdit, 9, 400, 0, "Consolas")
|
||||
|
||||
GUISetState(@SW_SHOW)
|
||||
|
||||
; ========== 消息循环 ==========
|
||||
While 1
|
||||
Switch GUIGetMsg()
|
||||
Case $GUI_EVENT_CLOSE
|
||||
; 清理子进程
|
||||
_KillAllRunningTasks()
|
||||
Exit
|
||||
Case $g_hBtnSelect
|
||||
_SelectSourceDir()
|
||||
Case $g_hBtnStart
|
||||
_StartConversion()
|
||||
EndSwitch
|
||||
|
||||
; 如果正在转码,定期检查任务状态(模拟异步)
|
||||
If $g_bConverting Then
|
||||
_PollRunningTasks()
|
||||
Sleep(50) ; 减轻 CPU 占用
|
||||
EndIf
|
||||
WEnd
|
||||
|
||||
; ========== 函数定义 ==========
|
||||
Func _SelectSourceDir()
|
||||
Local $sDir = FileSelectFolder("请选择视频所在的源目录", "")
|
||||
If Not @error And $sDir <> "" Then
|
||||
$g_sSourceDir = $sDir
|
||||
GUICtrlSetData($g_hLabelStatus, "已选择: " & $g_sSourceDir)
|
||||
GUICtrlSetState($g_hBtnStart, $GUI_ENABLE)
|
||||
_Log("✅ 目录已选择:" & $g_sSourceDir)
|
||||
Else
|
||||
$g_sSourceDir = ""
|
||||
GUICtrlSetData($g_hLabelStatus, "未选择目录")
|
||||
GUICtrlSetState($g_hBtnStart, $GUI_DISABLE)
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func _StartConversion()
|
||||
If $g_sSourceDir = "" Or $g_bConverting Then Return
|
||||
|
||||
$g_bConverting = True
|
||||
GUICtrlSetState($g_hBtnStart, $GUI_DISABLE)
|
||||
GUICtrlSetState($g_hBtnSelect, $GUI_DISABLE)
|
||||
_Log(@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("❌ 目录中没有找到任何支持的视频文件。")
|
||||
_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, "^.*\\", "") ; 如 "video.mp4"
|
||||
Local $sBaseName = StringRegExpReplace($sFileName, "\.[^.\\]+$", "") ; 如 "video"
|
||||
|
||||
; 获取视频所在目录(关键!)
|
||||
Local $sVideoDir = StringLeft($sFilePath, StringInStr($sFilePath, "\", 0, -1) - 1)
|
||||
|
||||
; 输出目录 = 视频所在目录 \ 视频名(无扩展名)
|
||||
Local $sOutputDir = $sVideoDir & "\" & $sBaseName
|
||||
|
||||
If DirCreate($sOutputDir) = 0 Then
|
||||
_Log("[系统] ⚠️ 无法创建目录: " & $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("[" & $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("[" & $g_iCurrentIndex & "/" & $g_iTotalFiles & "] [" & $sBaseName & "] 🔄 开始转码...")
|
||||
_Log(" 路径: " & $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("[" & $sName & "] ✅ 成功完成")
|
||||
Else
|
||||
_Log("[" & $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(@CRLF & "🎉 所有转码任务完成!成功处理 " & $g_iSuccess & " 个视频。")
|
||||
GUICtrlSetState($g_hBtnStart, $GUI_ENABLE)
|
||||
GUICtrlSetState($g_hBtnSelect, $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($sText)
|
||||
Local $sCurrent = GUICtrlRead($g_hLogEdit)
|
||||
Local $sNew = $sCurrent & $sText & @CRLF
|
||||
If StringLen($sNew) > 2000 Then
|
||||
$sNew = StringRight($sNew, 2000)
|
||||
EndIf
|
||||
GUICtrlSetData($g_hLogEdit, $sNew)
|
||||
GUICtrlSendMsg($g_hLogEdit, $EM_LINESCROLL, 0, GUICtrlSendMsg($g_hLogEdit, $EM_GETLINECOUNT, 0, 0))
|
||||
EndFunc
|
||||
Binary file not shown.
Reference in New Issue
Block a user