Orion9

Joined: 01 Jan 2024 Posts: 1092
|
(Separately) Posted: Thu May 07, 2026 00:33 Post subject: Подсчёт времени воспроизведения медиафайлов |
|
|
Кнопка для подсчёта времени воспроизведения аудио и видео файлов. Реализовано на скриптовом языке плагина Autorun.
Желательно установить последнюю версию плагина Autorun. На данный момент это 2.2.21.3 (15.10.2025)
Инструкция для установки (ниже на странице)
https://total.darkhost.ru/wiki/doku.php?id=ru:autorun
Особенности последней версии. Для версии 2.2.21.3 нет полного архива с дополнительными модулями (субплагинами) и файлами справки, поэтому установку придется проводить в два этапа. Сначала нужно установить версию 2.2.21 от 28.09.2025
https://total.darkhost.ru/files/autorun/beta/wdx_autorun_2.2.21_beta.zip
Поверх неё накатить последний модуль от 15.10.2025
https://total.darkhost.ru/files/temp/autorun_20251015.zip
 Кнопка TOTALCMD#BAR#DATA
71000
%COMMANDER_EXE%
Подсчёт времени воспроизведения|Alt - Использовать TCMediaInfo.wdx|Ctrl - Глубина каталога = 1|Shift - Отключить фильтр расширений
-1
 Duration.aucfg | Code: | Pragma IncludeOnce
# код команды
RegisterCommand 71000 "Duration"
# путь к библиотеке MediaInfo
Global gMediaInfoX32 = COMMANDER_PATH & "\Ini\Tools\MediaInfo_i386.dll"
Global gMediaInfoX64 = COMMANDER_PATH & "\Ini\Tools\MediaInfo.dll"
# флаги запущенной операции, тип подсказки, лог операции
Global gDurationInfo = 0, gDurationBreak = false, gDurationBaloon = 1, gDurationLog = ""
# фильтр расширений для аудио файлов
Global gFilterAudio = ".aac .ac3 .aif .aiff .aifc .afc .ape .au .snd .cda .dsf .dts .dtswav .dtshd .dtsma .eac3 .flac .fla .m1a .m2a .mka .mpa .mp1 .mp2 .mp3 .mp4 .m4a .m4b .m4r .mod .mpc .mp+ .mpp .oga .ogx .ogg .ra .spx .opus .qoa .svx .8svx .tak .tta .wav .wave .w64 .bwf .rf64 .wma .wv"
# фильтр расширений для видео файлов
Global gFilterVideo = ".avi .wmv .wmp .wm .asf .mpg .mpeg .mpe .m1v .m2v .mpv2 .mp2v .ts .tp .tpr .trp .vob .ifo .ogm .ogv .mp4 .m4v .m4p .m4b .3gp .3gpp .3g2 .3gp2 .mkv .rm .ram .rmvb .rpm .flv .swf .mov .qt .amr .nsv .dpg .m2ts .m2t .mts .dvr-ms .k3g .skm .evo .nsr .amv .divx .webm .wtv .f4v .mxf"
# внутренние переменные для подсказки
Global gBTipWndProc, gBTipWnd = Map(), gBTip = 0
Global gBText = Buffer(1024*4), gBTool = Buffer(auX64 ? 56 : 40)
Global gBTipX, gBTipY
Global gBTipWP = Callback("BTipWndProc", "hwnd;uint;wparam;lparam")
# запуск операции
Func Duration(lParam)
# операция уже запущена
# прервать и выйти при повторном вызове
If gDurationInfo > 0 Then
gDurationBreak = true
Sleep(250)
Return
EndIf
# создание окна подсказки
If gDurationBaloon Then
gBTip = CreateBaloonTip("MediaInfo", 1, 200)
If gBTip = 0 Then Return MsgBox("Не удалось создать окно подсказки BaloonTip")
EndIf
# запуск подсчета в отдельном потоке
RunThread DurationInfo
EndFunc
Func DurationInfo()
# запоминание времени старта
Local T1 = GetUptime(), T2 = T1
# состояние клавиш модификаторов
Local bCaps = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)
Local bCtrl = IsPressed(0x11), bShift = IsPressed(0x10), bAlt = IsPressed(0x12)
# загрузка библиотеки MediaInfo в зависимости от рязрядности ТС
Static sLib = auX64 ? gMediaInfoX64 : gMediaInfoX32
Static hLib = DllCall("LoadLibrary", "wstr", sLib, "ptr")
# общий фильтр для медиа файлов
Static sExt = gFilterAudio & " " & gFilterVideo
# модификатор Alt
# выбор "движка" операции
If Not bAlt Then
If hLib = 0 Then
ShowMediaHint("Error LoadLibrary " & sLib)
Return
EndIf
Static pNew = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_New", "Ptr")
Static pOpen = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Open", "Ptr")
Static pGet = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Get", "Ptr")
Static pDel = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Delete", "Ptr")
Local hMI = DllCall(pNew, "Ptr")
If hMI = 0 Then
ShowMediaHint("MediaInfo.dll couldn't create new object")
Return
Endif
Else
Local obj = Plugin("TCMediaInfo")
If ERROR <> 0 Then
ShowMediaHint("TCMediaInfo.wdx plugin error " & ERROR)
Return
Endif
EndIf
# продолжительность
Local sDuration
# выделение на панели
Local aSel = List()
aSel.Text = GetSelectedItems(3, 0)
# текущий файл на панели
Local sPath = RequestCopyDataInfo("SP")
Local sName = RequestCopyDataInfo("SN")
Local sFile = sPath & sName
# проверка существования файла
If aSel.Count = 0 Then
If Not FileExist(sFile) Then
ShowMediaHint("Файл не существует " & sFile)
Free(aSel)
If bAlt Then Free(obj)
Return
Endif
EndIf
# вывод результата для одиночного файла
If aSel.Count = 0 And Not StrPos(FileGetAttr(sFile), "D") Then
Free(aSel)
If bAlt Then
obj.FileName = sFile
sDuration = obj.GetValue(0) # duration
Free(obj)
Else
If DllCall(pOpen, "Ptr", hMI, "Wstr", sFile, "Uint") <> 1 Then
Return ShowMediaHint("MediaInfo.dll coudn't open file " & sFile)
EndIf
sDuration = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
"Wstr", "Duration/String3", "Int", 1, "Int", 0, "Wstr")
EndIf
Return ShowMediaHint("Файл: " & sName & auCRLF & "Длительность: " & sDuration, 1)
Endif
gDurationInfo = 1
gDurationBreak = false
ShowMediaHint("Загрузка", 1)
Local i, j, valid = 0, extlog = false
Local dirs = 0, files = 0, errs = 0, item = 0, aFiles = List()
Local depth = (bCtrl ? 1 : 0), ms = 0, vc = "", ac = "", seconds = 0
# загрузка файлов в массив
If aSel.Count > 0 Then
For j = 0 To aSel.Count - 1
sFile = sPath & aSel[j]
If FileExist(sFile) Then
valid += 1
aFiles.Add(sFile)
If StrPos(FileGetAttr(sFile), "D") Then
If ListMediaFiles(sFile, aFiles, depth) > 0 Then aFiles.Count = 0
EndIf
EndIf
Next
sName = aSel.Count & "/" & valid
Else
aFiles.Add(sFile)
ListMediaFiles(sFile, aFiles, depth)
EndIf
Free(aSel)
# файлов в массиве больше одного
If aFiles.Count > 1 Then
ShowMediaHint("Depth: " & depth & auCRLF & _
"Filter: " & (Not bShift ? "Yes" : "No") & auCRLF & _
(bAlt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
"Элементов: " & aFiles.Count & auCRLF & _
"Подсчёт времени...", 1)
EndIf
T2 = GetUptime()
DurationLogString("Duration.Name:" & sName)
DurationLogString("Duration.Items:" & aFiles.Count)
# обработка массива
For i = 0 To aFiles.Count - 1
# расширенный лог
If extlog Then DurationLogString("Duration.File:" & aFiles[i])
# количество каталогов
If StrPos(FileGetAttr(aFiles[i]), "D") Then
dirs += 1
# расширенный лог
If extlog Then DurationLogString("Duration.Directory:" & aFiles[i])
Continue
EndIf
item += 1
If bAlt Then
# поле плагина TCMediaInfo
obj.FileName = aFiles[i]
ms = obj.GetValue(0,3) # duration -> milliseconds
Else
# расширение текущего файла
ext = FileGetExt(aFiles[i])
# фильтр расширений включен
If Not bShift Then
# проверка текущего расширения
If Not StrPos(sExt, '.' & ext) Then
DurationLogString("Duration.Filter:" & aFiles[i])
Continue
EndIf
EndIf
# ошибка библиотеки при открытии файла
If DllCall(pOpen, "Ptr", hMI, "Wstr", aFiles[i], "Uint") <> 1 Then
errs += 1
DurationLogString("Duration.Error:" & aFiles[i])
Continue
EndIf
# количество миллисекунд
ms = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
"Wstr", "Duration", "Int", 1, "Int", 0, "Wstr")
# количество видео дорожек
vc = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
"Wstr", "VideoCount", "Int", 1, "Int", 0, "Wstr")
# количество аудио дорожек
ac = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
"Wstr", "AudioCount", "Int", 1, "Int", 0, "Wstr")
# возможный файл субтитров
If ms > 0 And vc = "" And ac = "" Then
DurationLogString("Duration.Warning:" & aFiles[i])
Continue
EndIf
EndIf
# суммирование миллисекунд и количества файлов
If ms > 0 Then
seconds += ms
files += 1
Else
DurationLogString("Duration.Empty:" & aFiles[i])
EndIf
# случилось прерывание операции
If gDurationBreak = true Or (IsPressed (0x11) And IsPressed (0x1B)) Then
ShowMediaHint("Операция прервана", 1)
Sleep(500)
If gDurationBaloon Then
Static buf = Buffer(160),
buf.Zero()
buf.SetStr("Прервано" & Chr(0))
SendMessage(gBTip, 1057, 3, buf.ptr)
EndIf
Break
EndIf
# обновление статуса каждые полсекунды
If Round(GetUptime() - T2, 0) > 500 Then
ShowMediaHint("Depth: " & depth & auCRLF & _
"Filter: " & (Not bShift ? "Yes" : "No") & auCRLF & _
(bAlt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
"Элементов: " & aFiles.Count & auCRLF & _
"Обработка: " & i & " из " & aFiles.Count, 1)
T2 = GetUptime()
EndIf
Next
If bAlt Then Free(obj)
If Not bAlt And hMI <> 0 Then DllCall(pDel, "Ptr", hMI)
gDurationInfo = 0
gDurationBreak = false
# перевод в секунды
seconds = Floor(seconds / 1000)
# вычисление прочих состовляющих
Local day = Floor(seconds / 86400)
Local hour = Floor((seconds - (day * 86400)) / 3600)
Local min = Floor(((seconds - (day * 86400)) - (hour * 3600)) / 60)
Local sec = seconds - (day * 86400 + hour * 3600 + min * 60)
Local total = StrFormat("%02d:%02d:%02d:%02d",day,hour,min,sec)
Local tl = Round(GetUptime() - T1, 0) / 1000
# отчет текущей операции
Local txt = "Имя: " & sName & auCRLF & _
"Каталогов: " & dirs & auCRLF & _
"Элементов: " & aFiles.Count & auCRLF & _
"Обработано: " & files & " из " & item & auCRLF & _
"Ошибки DLL: " & errs & auCRLF & _
"Секунды: " & seconds & auCRLF & _
"Минуты: " & Round(seconds / 60, 2) & auCRLF & _
"Часы: " & Round(seconds / 3600, 2) & auCRLF & _
"Сутки: " & Round(seconds / 86400, 2) & auCRLF & _
"Время: " & Chr(0x2211) & " "& total & auCRLF & _
"Powered by " & (bAlt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
"Время операции: " & StrFormat("%.3f", tl) & " sec"
Free(aFiles)
# запись в лог
DurationLogString(txt & auCRLF & auCRLF)
# копирование в буфер
ClipPut(txt)
# отображение отчета
ShowMediaHint(txt, 1)
EndFunc
Func ListMediaFiles(sPath, ByRef aList, nDepth = 1)
Local sFile, nAttr
Local ffd = Buffer(604), bEsc = false
Local hf = DllCall("FindFirstFileW", "wstr", sPath & "\*.*", "ptr", ffd.Ptr)
If hf <> 0 then
While True
sFile = ffd.GetStr(44) # cFileName
nAttr = ffd.GetNum(0, "dword") # dwFileAttributes
If BitAND(nAttr, 16) Then
If Not ((sFile = ".") Or (sFile = "..")) Then
aList.Add(sPath & "\" & sFile)
If nDepth <> 1 Then
ListMediaFiles(sPath & "\" & sFile, aList, nDepth - 1)
EndIf
EndIf
Else
aList.Add(sPath & "\" & sFile)
Endif
If DllCall("FindNextFileW", "handle", hf, "ptr", ffd.Ptr) = 0 Then Break
If IsPressed (0x1B) Then
bEsc = true
Break
EndIf
Wend
DllCall("FindClose", "handle", hf)
Endif
Free(ffd)
Return bEsc
EndFunc
Func DurationLogString(LogString)
gDurationLog &= LogString & auCRLF
EndFunc
Func ShowMediaHint(MediaHint, Persist = false)
# удаление подсказки тотала
If gDurationBaloon And gBTip > 0 Then
Local hTip = WinFind(0, "TToolTip")
If hTip > 0 Then SendMessage(hTip, 0x0010, 0, 0)
UpdateBaloonTip(gBTip, MediaHint)
Return
EndIf
# при неиспользовании BaloonTip
# используется подсказка Autorun
SetHintParam("ShowHint", "Font", 11, "Arial")
#SetHintParam("ShowHint", "BackColor", 0x000000)
#SetHintParam("ShowHint", "Text", 0xFFFFFF)
SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
SetHintParam("ShowHint", "DarkText", 0xFFFFFF)
If Persist Then
ShowHint(MediaHint)
Else
ShowHint(MediaHint, "", "", 3000, 1)
EndIf
Sleep(10)
SetHintParam("ShowHint", "Reload")
EndFunc
Func CreateBaloonTip(TipTitle, TipIcon, TipWidth)
Static TTM_TRACKACTIVATE = 1041, _
TTM_TRACKPOSITION = 1042, _
TTM_SETMAXTIPWIDTH = 1048, _
TTM_SETTITLE = 1057, _
TTM_ADDTOOL = 1074
Local hTip
hTip = DllCall("CreateWindowExW", _
"dword", 8, _
"wstr", "tooltips_class32", _
"wstr", "", _
"dword", 0x00000c0, _
"int", 0, "int", 0, "int", 0, "int", 0, _
"handle", AUTORUN_TCHANDLE, _
"handle", 0, "handle", 0, "ptr", 0, "handle")
If hTip = 0 Then Return 0
MouseGetPos("gBTipX","gBTipY")
#gBTipWnd.Set(hTip, MakeInt(gBTipX, gBTipY, 2))
gBTipWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
"hwnd", hTip, "int", -4, "long_ptr", gBTipWP.Ptr, "ptr")
Local buf = Buffer(160)
buf.Zero()
buf.SetStr(TipTitle & Chr(0))
SendMessage(hTip, TTM_SETTITLE, TipIcon, buf.ptr)
gBText.Zero()
gBText.SetStr("Инициализация" & Chr(0))
gBTool.Zero() # TTTOOLINFO
If auX64 Then
gBTool.SetNum(0, "uint", gBTool.size, _
"uint", 0x00a0, _
"hwnd", AUTORUN_TCHANDLE, _
"uint_ptr", 0)
gBTool.SetNum(48, "ptr", gBText.ptr)
Else
gBTool.SetNum(0, "uint", gBTool.size, _
"uint", 0x00a0, _
"hwnd", AUTORUN_TCHANDLE, _
"uint_ptr", 0, _
"long", 0, _
"long", 0, _
"long", 0, _
"long", 0, _
"ptr", 0, _
"ptr", gBText.ptr)
EndIf
SendMessage(hTip, TTM_SETMAXTIPWIDTH, 0, TipWidth)
SendMessage(hTip, TTM_ADDTOOL, 0, gBTool.ptr)
SendMessage(hTip, TTM_TRACKACTIVATE, 1, gBTool.ptr)
SendMessage(hTip, TTM_TRACKPOSITION, 0, MakeInt(gBTipX, gBTipY, 0))
Free(buf)
Return hTip
EndFunc
Func UpdateBaloonTip(hTip, TipText)
Static TTM_UPDATETIPTEXT = 1081
gBText.Zero()
gBText.SetStr(TipText & Chr(0))
SendMessage(gBTip, TTM_UPDATETIPTEXT, 0, gBTool.ptr)
EndFunc
Func BTipWndProc(hWnd, uMsg, wParam, lParam)
Static IsDrag = 0, _
WM_CREATE = 0x0001, _
WM_DESTROY = 0x0002, _
WM_SHOWWINDOW = 0x0018, _
MK_LBUTTON = 0x0001, _
WM_MOUSEMOVE = 0x0200, _
WM_LBUTTONDOWN = 0x0201, _
WM_LBUTTONUP = 0x0202, _
WM_RBUTTONUP = 0x0205, _
TTM_TRACKACTIVATE = 1041, _
TTM_TRACKPOSITION = 1042
Static SM_CXDRAG = 68, _
SM_CYDRAG = 69, _
DragWidth = GetSystemMetrics(SM_CXDRAG), _
DragHeight = GetSystemMetrics(SM_CYDRAG), _
StartX = 0, _
StartY = 0
Local x, y
Static tx, ty, buf = Buffer(8)
Switch uMsg
Case WM_SHOWWINDOW
If wParam = 0 Then DllCall("DestroyWindow", "handle", hWnd)
Case WM_DESTROY
If gDurationInfo Then
gDurationBreak = true
MsgBox("Операция отменена")
Return 0
EndIf
Case WM_LBUTTONDOWN
If IsDrag = 0 Then
buf.Zero()
x = BitAND(lParam, 0xFFFF)
y = BitAND(BitShift(lParam,16), 0xFFFF)
StartX = x
StartY = y
buf.SetNum(0, "int", x, "int", y)
DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
tx = buf.GetNum(0)
ty = buf.GetNum(4)
WinGetPos("gBTipX", "gBTipY", "", "", hWnd)
IsDrag = 1
DllCall("SetCapture", "hwnd", hWnd, "hwnd")
EndIf
Case WM_LBUTTONUP
If IsDrag = 1 Then
WinGetPos("gBTipX", "gBTipY", "", "", hWnd)
IsDrag = 0
DllCall("ReleaseCapture")
Else
DllCall("DestroyWindow", "handle", hWnd)
EndIf
Case WM_MOUSEMOVE
If IsDrag = 1 And BitAND(wParam, MK_LBUTTON) Then
x = BitAND(lParam, 0xFFFF)
y = BitAND(BitShift(lParam,16), 0xFFFF)
If Abs(x - startX) > DragWidth Or Abs(y - startY) > DragHeight Then
buf.Zero()
buf.SetNum(0, "int", x, "int", y)
DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
x = buf.GetNum(0)
y = buf.GetNum(4)
SendMessage(hWnd, TTM_TRACKPOSITION, 0, MakeInt(gBTipX+(x-tx), gBTipY+(y-ty), 0))
EndIf
EndIf
Case WM_RBUTTONUP
BTipShowMenu(hWnd)
EndSwitch
Return DllCall("CallWindowProcW", _
"ptr", gBTipWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc
Func BTipShowMenu(hWnd)
ShowPopupMenu /D /F /I:0 "BTipCreateMenu"
EndFunc
Func BTipCreateMenu()
Local txt
txt &= 'MENUITEM "Лог операций", em_aucmd ' & (gDurationLog <> "" ? "" : "/D") & ' -1 DurationText' & auCRLF
Return txt
EndFunc
Func DurationText()
Local sLogFile = TEMP & "\duration.txt"
FileWrite(sLogFile, gDurationLog)
ShellExec(sLogFile)
EndFunc |
Модуль можно подключить к основной конфигурации плагина строкой
| Code: | Pragma Include %COMMANDER_PATH%\Ini\Autorun\Duration.aucfg |
Пример файла autorun.cfg чистого плагина
 Hidden text | Code: | Pragma AutorunBlockUnload
LoadLibrary Plugins\Autorun_Tweaks.dll
LoadLibrary Plugins\Autorun_Sysinfo.dll
LoadLibrary Plugins\Autorun_Runtime.dll
LoadLibrary Plugins\Autorun_Process.dll
Pragma Include %COMMANDER_PATH%\wdx\Autorun\Duration.aucfg
Pragma AutorunFinalizeSection |
В заголовке модуля необходимо задать путь к файлам библиотеки MediaInfo. Кнопка может также работать с плагином TCMediaInfo, если он установлен и настроен по дефолту.
Модификатор Shift позволяет отключить фильтр расширений, указанных в заголовке модуля. По умолчанию фильтр включен, т.к. он ускоряет операцию подсчета (MediaInfo не приходится открывать каждый файл в каталоге). Однако бывают случаи, когда нужно обработать все файлы. Если файл при этом не содержит сведений о времени воспроизведения, в лог заносится "Empty".
При отключенном фильтре текстовые файлы субтитров могут возвращать информацию о времени воспроизведения. В режиме MediaInfo.dll такие файлы в расчет не принимаются (в лог записывается "Warning"), но если по какой-то причине нужно посчитать длительность таких файлов, можно использовать режим плагина TCMediaInfo.wdx через запуск с Alt. Это возможно, поскольку плагин TCMediaInfo.wdx имеет собственный файл конфигурации и собственные фильтры.
Операцию можно прервать в любой момент либо повторным запуском, либо закрытием окна подсказки, либо комбинацией Ctrl+ESC. В первом и последнем случае отобразится промежуточный результат. Окно подсказки можно также закрыть двойным кликом по самой подсказке. Правый клик открывает меню лога операций.
Можно держать открытыми несколько окон подсказки и перемещать их по экрану.
 Скрин с примером
Ошибки и недочеты могут быть, но в основном кнопка рабочая. Надеюсь, кому-то пригодится. |
|