前言
这是关于上一篇文章的补充,以及对一些问题的说明,主要是脚本内容更新。
脚本方面虽然不涉及大量反向工程和源代码破解,应该是不构成侵权。行为算是做了个 自用 的插件。但为了安全并且还是习惯于文档类型的写作,没有在github上创建项目。
算是免责声明?我一般发文默认都带着CC 4.0 BY-SA版权,这个协议的要求是转载请附上原文出处链接和声明,虽然通常有些人转载和搬运,修改都不改,也不带链接,但实际上只要不是为了什么原因或者原创的名头,去过来反咬一口或者做出其他行为让人心里堵塞,也不是重要的事情。改点就当他写了笔记,全搬可以认为是他收藏或摘抄。
最终目的是想说:这里的内容没毒。但是如果有人乱搬运,乱搞塞病毒导致的问题,或者搬运什么东西到什么平台导致的侵权,和作者无关。
为啥发了个这内容。很久之前知道一个当代赛博菩萨,喜欢热心分享,结果有人把她整理的包和写的代码到处卖,还有人加病毒,还添加了她的某账号,让用户去评论区问她,被动的当了客服。故事挺有意思,听了又觉得无语。提前说,省的有人加点东西或者乱搞,骂到我这。
配置简述
之前的文章大致说了一下去重的经验,没有实际的说明如何勾选。
通常来说,这个软件可以切换不同的语言,实际上多用几次就会了或者查看官方文档进行翻译。但是还是说一些小经验,可以节省几天的摸索。
<1>主页(拥有导航)- 探索规则 - 扫描位置 - 工作界面 - 重复文件 - 删除菜单 - 工具包 - 设置
<2>主页
基本是个演示配置工作流程的界面,可以从这里进行配置扫描工作流程。但是常用的还是点击顶端的按钮依次进行配置
有用的一点是可以导入csv,csv要包含需要扫描的重复文件列表
- 创建虚拟文件夹相当于根据一个源目录的md5等信息创建一个虚拟目录,里面包含对应md5等信息,由于计算的是哈希值速度极快
- 在扫描的时候会快速比较源目录文件是否有变动,对比的是哈希值
- 需要在扫描位置添加源目录和这个虚拟目录,使用常规模式,对比速度几乎是瞬间
- 缺点是只能对比源目录和虚拟目录,对比是否有变动
- 可以在首页的虚拟目录位置移除创建的虚拟目录
<3>探索规则
通常从这一步开始进行工作流程的配置。根据不同文件和目的选择不同的模式,每个模式只扫描目录中的对应的文件类型
要是明确知道扫描的文件得具体情况,可以进行具体配置,加快效率。例如:相似的文件名,相同文件名,日期等。
<4>扫描位置
- 从左边选择要扫描的磁盘,文件夹或文件,出现向右的绿色箭头后,点击就能添加到搜索位置
- 如果磁盘或者手机等未连接或者不想去扫描可以点击已包含,变成已排除后,此次扫描不扫描已排除的磁盘或文件
- 锁头那个按钮,代表是否可添加保护。开启时候可删除目录中的文件,关闭的时候不可以删除这个目录中的文件
- 清空列表会导致搜索位置的内容全部清空,不会清理已扫描过的具体的文件数据。已经扫描过的数据会在数据库中,重新进入扫描流程后,已扫描成功的会快速略过。通常不建议清空,避免每次都添加
- 刷新列表是对搜索位置的内容刷新,获取最新状态,比如剩余空间之类的
- 要从搜索位置取消某个目录,右键选择从列表中移除所选目录即可
- 跳过忽略清单中的文件/文件夹,其中包含需要跳过,不扫描的具体的文件信息。可以笔形的按钮那里,打开并进行修改
<5>工作界面
类似播放按钮那个位置。包含具体的数据信息和工作流程
<6>重复文件界面
- 在扫描完成后出现,展示可能的重复文件,并进行处理。 由于精度问题,不一定都是重复文件,只有哈希值精确度最高。
- 左边的选择助手这里是,用于标记的勾选选项,符合条件的会自动勾选,类似于条件判断。下方的选择可以做到更多,可以尝试配置,它会进行标记
- 点击右边勾选的内容,右键会弹出要选择的处理方法,删除,反选,取消全部标记,添加到忽略清单
- 双击文件可以直接打开,视频会播放,图片会打开
- 过滤器的选项可以做到更多,符合条件的会过滤出来,而不是排除。像find和findstr,就是找符合条件的文件的意思
<7>删除界面
- 可以配置如何删除,建议勾选删除文件到回收站,有一次反悔机会,否则会直接删除
- 导出可以将重复的文件信息导出到csv,可用于再次去重或其他。
- 链接文件需要以管理员身份运行这个软件,可以将重复文件删除,并在原来位置创建链接或快捷方式。不建议移动硬盘间搞这个,首先只支持ntfs格式,并且因为有可能盘符会变化导致失败。只建议笔记本或电脑上的ntfs格式的磁盘间做这些。
<8>工具包
公文包图标那里。具有独立的工具,可以对选择的目录或文件,完成移除空目录或者对比哈希的工作。
<9>设置
这是最后的设置界面。
- 建议勾选缓存。如果要完全的重新开始计算,点击清理缓存
- 最下面的重置相当于软件格式化,所有设置和数据清空
GPU加速2.0
主要是解决传参和重组命令中出现特殊字符* !& ?| 等时,导致的参数分割出错,导致文件找不到或跳过,出现转码失败报错的问题。因为人类的需求总是千奇百怪,并且不同国家不同的字符,导致有大量奇怪且不一定规则的文件名。
关于bat转换成exe查看上一篇文档
https://blog.csdn.net/GX_1_11_real/article/details/149057998
打包好的文件已上传,不想打包的,可直接使用。上一个压缩包懒得删除了。
将这些打包好的文件直接上传到Duplicate cleaner pro安装目录即可,运行解压缩视频过程时,会在此目录生成日志ffmpeg_log
编写bat脚本
建议bat脚本中不使用中文,避免乱码;
if判断或者循环内部不写注释,避免转换出现问题;
如果是使用记事本编写,记得另存为ansi编码
还是要避免函数和嵌套与复杂写法,和上一个版本的资源占用情况基本一样。创建和使用了几个小文件,性能影响不大。
由于用set转换特殊字符,尤其是!,在转换和运行中始终会出现问题,于是配置js脚本和通过对文件的操作,用于对特殊字符!转换。
由于只添加!显得多余,(? 和* )的转换也添加到了js脚本中,以后可通过单独修改js脚本,完成特殊字符的转换
基本思路:
- 开启延伸扩展(setlocal enabledelayedexpansion)之前,获取完整的参数,可包含特殊字符,尤其是!
- 如果写在开启延伸扩展之后,!会被识别为变量的符号。这种情况下加^进行脱义不会有任何效果
- 将参数和获取的完整内容写入文件,查询到包含特殊字符的,将通过js将特殊字符转换成特定的字符串
- 命令重组完成后,通过js将特定的字符串转换回特殊字符
- 为了避免watchdog未成功删除或者残留它对应的cmd进程,影响后续运行,在多个位置删除watchdog
代码在下方可根据具体情况修改
ffmpeg.bat
@echo off
set "tmp_file=%temp%\param_%random%.tmp"
set "tmp_new=%temp%\param_new_%random%.tmp"
set "tmp_cpu=%temp%\param_cpu_%random%.tmp"
set "tmp_cuda=%temp%\param_cuda_%random%.tmp"
set "tmp_cpu_line=%temp%\param_cpu_line_%random%.tmp"
set "tmp_cuda_line=%temp%\param_cuda_line_%random%.tmp"
(
echo %*
) > "%tmp_file%"
setlocal enabledelayedexpansion
:: Set the directory where the script is located as the working directory
set "has_cuda=0"
set "script_dir=%~dp0"
set "log_file=%script_dir%ffmpeg_log.txt"
set "orig_exe=%script_dir%ffmpeg_orig.exe"
set STATE_FILE=%temp%\ffmpeg_state.lock
set "js_script=%script_dir%replace.js"
set "js_script2=%script_dir%replace2.js"
set INPUT="%tmp_file%"
set /p input_cmd=<"%INPUT%"
:: Enhance logging functionality
echo =========================================== >> "%log_file%"
echo [%date% %time%] INFO: script start >> "%log_file%"
echo =========================================== >> "%log_file%"
:: Verify that the file exists and is executable
if not exist "%orig_exe%" (
echo ========================================================== >> "%log_file%"
echo [%date% %time%] [ERROR] ffmpeg_orig.exe does not exist >> "%log_file%"
echo ========================================================== >> "%log_file%"
exit /b 1
)
:: CUDA detection
where nvidia-smi >nul 2>&1
if !errorlevel! equ 0 set "has_cuda=1"
:: initialize variable
set "new_filter="
set "original_filter="
set "filter_processed=0"
set "in_quote=0"
set "found=0"
for /f "delims=" %%a in ('type "%tmp_file%" ^| findstr /r "[^!?*]"') do (
set "found=1"
)
if %found% equ 1 (
echo "Special Content" >> "%log_file%"
cscript //nologo "%js_script%" "%INPUT%" > "%tmp_new%"
set /p input_cmd=<"!tmp_new!"
) else (
echo "Original content" >> "%log_file%"
set /p input_cmd=<"%INPUT%"
)
:: cleaner file
del %tmp_new%
del %tmp_file%
:: Improved extraction of raw filter parameters (handling quoted cases)
for %%a in (%input_cmd%) do (
if "%%a"=="-filter_complex" (
set "next_is_filter=1"
) else if defined next_is_filter (
if "!original_filter!"=="" (
set "original_filter=%%a"
) else (
set "original_filter=!original_filter! %%a"
)
if "%%a"==""*"" set /a "in_quote=!in_quote!^^1"
if !in_quote! equ 0 set "next_is_filter="
)
)
::Remove quotation marks
set "original_filter=!original_filter:"=!"
echo "original_filter %original_filter%" >> "%log_file%"
:: Dynamically build new filter parameters (preserve complete structure)
if defined original_filter (
:: Remove duplicate select sections
set "temp2=!original_filter:select=!"
if not "!temp2!"=="!original_filter!" (
set "new_filter=[0:v]hwdownload,format=nv12,!original_filter!"
) else (
set "new_filter=[0:v]hwdownload,format=nv12,!original_filter!"
)
:: Fix escape characters and quotation marks
:: set "new_filter=!new_filter:\=!"
set "new_filter=!new_filter:""=!"
)
echo "new_filter %new_filter%" >> "%log_file%"
:: Skip the original ffmpeg when refactoring CPU commands
set "cpu_cmd="
set "skip_first=0"
for %%A in (%input_cmd%) do (
set "current_arg=%%A"
if "!current_arg!"=="%script_dir%ffmpeg.exe" (
set "skip_first=1"
) else if !skip_first! equ 1 (
set "skip_first=0"
) else (
set "cpu_cmd=!cpu_cmd! !current_arg!"
)
)
:: Skip original filter parameters when refactoring CUDA commands
set "new_cmd="
set "filter_processed=0"
set "skip_next=0"
for %%A in (%input_cmd%) do (
set "current_arg2=%%A"
if "!current_arg2!"=="%script_dir%ffmpeg.exe" (
set "skip_next=1"
) else if !skip_next! equ 1 (
set "skip_next=0"
) else if "!current_arg2!"=="-filter_complex" (
if !filter_processed! equ 0 (
set "new_cmd=!new_cmd! -filter_complex "!new_filter!""
set "filter_processed=1"
set "skip_next=1"
) else (
set "new_cmd=!new_cmd! !current_arg2!"
)
) else (
set "new_cmd=!new_cmd! !current_arg2!"
)
)
::Convert string to character
echo !cpu_cmd! > "%tmp_cpu%"
echo !new_cmd! > "%tmp_cuda%"
cscript //nologo "%js_script2%" "%tmp_cpu%" > "%tmp_cpu_line%"
set /p cpu_cmd=<"%tmp_cpu_line%"
cscript //nologo "%js_script2%" "%tmp_cuda%" > "%tmp_cuda_line%"
set /p cuda_cmd=<"%tmp_cuda_line%"
del %tmp_cpu%
del %tmp_cuda%
del %tmp_cpu_line%
del %tmp_cuda_line%
:: Build two command schemes
set exec_status=0
echo "cuda_cmd !cuda_cmd!" >> "%log_file%"
echo "cpu_cmd !cpu_cmd!" >> "%log_file%"
echo "has_cuda %has_cuda%" >> "%log_file%"
:: cleaner watchdog_ffmpeg
taskkill /f /t /im watchdog_ffmpeg.exe >nul 2>&1
for /f "tokens=2 delims=," %%i in (
'wmic process where "name='cmd.exe' and commandline like '%%watchdog_ffmpeg.exe%%' and not commandline like '%%%~nx0%%'" get ProcessId /format:csv ^| findstr [0-9]'
) do (
wmic process where ProcessId=%%i delete >> "%log_file%" 2>&1
)
del %STATE_FILE%
:: Execute logic with synchronous timeout detection and automatic rollback
if !has_cuda! equ 1 (
echo ============================================================== >> "%log_file%"
echo [%date% %time%] INFO: Using CUDA acceleration plan1 CUDA >> "%log_file%"
echo "%orig_exe%" -hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid -loglevel !cuda_cmd! >> "%log_file%"
echo ============================================================== >> "%log_file%"
start "CUDA_Watchdog" /B cmd /c watchdog_ffmpeg.exe
"%orig_exe%" -hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid -loglevel !cuda_cmd! >> "%log_file%" 2>&1
set exec_status=!errorlevel!
)
:: moxiao.GX
:: STATE_FILE
if exist "%STATE_FILE%" (
echo [%date% %time%] WARN: CUDA timed out after 240s seconds >> "%log_file%"
set /p exec_status=<"%STATE_FILE%"
del %STATE_FILE%
)
:: CPU fallback execution branch
if !exec_status! neq 0 (
echo [%date% %time%] WARN: CUDA failed !exec_status!, falling back to CPU >> "%log_file%"
echo [%date% %time%] INFO: Starting CPU execution >> "%log_file%"
echo "%orig_exe%" -loglevel !cpu_cmd! >> "%log_file%"
echo ================================================== >> "%log_file%"
taskkill /f /t /im watchdog_ffmpeg.exe >nul 2>&1
del %STATE_FILE%
"%orig_exe%" -loglevel !cpu_cmd! >> "%log_file%" 2>&1
set exec_status=!errorlevel!
)
:: error handling
if !exec_status! equ 0 (
echo ============================================================ >> "%log_file%"
echo [%date% %time%] [INFO] Execution succeeded >> "%log_file%"
echo ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> "%log_file%"
) else (
echo ============================================================ >> "%log_file%"
echo [%date% %time%] ERROR: Execution failed !exec_status! >> "%log_file%"
echo ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> "%log_file%"
exit /b !exec_status!
)
:: cleaner watchdog_ffmpeg
taskkill /f /t /im watchdog_ffmpeg.exe >nul 2>&1
for /f "tokens=2 delims=," %%i in (
'wmic process where "name='cmd.exe' and commandline like '%%watchdog_ffmpeg.exe%%' and not commandline like '%%%~nx0%%'" get ProcessId /format:csv ^| findstr [0-9]'
) do (
wmic process where ProcessId=%%i delete >> "%log_file%" 2>&1
)
del %STATE_FILE%
endlocal
watchdog_ffmpeg.bat 没有改动
@echo off
setlocal enabledelayedexpansion
::moxiao_Gx
set cuda_timeout=240
set "script_dir=%~dp0"
set "log_file=%script_dir%ffmpeg_log.txt"
set STATE_FILE=%temp%\ffmpeg_state.lock
del %STATE_FILE%
timeout /t %cuda_timeout% /nobreak >nul
tasklist | findstr "ffmpeg_orig.exe" >nul
if %errorlevel% equ 0 (
taskkill /f /im ffmpeg_orig.exe >nul 2>&1
echo TIMEOUT_GPU > "%temp%\ffmpeg_state.lock"
exit /b 1
) else (
exit /b 0
)
endlocal
编写js脚本
replace.js
var fso = new ActiveXObject("Scripting.FileSystemObject");
try {
var file = fso.OpenTextFile(WScript.Arguments(0), 1); // 1=ForReading
var str = file.ReadAll();
file.Close();
str = str.replace(/\!/g,'#BANG#')
.replace(/\?/g,'#QMARK#')
.replace(/\*/g,'#AST#');
WScript.Echo(str);
} catch(e) {
WScript.Echo("错误:" + e.message);
WScript.Quit(1);
}
replace2.js
var fso = new ActiveXObject("Scripting.FileSystemObject");
try {
var file = fso.OpenTextFile(WScript.Arguments(0), 1); // 1=ForReading
var str = file.ReadAll();
file.Close();
str = str.replace(/#BANG#/g,'!')
.replace(/#QMARK#/g,'?')
.replace(/#AST#/g,'*');
WScript.Echo(str);
} catch(e) {
WScript.Echo("错误:" + e.message);
WScript.Quit(1);
}
实际效果
速度和上一版本没有变化。仅仅是支持了更多的特殊字符,用户的文件名可以更奇怪点。
已经支持了键盘上能找到的字符,以及大多数文件名中可能出现的字符和图案,不仅下面列出的。
* !& ?? | \ @ # ¥ % $ % ^ ()- = _ + { } " ' ~ ` . , 「 」 《》 < > ★ x ﹏﹋︴ˉ﹂﹄︼﹁﹃︻︼﹀︺︾︾ˉ﹂﹄︼
不支持其他编码中可能与系统不兼容或者有特殊效果的字符
目前已发现的,例如:
例如:
希腊字符
Æ Ç ı Ø Œ ΐ
日本字符
日语中的中顿符号“・”
・
"〜"是全角符号(U+FF5E),多见于日文 "~"是半角符号(U+007E),常用英文输入法的输出
~
解决方法
如何处理更多特殊字符或者如何对特殊字符导致问题的文件执行扫描?
方法一
因为这种文件名不符合通常的命名格式的问题,只能建议修改文件名。相比于修改代码,修改文件名相对简单
方法二
如果想要程序支持,只能修改代码,建议修改两个js文件进行添加,并将js转换成能正确解析这些字符的编码
方法三
在使用这个劫持程序执行时,参数传递中,具有不可转换或不可正常识别的字符的文件名的文件,在重组命令的时候就会异常,通常是文件名异常,找不到指定文件,cuda命令和cpu命令会报错,1s内快速的进行跳过。
- 在使用这个程序执行完一遍后,退出软件
- 修改文件名,将劫持程序的ffmpeg.exe修改成其他名称;Duplicate cleaner
pro自带的ffmpeg_orig.exe(最初的ffmpeg.exe)改回ffmpeg.exe;意思是使用原来的ffmpeg.exe运行,不使用加速程序 - 开启软件,再次扫描。之前扫描完成并且成功的文件,都记录在了数据库。再次运行,对比后会将数据对上的 快速略过,不会执行ffmpeg或其他算法,只会把剩下需要扫描的或之前失败的文件,会再次调用ffmpeg.exe扫描和计算。因为这时候大多数执行扫描的文件是,文件本身或文件名异常导致。要是文件本身问题必定会失败,文件名异常的未必文件本身有问题,有可能运行成功。这时候日志中基本还是报错的多,这个现象是正常的。
使用GPU加速程序的ffmpeg.exe产生的日志:
ffmpeg_log.txt
C:\Users\admin\Documents\Duplicate Cleaner log file.txt
使用原来的ffmpeg.exe产生的日志:
C:\Users\admin\Documents\Duplicate Cleaner log file.txt