今天有一個臨時的需求,就是需要將一個wmv的初步轉碼成mp4的格式。找了一圈,免費的工具少,即使有免費的工具,在功能上也是有所限制,或者會給你塞廣告或者附帶安裝其它流氓小游戲或者殺毒程序。
我并非不支持正版,我本人不是專業的視頻創作者,平日里對視頻轉碼編輯的需求極少,為這臨時需要而買個正常軟件,一沒必要,二不劃算。
FFmpeg 是一個強大的開源視頻工具包,通過命令行可以輕易的將一個 wmv 文件轉碼成需要的目標格式文件,例如轉碼成mp4。
Python 也是個很好用的腳本語言。為可以快速的搜索視頻文檔,然后對其進行轉碼操作,使用Python寫了一個簡單的腳本,該腳本可以借助Everything的強大搜索功能,協助用戶快速的搜索定位需要轉碼的文檔,然后對其進行轉碼。
Python 腳本
Python腳本比較簡單,邏輯也比較清晰。截圖如下:
如👆上圖所示,腳本首先定義了一個命令行入參的類,用于管理和解析命令行傳入的參數。
然后定義了一個小的函數,這個函數并不是核心功能,僅用于對比兩個指定的字符串中的差異。
在主程序中(if __name__ == '__main__'
部分),腳本首先解析了來自命令行的參數;
然后定義了一個Everything搜索接口對象(該Everything搜索接口對象提供了基于Everything的搜索功能),用于協助用戶快速的搜索定位興趣文檔。
在功能邏輯循環體中,首先引導用戶搜索定位待轉碼的視頻文檔;然后與用戶交互確定轉碼的目標格式;最后借助 moviepy這個Python包完成視頻轉碼操作。
以上,腳本邏輯清晰自然,貼出Python代碼如下👇。
# -*- coding:UTF-8 -*-
"""
@author: dyy
@contact: douyaoyuan@126.com
@time: 2024/5/10 21:40
@file: 視頻轉碼.py
@desc: xxxxxx
"""# region 引入必要的依賴
import os模塊名 = 'DebugInfo'
try:from DebugInfo.DebugInfo import *
except ImportError as impErr:print(f"嘗試導入 {模塊名} 依賴時檢測到異常:{impErr}")print(f"嘗試安裝 {模塊名} 模塊:")try:os.system(f"pip install {模塊名}")except OSError as osErr:print(f"嘗試安裝模塊 {模塊名} 時檢測到異常:{osErr}")exit(0)else:try:from DebugInfo.DebugInfo import *except ImportError as impErr:print(f"再次嘗試導入 {模塊名} 依賴時檢測到異常:{impErr}")exit(0)模塊名 = 'difflib'
try:import difflib # 需要安裝 difflib 模塊,以支持字符差異對比操作
except ImportError as impErr:print(f"嘗試導入 {模塊名} 依賴時檢測到異常:{impErr}")print(f"嘗試安裝 {模塊名} 模塊:")try:os.system(f"pip install {模塊名}")except OSError as osErr:print(f"嘗試安裝模塊 {模塊名} 時檢測到異常:{osErr}")exit(0)else:try:import difflibexcept ImportError as impErr:print(f"再次嘗試導入 {模塊名} 依賴時檢測到異常:{impErr}")exit(0)模塊名 = 'moviepy'
try:from moviepy.editor import VideoFileClip # 需要引入 moviepy 模塊,以支持視頻轉碼的操作
except ImportError as impErr:print(f"嘗試導入 {模塊名} 依賴時檢測到異常:{impErr}")print(f"嘗試安裝 {模塊名} 模塊:")try:os.system(f"pip install {模塊名}")except OSError as osErr:print(f"嘗試安裝模塊 {模塊名} 時檢測到異常:{osErr}")exit(0)else:try:from moviepy.editor import VideoFileClipexcept ImportError as impErr:print(f"再次嘗試導入 {模塊名} 依賴時檢測到異常:{impErr}")exit(0)
# endregion# 定義一個 命令行參數類,用于解析和記錄命令行參數
class 命令行參數類(入參基類):def __init__(self):super().__init__()self._添加參數('搜索關鍵字', str, '指定文檔搜索的關鍵字')self._添加參數('everything服務端口', str, 'everything HTTP 服務端口', '22')# region 訪問器@propertydef jsonCfg(self) -> str:if 'jsonCfg' in self._參數字典:return self._參數字典['jsonCfg'].值else:return ''@jsonCfg.setterdef jsonCfg(self, 值: str):if 'jsonCfg' in self._參數字典:self._參數字典['jsonCfg'].值 = str(值)@propertydef 搜索關鍵字(self) -> str:if '搜索關鍵字' in self._參數字典:return self._參數字典['搜索關鍵字'].值else:return ''@搜索關鍵字.setterdef 搜索關鍵字(self, 值: str):if '搜索關鍵字' in self._參數字典:self._參數字典['搜索關鍵字'].值 = str(值)@propertydef everything服務端口(self) -> str:if 'everything服務端口' in self._參數字典:return self._參數字典['everything服務端口'].值else:return ''@everything服務端口.setterdef everything服務端口(self, 值: str):if 'everything服務端口' in self._參數字典:self._參數字典['everything服務端口'].值 = str(值)# endregion# 標注兩個字符串的差異項
def 差異標注(字串1: str, 字串2: str) -> tuple[str, str]:字串1 = str(字串1 if 字串1 else '').strip()字串2 = str(字串2 if 字串2 else '').strip()# region 原文檔變動對齊# 使用SequenceMatcher()類計算兩個字符串的相似度匹配器 = difflib.SequenceMatcher(None, 字串1, 字串2)差異 = 匹配器.get_opcodes()# 差異標注字串1差異標注: str = ''字串2差異標注: str = ''for opcode, i1, i2, j1, j2 in 差異:if opcode == 'equal':字串1差異標注 = 字串1差異標注 + 字串1[i1:i2]字串2差異標注 = 字串2差異標注 + 字串2[j1:j2]elif opcode == 'delete':字串1差異標注 = 字串1差異標注 + 紅字(字串1[i1:i2])elif opcode == 'insert':字串2差異標注 = 字串2差異標注 + 綠字(字串2[j1:j2])elif opcode == 'replace':字串1差異標注 = 字串1差異標注 + 紅字(字串1[i1:i2])字串2差異標注 = 字串2差異標注 + 綠字(字串2[j1:j2])return 字串1差異標注, 字串2差異標注if __name__ == '__main__':畫板 = 打印模板(False)畫板.執行位置(__file__)# region 解析入參入參 = 命令行參數類()入參.解析Json(畫板=畫板.副本.縮進())入參.解析入參(畫板=畫板.副本.縮進())if 畫板.正在調試:入參.展示(畫板=畫板.副本.縮進())搜索關鍵字: str = 入參.搜索關鍵字 if 入參.搜索關鍵字 else ''# endregion# region 定義搜索接口本地搜索: 搜索接口類 = 搜索接口類()if 在nt系統中():if 入參.everything服務端口:本地搜索.everything服務端口 = int(入參.everything服務端口)else:畫板.提示錯誤('入參.everything服務端口 參數無效')exit(1)# endregion# region 開始邏輯循環while True:def 文檔排除規則(文檔: str) -> bool: # 排除一些不必要的干擾文檔if not 文檔: # 空文檔不要return Trueif '\\recent\\' in 文檔.lower(): # recent 文檔夾內的文檔不要return Trueif 文檔.lower().endswith('.lnk'): # *.lnk 文檔不要return True# region 引導用戶選擇需要轉碼的視頻文檔while True:原視頻文檔列表 = 交互接口類.指定選擇文檔(輸入提示='請輸入關鍵字以定位視頻文檔(0: 退出程序):',搜索接口=本地搜索,搜索關鍵字=搜索關鍵字,候選項上限=250,排除規則=文檔排除規則,多選=True,畫板=畫板.副本.縮進())if '0' in 原視頻文檔列表:# 用戶要求退出程序exit(0)if 原視頻文檔列表:畫板.消息('您選擇的文檔列出如下:')for 文檔 in 原視頻文檔列表:畫板.消息(綠字(文檔))break# endregion# region 目標格式信息交互目標視頻文檔列表: list[str] = []while True:目標格式后綴名: str = 交互接口類.發起文本交互(輸入提示='請輸入視頻目標格式(0: 退出程序):',畫板=畫板.副本.縮進())if '0' == 目標格式后綴名:# 用戶要求退出程序exit(0)if 目標格式后綴名.startswith('。'):目標格式后綴名 = f'.{目標格式后綴名[1:]}'if not 目標格式后綴名.startswith('.'):目標格式后綴名 = f'.{目標格式后綴名}'if not 目標格式后綴名.strip('.'):# 如果后綴名是空的,則重新要求輸入continue# 后綴名統一轉換成小寫格式目標格式后綴名 = 目標格式后綴名.lower()# 合成目標文檔名:for 文檔 in 原視頻文檔列表:目標視頻文檔列表.append(f'{文檔}{目標格式后綴名}')# 打的并提示用戶確認轉換方式畫板.消息(黃字('即將啟動的視頻轉碼如下:'))畫板.準備表格(對齊控制串='rl')for 序號 in range(len(原視頻文檔列表)):原文檔,目標文檔 = 差異標注(原視頻文檔列表[序號],目標視頻文檔列表[序號])畫板.添加一行(原文檔,洋紅字('->'),目標文檔)畫板.展示表格(列間距=[0,0])轉碼確認: str = 交互接口類.發起文本交互(輸入提示='是否確認轉碼?(y: 確認; n: 修改目標格式; 0: 退出程序)',限定范圍='YyNn0',畫板=畫板.副本.縮進())if '0' == 轉碼確認:# 用戶要求退出exit(0)elif 'y' == 轉碼確認.lower():# 用戶確認轉碼break# endregion# region 開始視頻轉碼編解碼格式: str = ''for 序號 in range(len(原視頻文檔列表)):視頻切片 = VideoFileClip(原視頻文檔列表[序號])while True:if not 編解碼格式:編解碼格式 = 交互接口類.發起文本交互(輸入提示=f'默認 codec 參數是 libx264, 輸入 {綠字("y")} 以確認; 'f'或者您可以輸入其它編碼格式({紅字("0")}: {紅字("退出程序")}):',畫板=畫板.副本.縮進())if '0' == 編解碼格式:# 用戶要求退出程序exit(0)編解碼格式 = 編解碼格式.lower()if 'y' == 編解碼格式:# 用戶確認使用 libx264 作為 codec 參數編解碼格式 = 'libx264'try:if 目標格式后綴名.endswith('gif'):視頻切片.write_gif(filename=目標視頻文檔列表[序號])elif 編解碼格式:視頻切片.write_videofile(filename=目標視頻文檔列表[序號], codec=編解碼格式)else:視頻切片.write_videofile(filename=目標視頻文檔列表[序號])except Exception as exp:畫板.提示錯誤('視頻轉碼遇到異常如下:')畫板.消息(exp.__str__())# 清除 codec 參數,重新發起交互編解碼格式 = ''continueelse:# 如果轉碼順利完成,則關閉 視頻切片,開始下一段視頻的處理視頻切片.close()break# endregion# endregion
使用演示
👉第一步,腳本首先引導用戶搜索和定位視頻文件,我們可以輸入視頻文件名信息以定位視頻文檔,然后參過序號選擇目標文檔;如下👇:
👉第二步,腳本詢問我們需要將視頻文檔轉換為什么樣的目標格式,我們輸入目標文檔的后綴名,然后再一次確認腳本的問詢;如下👇:
在這一步中,如果我們輸入了錯誤的目標格式,我們還可以輸入 n 來重新修改目標格式。
👉第三步,腳本會詢問我們是否使用默認的 codec 參數(默認為libx264)進行視頻轉碼,如果我們希望使用其它 codec 參數,例如 png,或者libvorbis 等,我們也可以直接輸入 codec 值來告訴腳本;如下👇:
以上👆腳本交互完成后,腳本即開始了視頻轉碼過程,我們耐心等待轉碼完成即可,如下👇:
懶人包
以上Python腳本的功能比較簡單,對于視頻轉碼的參數控制較少,但如果大家有需要,可以輕易的加入更多的轉碼參數控制功能。
以上 Python腳本,對應的bat調用腳本,和對應的cfg參數文檔,統一進行了打包,見:Python 視頻轉碼腳本。