avif 动图制作一键 python 脚本
:::note[mp4_to_10s_avif]
import os
import subprocess
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QLineEdit, QLabel, QFileDialog, QSpacerItem, QSizePolicy
from datetime import datetime
class VideoEditor(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.main_video_path = None
self.jdt_video_path = '/Users/macm2/Documents/vuepress/src/diary/2024/tools/jdt.mp4'
self.set_config = 1
self.trim_hour = '00'
self.trim_minute = '00'
self.trim_second = '00'
self.crf = '24'
self.output_file_name = None
# 创建GUI元素
self.btn_select_main = QPushButton('选择主视频', self)
self.lbl_main_video = QLabel("主视频未选择!", self)
self.btn_select_jdt = QPushButton('选择进度条视频', self)
self.lbl_jdt_video = QLabel("进度条视频未选择!", self)
if self.jdt_video_path:
self.lbl_jdt_video.setText(f"已选择:{self.jdt_video_path}")
self.set_config = QLabel("参数设置: ", self)
self.lbl_trim_hour = QLabel("时: ", self)
self.txt_trim_hour = QLineEdit(self)
self.txt_trim_hour.setFixedWidth(60)
self.txt_trim_hour.setPlaceholderText(f"时:{self.trim_hour}")
self.lbl_trim_minute = QLabel("分: ", self)
self.txt_trim_minute = QLineEdit(self)
self.txt_trim_minute.setFixedWidth(60)
self.txt_trim_minute.setPlaceholderText(f"分:{self.trim_minute}")
self.lbl_trim_second = QLabel("秒: ", self)
self.txt_trim_second = QLineEdit(self)
self.txt_trim_second.setFixedWidth(60)
self.txt_trim_second.setPlaceholderText(f"秒:{self.trim_second}")
self.lbl_crf = QLabel("crf:", self)
self.txt_crf = QLineEdit(self)
self.txt_crf.setPlaceholderText("默认24(0-63, 越低质量越好)")
self.lbl_output_name = QLabel("输出文件名:", self)
self.txt_output_name = QLineEdit(self)
self.txt_output_name.setPlaceholderText("输入输出文件名(例如:output)")
self.btn_process = QPushButton('处理视频', self)
# 布局设置
layout = QVBoxLayout()
layout.addWidget(self.btn_select_main)
layout.addWidget(self.lbl_main_video)
layout.addWidget(self.btn_select_jdt)
layout.addWidget(self.lbl_jdt_video)
layout.addItem(QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding))
layout.addWidget(self.set_config)
layout.addItem(QSpacerItem(20, 10, QSizePolicy.Minimum, QSizePolicy.Expanding))
time_layout = QHBoxLayout()
time_layout.addWidget(self.lbl_trim_hour)
time_layout.addWidget(self.txt_trim_hour)
time_layout.addWidget(self.lbl_trim_minute)
time_layout.addWidget(self.txt_trim_minute)
time_layout.addWidget(self.lbl_trim_second)
time_layout.addWidget(self.txt_trim_second)
layout.addLayout(time_layout)
crf_layout = QHBoxLayout()
crf_layout.addWidget(self.lbl_crf)
crf_layout.addWidget(self.txt_crf)
layout.addLayout(crf_layout)
output_name_layout = QHBoxLayout()
output_name_layout.addWidget(self.lbl_output_name)
output_name_layout.addWidget(self.txt_output_name)
layout.addLayout(output_name_layout)
layout.addWidget(self.btn_process)
self.setLayout(layout)
# 连接信号和槽函数
self.btn_select_main.clicked.connect(self.select_main_video)
self.btn_select_jdt.clicked.connect(self.select_jdt_video)
self.btn_process.clicked.connect(self.process_videos)
def select_main_video(self):
file_dialog = QFileDialog()
self.main_video_path, _ = file_dialog.getOpenFileName(self, '选择视频文件', '', 'Video Files (*.mp4 *.avi *.mov)')
if self.main_video_path:
self.lbl_main_video.setText(f"已选择:{self.main_video_path}")
def select_jdt_video(self):
file_dialog = QFileDialog()
self.jdt_video_path, _ = file_dialog.getOpenFileName(self, '选择进度条视频文件', '', 'Video Files (*.mp4 *.avi *.mov )')
if self.jdt_video_path:
self.lbl_jdt_video.setText(f"已选择:{self.jdt_video_path}")
def process_videos(self):
now = datetime.now()
formatted_time = now.strftime("%m%d_%H%M")
self.trim_hour = self.txt_trim_hour.text() or self.trim_hour
self.trim_minute = self.txt_trim_minute.text() or self.trim_minute
self.trim_second = self.txt_trim_second.text() or self.trim_second
self.crf = self.txt_crf.text() or self.crf
self.output_file_name = self.txt_output_name.text() or formatted_time
if not (self.main_video_path and self.jdt_video_path and self.trim_hour and self.trim_minute and self.trim_second and self.crf and self.output_file_name):
print("请确保所有字段都已填写")
return
output_dir = os.path.dirname(self.main_video_path)
main_video_trimmed = os.path.join(output_dir, "main_video.mp4")
# 构建时间字符串
trim_time = f"{self.trim_hour}:{self.trim_minute}:{self.trim_second}"
# 获取视频分辨率
probe_cmd = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "csv=p=0", self. main_video_path]
probe_result = subprocess.check_output(probe_cmd).decode('utf-8').strip(). split(',')
video_width, video_height = map(int, probe_result)
# 根据视频分辨率调整裁剪命令
if video_width != 1920 or video_height != 1080:
trim_cmd = [
"ffmpeg",
"-ss", trim_time,
"-i", self.main_video_path,
"-t", "10",
"-vf", "scale=1920:1080",
"-c:v", "libx264",
"-preset", "fast",
"-crf", "12",
"-c:a", "copy",
main_video_trimmed
]
else:
trim_cmd = [
"ffmpeg",
"-ss", trim_time,
"-i", self.main_video_path,
"-t", "10",
"-c", "copy",
main_video_trimmed
]
subprocess.run(trim_cmd)
# 合并两个视频
merge_cmd = [
"ffmpeg",
"-i", main_video_trimmed,
"-i", self.jdt_video_path,
"-filter_complex",
"[1:v] crop=in_w:10:0:1080-10 [jdt];"
"[jdt] setpts=PTS-STARTPTS [jdt_sync];"
"[0:v][jdt_sync] overlay=(W-w)/2:H-h [outv]",
"-map", "[outv]",
"-map", "0:a",
"-c:v", "libsvtav1",
"-crf", self.crf,
"-pix_fmt", "yuv420p",
"-an",
"-f", "avif",
os.path.join(output_dir, f"{self.output_file_name}.avif")
]
subprocess.run(merge_cmd)
# 删除临时文件
try:
os.remove(main_video_trimmed)
except OSError as e:
print(f"删除文件时出现错误: {e}")
print("视频处理完成")
if __name__ == '__main__':
app = QApplication([])
editor = VideoEditor()
editor.show()
app.exec_()
:::
:::note[avif封装转换及torrent制作]
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QFileDialog, QMessageBox, QVBoxLayout
import subprocess
import os
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'MP4 to AVIF Converter & Torrent Creator'
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
layout = QVBoxLayout()
# MP4 to AVIF Converter
self.converter_button = QPushButton('选择avi编码的.mp4文件', self)
self.converter_button.clicked.connect(self.convert_to_avif)
layout.addWidget(self.converter_button)
# Transmission Torrent Creator
self.torrent_button = QPushButton('选择文件或目录生成.torrent', self)
self.torrent_button.clicked.connect(self.create_torrent)
layout.addWidget(self.torrent_button)
self.setLayout(layout)
self.setGeometry(100, 100, 400, 150)
self.show()
def convert_to_avif(self):
options = QFileDialog.Options()
options |= QFileDialog.ReadOnly
input_files, _ = QFileDialog.getOpenFileNames(self, "选择多个文件", "", "MP4 Files (*.mp4);;All Files (*)", options=options)
if input_files:
success_files = []
failed_files = []
skipped_files = []
for input_file in input_files:
input_path, input_filename = os.path.split(input_file)
output_filename = os.path.splitext(input_filename)[0] + '.avif'
output_file = os.path.join(input_path, output_filename)
# 检查是否已经存在目标文件
if os.path.exists(output_file):
skipped_files.append(output_file)
continue
# 调用ffmpeg命令
command = ["ffmpeg", "-i", input_file, "-c", "copy", "-f", "avif", output_file]
try:
subprocess.run(command, check=True)
success_files.append(output_file)
except subprocess.CalledProcessError as e:
failed_files.append((input_filename, str(e)))
# 汇总报告
report = "转换完成。\n\n"
if success_files:
report += "成功文件:\n" + "\n".join(success_files) + "\n\n"
if failed_files:
report += "失败文件:\n" + "\n".join([f"{file}: {error}" for file, error in failed_files]) + "\n\n"
if skipped_files:
report += "跳过文件(已存在):\n" + "\n".join(skipped_files) + "\n\n"
QMessageBox.information(self, "汇总报告", report)
def create_torrent(self):
options = QFileDialog.Options()
options |= QFileDialog.ReadOnly
input_items, _ = QFileDialog.getOpenFileNames(self, "选择文件或目录", "", "All Files (*)", options=options)
if input_items:
success_items = []
failed_items = []
comment = "sanqiz"
for input_item in input_items:
input_path, input_name = os.path.split(input_item)
output_file = os.path.join(input_path, input_name + '.torrent')
# 调用transmission-create命令
command = ["transmission-create", "-o", output_file, "-c", comment, "-t", "www.baidu.com", input_item]
try:
subprocess.run(command, check=True)
success_items.append(output_file)
except subprocess.CalledProcessError as e:
failed_items.append((input_name, str(e)))
# 汇总报告
report = "Torrent 文件创建完成。\n\n"
if success_items:
report += "成功创建:\n" + "\n".join(success_items) + "\n\n"
if failed_items:
report += "失败创建:\n" + "\n".join([f"{item}: {error}" for item, error in failed_items]) + "\n\n"
QMessageBox.information(self, "汇总报告", report)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
:::