Python 批量横屏转竖屏视频处理工具

Python 批量横屏转竖屏视频处理工具

相关资源文件已经打包成EXE文件,可双击直接运行程序,且文章末尾已附上相关源码,以供大家学习交流,博主主页还有更多Python相关程序案例,秉着开源精神的想法,望大家喜欢,点个关注不迷路!!!

1. 简介:

这是一款基于Python和Tkinter框架开发的视频处理器应用。该应用集成了FFmpeg,用于批量横屏转竖屏视频处理,支持多种视频格式和编码选择,并提供多线程支持以提升处理效率。用户可以通过简洁直观的图形界面导入、删除视频文件,并且对每个文件设置处理参数,如输出格式、视频编码器、音频编码器及高斯模糊效果。应用还支持暂停/继续和多线程并发处理,确保在长时间处理时能保持灵活性和高效性。

2.功能说明:

在这里插入图片描述
在这里插入图片描述

2.1 文件管理:

  1. 支持通过拖放或文件选择对话框导入视频文件。
  2. 支持删除已导入的文件。
  3. 显示导入文件的基本信息,包括文件名和处理状态。

2.2 FFmpeg配置:

  1. 用户可以选择视频输出格式(MP4、AVI、MOV等)。
  2. 提供不同的视频和音频编码器选项(如libx264、aac)。
  3. 支持设置处理线程数,允许用户根据系统性能调整并发处理数量。
  4. 可选应用高斯模糊效果以实现视频特效。

2.3 多线程处理:

  1. 使用ThreadPoolExecutor实现多线程并发处理多个视频文件。
  2. 支持暂停和继续处理,用户可以在处理过程中暂停视频文件的转换,稍后继续。

2.4 输出文件管理:

  1. 用户可以设置输出文件夹,处理完成的视频会保存至该目录。
  2. 支持在处理完成后直接打开输出文件夹。

2.5 进度监控与日志:

  1. 在文件列表中显示每个视频的处理进度。
  2. 提供日志框,实时显示处理过程中的信息。

2.6 拖放支持:

  • 支持通过拖拽文件到窗口的方式导入视频文件,提升用户体验。

2.7 错误处理与反馈:

  • 针对文件已存在、格式不支持等情况提供相应的错误提示。

3. 运行效果:

在这里插入图片描述
在这里插入图片描述

视频处理转换前(横屏状态):
在这里插入图片描述

视频处理转换后(竖屏状态):
在这里插入图片描述

4. 总结:

该视频处理器应用通过Python与Tkinter提供了一个强大而简洁的图形界面,允许用户批量处理视频文件。借助FFmpeg,用户不仅可以自由选择输出格式和编码器,还可以根据需求应用视频特效,如高斯模糊。多线程的支持使得处理多个视频文件更加高效,确保了在处理过程中能够灵活控制暂停、继续和取消操作。通过增强的文件管理和进度监控,用户能够轻松掌控整个视频处理过程。此工具对于需要批量转换视频格式或应用特效的用户非常实用,尤其适合需要高效处理大量视频文件的场景。

5. 相关源码:

import os
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from tkinter import filedialog, messagebox, END, Text, StringVar, IntVar, BooleanVar, Menu
from concurrent.futures import ThreadPoolExecutor, as_completed
import subprocess
import threading
import psutil
import re
import sysfrom tkinterdnd2 import TkinterDnD, DND_FILESdef resource_path(relative_path):""" Get absolute path to resource, works for dev and for PyInstaller """try:# PyInstaller creates a temp folder and stores path in _MEIPASSbase_path = sys._MEIPASSexcept Exception:base_path = os.path.abspath(".")return os.path.join(base_path, relative_path)class VideoProcessor:def __init__(self, master):self.master = masterself.master.title("视频处理器  吾爱作者:是谁的大海(是貔貅呀) 版本:1.3")self.input_files = []self.output_folder = ""self.process_thread = Noneself.pause_event = threading.Event()self.pause_event.set()  # Start in the unpaused stateself.ffmpeg_processes = []  # List to keep track of all ffmpeg processesself.is_closing = Falseself.output_format = StringVar(value="mp4")self.video_codec = StringVar(value="libx264")self.audio_codec = StringVar(value="aac")self.thread_count = IntVar(value=1)  # Default to 1 threadsself.apply_blur = BooleanVar(value=False)  # Boolean to check if blur should be appliedself.create_widgets()self.create_menu()self.master.protocol("WM_DELETE_WINDOW", self.on_closing)def create_widgets(self):frame = ttk.Frame(self.master, padding=10)frame.pack(fill=BOTH, expand=YES)self.file_list_frame = ttk.Frame(frame)self.file_list_frame.pack(fill=BOTH, expand=YES, pady=5)columns = ('序号', '文件夹名字', '进度')self.file_tree = ttk.Treeview(self.file_list_frame, columns=columns, show='headings')self.file_tree.heading('序号', text='序号')self.file_tree.heading('文件夹名字', text='文件夹名字')self.file_tree.heading('进度', text='进度')self.file_tree.column('序号', width=100, anchor='center')self.file_tree.column('文件夹名字', width=400, anchor='w')self.file_tree.column('进度', width=100, anchor='center')self.file_tree.pack(side=LEFT, fill=BOTH, expand=YES)scrollbar = ttk.Scrollbar(self.file_list_frame, orient="vertical", command=self.file_tree.yview)self.file_tree.configure(yscrollcommand=scrollbar.set)scrollbar.pack(side=RIGHT, fill=Y)self.log_text = Text(frame, height=5, state='disabled')self.log_text.pack(fill=BOTH, expand=YES, pady=5)button_frame = ttk.Frame(frame)button_frame.pack(fill=BOTH, expand=YES)self.add_files_button = ttk.Button(button_frame, text="导入文件", command=self.add_files, bootstyle=PRIMARY)self.add_files_button.pack(side=LEFT, padx=5, pady=5)self.remove_files_button = ttk.Button(button_frame, text="删除文件", command=self.remove_files, bootstyle=DANGER)self.remove_files_button.pack(side=LEFT, padx=5, pady=5)self.pause_button = ttk.Button(button_frame, text="暂停/继续", command=self.toggle_pause, bootstyle=WARNING)self.pause_button.pack(side=LEFT, padx=5, pady=5)self.open_output_folder_button = ttk.Button(button_frame, text="打开文件", command=self.open_output_folder, bootstyle=SUCCESS)self.open_output_folder_button.pack(side=LEFT, padx=5, pady=5)self.set_output_folder_button = ttk.Button(button_frame, text="导出文件", command=self.set_output_folder, bootstyle=SUCCESS)self.set_output_folder_button.pack(side=LEFT, padx=5, pady=5)self.process_button = ttk.Button(button_frame, text="开始处理文件", command=self.start_processing, bootstyle=INFO)self.process_button.pack(side=RIGHT, padx=5, pady=5)config_frame = ttk.LabelFrame(frame, text="FFmpeg 配置")config_frame.pack(fill=BOTH, expand=YES, pady=5)ttk.Label(config_frame, text="输出格式:").pack(side=LEFT, padx=5, pady=5)ttk.OptionMenu(config_frame, self.output_format, "mp4", "mp4", "avi", "mov").pack(side=LEFT, padx=5, pady=5)ttk.Label(config_frame, text="视频编码器:").pack(side=LEFT, padx=5, pady=5)ttk.OptionMenu(config_frame, self.video_codec, "libx264", "libx264", "libx265", "mpeg4").pack(side=LEFT, padx=5, pady=5)ttk.Label(config_frame, text="音频编码器:").pack(side=LEFT, padx=5, pady=5)ttk.OptionMenu(config_frame, self.audio_codec, "aac", "aac", "mp3", "ac3").pack(side=LEFT, padx=5, pady=5)ttk.Label(config_frame, text="线程数:").pack(side=LEFT, padx=5, pady=5)ttk.Entry(config_frame, textvariable=self.thread_count).pack(side=LEFT, padx=5, pady=5)self.blur_checkbox = ttk.Checkbutton(config_frame, text="应用高斯模糊效果", variable=self.apply_blur)self.blur_checkbox.pack(side=LEFT, padx=5, pady=5)# Set up drag and dropself.master.drop_target_register(DND_FILES)self.master.dnd_bind('<<Drop>>', self.drop_files)def create_menu(self):menu_bar = Menu(self.master)self.master.config(menu=menu_bar)help_menu = Menu(menu_bar, tearoff=0)menu_bar.add_cascade(label="帮助", menu=help_menu)help_menu.add_command(label="使用说明", command=self.show_usage_instructions)help_menu.add_command(label="软件具体说明", command=self.show_software_details)def show_usage_instructions(self):instructions = ("使用说明:\n""1. 导入文件:点击“导入文件”按钮,选择需要处理的视频文件,或将视频文件拖拽到软件窗口中。\n""2. 设置输出文件夹:点击“导出文件”按钮,选择一个文件夹作为输出文件夹。\n""3. 配置FFmpeg参数:在“FFmpeg 配置”区域,选择输出格式、视频编码器、音频编码器、线程数,并可选择是否应用高斯模糊效果。\n""4. 开始处理:点击“开始处理文件”按钮,开始批量处理视频文件。处理过程中可以查看处理进度和日志信息。\n""5. 查看输出文件:点击“打开文件”按钮,打开输出文件夹查看处理完成的视频文件。\n""6. 删除文件:选择文件列表中的文件,点击“删除文件”按钮删除不需要处理的文件。\n")messagebox.showinfo("使用说明", instructions)def show_software_details(self):details = ("仅供学习,切勿使用到其他用途\n""1. 输出格式:支持MP4、AVI和MOV等常见格式,用户可自定义选择。\n""2. 视频压缩:默认使用libx264视频编码器和aac音频编码器,支持高效视频压缩,用户可自定义选择其他编码器。\n""3. 视频裁剪:适用于将1920x1080横屏视频裁剪成9:16竖屏视频,不会变形。\n""4. 高斯模糊:可选应用高斯模糊效果,适用于特殊视频效果需求。\n""5. 多线程处理:支持多线程并发处理,用户可自定义线程数,提高处理效率。\n")messagebox.showinfo("软件具体说明", details)def drop_files(self, event):files = self.master.tk.splitlist(event.data)for file in files:if file not in self.input_files and file.lower().endswith(('.mp4', '.avi', '.mov')):self.input_files.append(file)self.file_tree.insert('', END, values=(len(self.input_files), os.path.basename(file), "未处理"))self.log(f"导入文件: {file}")else:messagebox.showwarning("警告", f"文件已存在或不支持的文件类型: {os.path.basename(file)}")def add_files(self):files = filedialog.askopenfilenames(title="选择视频文件", filetypes=[("视频文件", "*.mp4 *.avi *.mov")])for file in files:if file not in self.input_files:self.input_files.append(file)self.file_tree.insert('', END, values=(len(self.input_files), os.path.basename(file), "未处理"))self.log(f"导入文件: {file}")else:messagebox.showwarning("警告", f"文件已存在: {os.path.basename(file)}")def remove_files(self):selected_items = self.file_tree.selection()indices_to_remove = []for item in selected_items:values = self.file_tree.item(item, 'values')if values:index = int(values[0]) - 1indices_to_remove.append(index)self.file_tree.delete(item)# 删除索引列表中的元素(倒序删除避免索引问题)for index in sorted(indices_to_remove, reverse=True):del self.input_files[index]self.log("删除选中文件")self.refresh_file_list()def refresh_file_list(self):for item in self.file_tree.get_children():self.file_tree.delete(item)for index, file in enumerate(self.input_files):self.file_tree.insert('', END, values=(index + 1, os.path.basename(file), "未处理"))def set_output_folder(self):self.output_folder = filedialog.askdirectory(title="选择输出文件夹")self.log(f"设置输出文件夹: {self.output_folder}")def start_processing(self):if not self.input_files or not self.output_folder:messagebox.showerror("错误", "请添加文件并设置输出文件夹。")returnself.process_thread = threading.Thread(target=self.process_videos_concurrently)self.process_thread.start()def toggle_pause(self):if self.pause_event.is_set():self.pause_event.clear()self.log("处理暂停")for process in self.ffmpeg_processes:proc = psutil.Process(process.pid)proc.suspend()else:self.pause_event.set()self.log("处理继续")for process in self.ffmpeg_processes:proc = psutil.Process(process.pid)proc.resume()def open_output_folder(self):if self.output_folder:os.startfile(self.output_folder)self.log(f"打开输出文件夹: {self.output_folder}")else:messagebox.showerror("错误", "请先设置输出文件夹。")def log(self, message):if not self.is_closing:self.master.after(0, self._log, message)def _log(self, message):if not self.is_closing:self.log_text.configure(state='normal')self.log_text.insert(END, message + '\n')self.log_text.configure(state='disabled')self.log_text.yview(END)def update_tree_status(self, index, status):if not self.is_closing:self.master.after(0, self._update_tree_status, index, status)def _update_tree_status(self, index, status):if not self.is_closing:self.file_tree.item(self.file_tree.get_children()[index], values=(index + 1, os.path.basename(self.input_files[index]), status))def process_videos_concurrently(self):max_workers = self.thread_count.get()with ThreadPoolExecutor(max_workers=max_workers) as executor:futures = [executor.submit(self.process_video, index, input_file) for index, input_file in enumerate(self.input_files)]for future in as_completed(futures):future.result()def process_video(self, index, input_file):ffmpeg_path = resource_path(os.path.join("ffmpeg_folder", "ffmpeg"))filename = os.path.basename(input_file)output_file = os.path.join(self.output_folder, f"processed_{filename}.{self.output_format.get()}")if os.path.exists(output_file):overwrite = messagebox.askyesno("文件已存在", f"{output_file} 已存在,是否覆盖?")if not overwrite:self.update_tree_status(index, "跳过")returnif self.apply_blur.get():cmd = [ffmpeg_path,"-y",  # 自动覆盖输出文件"-i", input_file,"-vf", "split[a][b];[a]scale=1080:1920,boxblur=10:5[1];[b]scale=1080:ih*1080/iw[2];[1][2]overlay=0:(H-h)/2","-c:v", self.video_codec.get(),"-crf", "18","-preset", "veryfast","-aspect", "9:16","-c:a", self.audio_codec.get(),output_file]else:cmd = [ffmpeg_path,"-y",  # 自动覆盖输出文件"-i", input_file,"-vf", "scale='if(gt(iw/ih,9/16),1080,-2)':'if(gt(iw/ih,9/16),-2,1920)',pad=1080:1920:(1080-iw)/2:(1920-ih)/2","-c:v", self.video_codec.get(),"-crf", "18","-preset", "veryfast","-c:a", self.audio_codec.get(),output_file]self.log(f"开始处理: {filename}")self.update_tree_status(index, "处理中")try:startupinfo = subprocess.STARTUPINFO()startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOWprocess = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True, encoding='utf-8', startupinfo=startupinfo)self.ffmpeg_processes.append(process)for line in process.stderr:if self.is_closing:breakprogress = self.parse_progress(line)if progress:self.update_tree_status(index, progress)process.wait()except Exception as e:self.log(f"处理文件时出错: {filename} - {str(e)}")self.update_tree_status(index, "处理失败")returnif self.is_closing:self.update_tree_status(index, "未完成")else:self.log(f"完成处理: {filename}")self.update_tree_status(index, "已完成")self.ffmpeg_processes.remove(process)def parse_progress(self, line):match = re.search(r'time=(\d+:\d+:\d+\.\d+)', line)if match:return f"进度: {match.group(1)}"return Nonedef on_closing(self):self.is_closing = Truefor process in self.ffmpeg_processes:proc = psutil.Process(process.pid)proc.terminate()self.master.destroy()if __name__ == "__main__":root = TkinterDnD.Tk()root.title("视频处理器")root.geometry("870x520")  # Set the window size to 870x520root.resizable(False, False)  # Make the window non-resizableapp = VideoProcessor(master=root)root.mainloop()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24330.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ESP32S3:解决RWDT无法触发中断问题,二次开发者怎么才能使用内部RTC看门狗中断RWDT呢?

目录 基于ESP32S3:解决RWDT无法触发中断问题引言解决方案1. 查看报错日志2. 分析报错及一步一步找到解决方法3.小结我的源码基于ESP32S3:解决RWDT无法触发中断问题 引言 在嵌入式系统中,RWDT(看门狗定时器)是确保系统稳定性的重要组件。然而,在某些情况下,RWDT可能无法…

在windows下安装windows+Ubuntu16.04双系统(上)

这篇文章的内容主要来源于这篇文章&#xff0c;给文章很详细的介绍了如何从windows下安装windowsubuntu16.04双系统。我刚开始装双系统都是参照这个方法&#xff0c;该作者前后更新了两个版本&#xff0c;在这里对其稍微进行整理一下。 一、准备&#xff1a;&#xff08;这里推…

计算机单位之详解——存储单位Byte 网络传输单位bps 视频码率单位bps

前言&#xff1a; 计算机里面单位有点复杂&#xff0c;容易混淆&#xff0c;很多时候混起来就容易概念不理解&#xff0c;包括一些小问题&#xff0c;比如说&#xff1a;为什么我买了1T硬盘&#xff0c;实际存在虚标。为什么所谓的千兆宽带&#xff0c;下载起来没有1G每秒&…

博客系统完整开发流程

前言 通过前⾯课程的学习, 我们掌握了Spring框架和MyBatis的基本使用, 并完成了图书管理系统的常规功能开发, 接下来我们系统的从0到1完成⼀个项⽬的开发. 企业开发的流程 1. 需求评审(产品经理(PM)会和运营(想口号),UI,测试,开发等沟通) ,会涉及到背景/目标/怎么做,可能会有多…

MFC笔记:本专栏课件

专栏导航 上一篇&#xff1a;在VS2019里面&#xff0c;调整代码字体大小 回到目录 下一篇&#xff1a;无 本节前言 在之前的讲解里面&#xff0c;我讲解了 Visual Studio 软件的一些个基础操作步骤。从本节开始&#xff0c;我们进入预备章。 本节内容&#xff0c;属于是 …

Flutter - 基础Widget

Flutter 中万物皆 Widget&#xff0c;基础Widget 同步对应 Android View. 普通文本 Text /*** 控制文本样式统一使用 style:TextStyle, 例&#xff1a;fontSize(字体大小),color(颜色),shadows(阴影)等等* 控制文本布局需单独设置&#xff1a;* textAlign(文不对齐方式)* te…

蓝桥杯之日期题

文章目录 1.蓝桥杯必备知识点2. 题型13.需求2 1.蓝桥杯必备知识点 蓝桥杯是一个面向全国高校计算机相关专业学生的学科竞赛&#xff0c;涵盖多个赛道&#xff0c;常见的有软件类&#xff08;如 C/C 程序设计、Java 软件开发、Python 程序设计&#xff09;和电子类&#xff08;…

本地部署大模型: LM Studio、Open WebUI 与 Chatbox 全面对比以及选型指南

1. 工具概述 LM Studio 定位&#xff1a;专注于本地化大模型实验与推理的桌面工具&#xff0c;支持多模型并行、Hugging Face集成及离线运行。 核心功能&#xff1a; 图形化界面直接加载GGUF模型文件&#xff0c;支持NVIDIA/AMD GPU加速。 内置OpenAI兼容API&#xff0c;可搭…

springboot实现多文件上传

springboot实现多文件上传 代码 package com.sh.system.controller;import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMap…

最新版IDEA下载安装教程

一、下载IDEA 点击前往官网下载 或者去网盘下载 点击前往百度网盘下载 点击前往夸克网盘下载 进去后点击IDEA 然后点击Download 选择自己电脑对应的系统 点击下载 等待下载即可 二、安装IDEA 下载好后双击应用程序 点击下一步 选择好安装目录后点击下一步 勾选这两项后点击…

vue3学习2

ts定义接口&#xff1a; 引入的时候要加type&#xff1a; 调用&#xff1a; ts创建自定义type类型&#xff0c;引入的时候也要加type&#xff1a; reactive可以直接传泛型&#xff1a; 加?声明不强制&#xff1a; defineProps接收父组件传递的props&#xff0c;其中defineProp…

Proof Beyond Boundaries: Hong Kong zkNight 活动精彩回顾

2 月 19 日&#xff0c;随着夜幕的降临&#xff0c;一场汇聚行业智慧与前瞻视野的高端主题活动 ——Proof Beyond Boundaries: Hong Kong zkNight&#xff0c;在香港铜锣湾 Vpoint 的 6/F 盛大启幕。本次活动由 ZEROBASE 主办&#xff0c;Techub News 承办&#xff0c;吸引了众…

PDF转HTML 超级好用 免费在线转换PDF 完美转换格式

PDF转HTML 超级好用 免费在线转换PDF 完美转换格式&#xff0c;PDF已成为一种广泛使用的文件格式&#xff0c;用于保存和分享文档。然而&#xff0c;PDF文件在某些场景下可能不够灵活&#xff0c;特别是在需要在网页上直接展示其内容时。为了满足这一需求&#xff0c;小白工具推…

星环科技推出DeepSeek全场景解决方案:即开即用、企业级部署、端侧智能三位一体

星环科技&#xff08;688031.SH&#xff09;正式发布DeepSeek全场景解决方案&#xff0c;全面覆盖个人用户、企业客户及行业场景需求&#xff0c;为用户提供从个人到企业、从云端到本地的全方位AI应用支持&#xff0c;为不同需求的用户提供了灵活、高效且安全的AI解决方案。 省…

全价值链数字化转型:以美的集团为例,探索开源AI大模型与S2B2C商城小程序源码的融合应用

摘要&#xff1a;在数字经济时代背景下&#xff0c;企业面临着前所未有的竞争压力与市场变革。全价值链的数字化转型&#xff0c;作为提升企业核心竞争力的关键策略&#xff0c;正逐步成为行业共识。美的集团&#xff0c;作为家电行业的领军企业&#xff0c;其基于数字化的全价…

Hi3516CV610开发板ISP调试之——图像ISP在线调试 环境搭建教程

本文讲解Hi3516CV610开发板如何实时在线调试图像ISP参数 首先烧录好资料包中的出厂固件&#xff08;默认出厂已烧录好&#xff09;&#xff0c;接好网线、usb转串口线、电源&#xff0c;进入开发板系统 打开odm查看实时视频 解压打开资料包中的PQTools_V1.x.xx.zip并找到PQTool…

isaac gym使用记录

一、使用测试 这里跑的是isaac gym官方的强化学习环境代码isaacgymenvs 下载链接&#xff1a;https://zhuanlan.zhihu.com/p/671309384 1、 运行命令和效果 训练命令 python train.py taskCartpole #headlessTrue运行倒立摆任务&#xff0c;运行一会就可以收敛。headless设置…

国科大——数据挖掘(0812课程)——课后作业

前沿&#xff1a; 此文章记录了2024年度秋季学期数据挖掘课程的三次课后作业&#xff0c;答案仅供参考。 第一次作业 1 假定数据仓库中包含4个维&#xff1a;date, product, vendor, location&#xff1b;和两个度量&#xff1a;sales_volume和sales_cost。 1&#xff09;画…

基于SpringBoot的“古城景区管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“古城景区管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统整体功能图 系统首页界面 系统注册界面 景…

HarmonyOS Design 介绍

HarmonyOS Design 介绍 文章目录 HarmonyOS Design 介绍一、HarmonyOS Design 是什么&#xff1f;1. 设计系统&#xff08;Design System&#xff09;2. UI 框架的支持3. 设计工具和资源4. 开发指南5. 与其他设计系统的对比总结 二、HarmonyOS Design 特点 | 应用场景1. Harmon…