commit ed4f923c3b177961d4a8f050fcb13ebf4332874b Author: wood chen Date: Mon Nov 18 10:00:13 2024 +0800 first commit diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2775ad7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f02e33b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,160 @@ +name: Build and Release + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write # 添加这个权限声明 + +jobs: + create-release: + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v3 + + - name: Generate version number + id: version + run: | + SHA_SHORT=$(git rev-parse --short HEAD) + echo "version=1.0.0-${SHA_SHORT}" >> $GITHUB_OUTPUT + + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.version.outputs.version }} + name: Release v${{ steps.version.outputs.version }} + draft: false + prerelease: false + token: ${{ secrets.GITHUB_TOKEN }} + generate_release_notes: true + + build-windows: + needs: create-release + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Download FFmpeg + run: | + mkdir ffmpeg + curl -L https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip -o ffmpeg.zip + 7z x ffmpeg.zip -offmpeg + move ffmpeg\ffmpeg-master-latest-win64-gpl\bin\ffmpeg.exe ffmpeg\ + move ffmpeg\ffmpeg-master-latest-win64-gpl\bin\ffprobe.exe ffmpeg\ + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + - name: Build with PyInstaller + shell: cmd + run: | + pyinstaller --name video2webp --onefile --windowed --hidden-import ffmpeg --add-data "ffmpeg/ffmpeg.exe;ffmpeg" --add-data "ffmpeg/ffprobe.exe;ffmpeg" --add-data "README.md;." --clean --noconfirm --log-level=INFO gui.py + + + - name: Upload Release Asset + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ needs.create-release.outputs.version }} + files: ./dist/video2webp.exe + + build-macos: + needs: create-release + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Download FFmpeg + run: | + mkdir ffmpeg + curl -L https://github.com/eugeneware/ffmpeg-static/releases/download/b4.4/darwin-x64 -o ffmpeg/ffmpeg + curl -L https://github.com/eugeneware/ffmpeg-static/releases/download/b4.4/darwin-x64 -o ffmpeg/ffprobe + chmod +x ffmpeg/ffmpeg ffmpeg/ffprobe + + # 或者使用 Homebrew(备选方案) + - name: Install FFmpeg using Homebrew + if: failure() + run: | + brew install ffmpeg + mkdir -p ffmpeg + cp $(which ffmpeg) ffmpeg/ + cp $(which ffprobe) ffmpeg/ + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + # 创建启动器脚本 + - name: Create launcher script + run: | + echo 'import os, sys; os.environ["PATH"] = os.path.join(os.path.dirname(sys.executable), "ffmpeg") + os.pathsep + os.environ["PATH"]' > launcher.py + cat gui.py >> launcher.py + + - name: Build with PyInstaller + run: | + pyinstaller --name video2webp --onefile --windowed --add-data "ffmpeg/ffmpeg:ffmpeg" --add-data "ffmpeg/ffprobe:ffmpeg" --add-data "README.md:." launcher.py + + - name: Create DMG + run: | + cd dist + mkdir -p video2webp.app/Contents/MacOS + mkdir -p video2webp.app/Contents/Resources/ffmpeg + mv video2webp video2webp.app/Contents/MacOS/ + cp ../ffmpeg/* video2webp.app/Contents/Resources/ffmpeg/ + + # 创建 Info.plist + cat > video2webp.app/Contents/Info.plist << EOL + + + + + CFBundleExecutable + video2webp + CFBundleIdentifier + com.example.video2webp + CFBundleName + Video2Webp + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + LSMinimumSystemVersion + 10.10 + NSHighResolutionCapable + + + + EOL + + # 创建 DMG + hdiutil create -volname "Video2Webp" -srcfolder video2webp.app -ov -format UDZO video2webp.dmg + + - name: Upload Release Asset + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ needs.create-release.outputs.version }} + files: ./dist/video2webp.dmg diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ed535d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/gui.cpython-311.pyc diff --git a/README.md b/README.md new file mode 100644 index 0000000..4cd3ea4 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# 视频转WEBP工具 + +这是一个使用Python编写的视频转WEBP工具,它提供了一个图形用户界面(GUI),方便用户将视频文件转换为WEBP动画。 + +## 功能 + +- 支持拖放视频文件到应用程序窗口进行转换。 +- 内置FFmpeg工具进行视频转码, 不需要在系统安装FFmpeg。 +- 多平台, 多线程。 + +## 安装 + +### Windows + +1. 下载最新的 `video2webp-windows-${version}.exe` 安装包。 +2. 双击安装包,按照提示完成安装。 + +### macOS + +1. 下载最新的 `video2webp-macos-${version}.dmg` 安装包。 +2. 双击安装包,将 `Video2Webp` 应用程序拖放到 `Applications` 文件夹中。 + + +## 使用方法 + +1. 启动应用程序。 +2. 将视频文件拖放到应用程序窗口中。 +3. 点击“转换”按钮开始转换。 +4. 转换完成后,WEBP文件将保存在与视频文件相同的目录下。 + +## 开发 + +### 环境搭建 + +1. 安装Python 3.10及以上版本。 +2. 安装依赖库: + ```bash + pip install -r requirements.txt + ``` +3. 运行应用程序: + ```bash + python gui.py + ``` diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..8553397 --- /dev/null +++ b/gui.py @@ -0,0 +1,476 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import subprocess +import sys +import platform +import os +import webbrowser +from threading import Thread +import traceback + +# 设置 FFmpeg 路径 +if getattr(sys, "frozen", False): + # 运行在 PyInstaller 打包后的环境 + ffmpeg_path = os.path.join(sys._MEIPASS, "ffmpeg") +else: + # 运行在开发环境 + ffmpeg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ffmpeg") + +# 添加到系统 PATH +if ffmpeg_path not in os.environ["PATH"]: + os.environ["PATH"] = ffmpeg_path + os.pathsep + os.environ["PATH"] + + +def get_subprocess_config(): + """获取subprocess配置,用于隐藏控制台窗口""" + if platform.system().lower() == "windows": + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + return { + "startupinfo": startupinfo, + "creationflags": subprocess.CREATE_NO_WINDOW, + } + return {} + + +class VideoToWebpConverter: + def __init__(self): + # 创建主窗口 + self.root = tk.Tk() + self.root.title("视频或gif转WEBP工具") + + # 设置窗口大小和位置 + window_width = 800 + window_height = 600 + screen_width = self.root.winfo_screenwidth() + screen_height = self.root.winfo_screenheight() + x = (screen_width - window_width) // 2 + y = (screen_height - window_height) // 2 + self.root.geometry(f"{window_width}x{window_height}+{x}+{y}") + # 设置窗口样式 + self.root.configure(bg="#f0f0f0") + style = ttk.Style() + style.configure("TButton", padding=6) + style.configure("TLabelframe", background="#f0f0f0") + + # 设置UI + self.setup_ui() + + def setup_ui(self): + # 显示支持的格式 + supported_formats_label = ttk.Label( + self.root, text="支持的格式为\"*.mp4 *.avi *.mov *.mkv *.webm *.gif'" + ) + supported_formats_label.pack(pady=5) + # 文件选择框 + self.file_frame = ttk.LabelFrame(self.root, text="选择文件") + self.file_frame.pack(padx=10, pady=5, fill="x") + + self.files_list = tk.Listbox(self.file_frame, height=5) + self.files_list.pack(padx=5, pady=5, fill="x") + + # 为文件列表添加右键菜单 + self.files_list_menu = tk.Menu(self.root, tearoff=0) + self.files_list_menu.add_command(label="删除选中", command=self.delete_selected) + self.files_list_menu.add_command( + label="清空列表", command=lambda: self.files_list.delete(0, tk.END) + ) + + self.files_list.bind("", self.show_context_menu) + + btn_frame = ttk.Frame(self.file_frame) + btn_frame.pack(fill="x", padx=5, pady=5) + + self.select_btn = ttk.Button( + btn_frame, text="选择视频", command=self.select_files + ) + self.select_btn.pack(side="left", padx=5) + + self.clear_btn = ttk.Button( + btn_frame, + text="清空列表", + command=lambda: self.files_list.delete(0, tk.END), + ) + self.clear_btn.pack(side="left", padx=5) + + # 转换设置框 + self.settings_frame = ttk.LabelFrame(self.root, text="转换设置") + self.settings_frame.pack(padx=10, pady=5, fill="x") + + # 尺寸设置 + size_frame = ttk.Frame(self.settings_frame) + size_frame.pack(fill="x", padx=5, pady=5) + + ttk.Label(size_frame, text="尺寸设置:").pack(side="left", padx=5) + self.size_var = tk.StringVar(value="original") + ttk.Radiobutton( + size_frame, text="保持原始尺寸", variable=self.size_var, value="original" + ).pack(side="left", padx=5) + ttk.Radiobutton( + size_frame, text="自定义尺寸", variable=self.size_var, value="custom" + ).pack(side="left", padx=5) + + # 自定义尺寸输入框 + custom_size_frame = ttk.Frame(self.settings_frame) + custom_size_frame.pack(fill="x", padx=5, pady=5) + + ttk.Label(custom_size_frame, text="宽度:").pack(side="left", padx=5) + self.width_var = tk.StringVar(value="480") + self.width_entry = ttk.Entry( + custom_size_frame, textvariable=self.width_var, width=8 + ) + self.width_entry.pack(side="left", padx=5) + + ttk.Label(custom_size_frame, text="高度:").pack(side="left", padx=5) + self.height_var = tk.StringVar(value="auto") + self.height_entry = ttk.Entry( + custom_size_frame, textvariable=self.height_var, width=8 + ) + self.height_entry.pack(side="left", padx=5) + + # FPS设置 + fps_frame = ttk.Frame(self.settings_frame) + fps_frame.pack(fill="x", padx=5, pady=5) + + ttk.Label(fps_frame, text="FPS:").pack(side="left", padx=5) + self.fps_var = tk.StringVar(value="10") + self.fps_entry = ttk.Entry(fps_frame, textvariable=self.fps_var, width=8) + self.fps_entry.pack(side="left", padx=5) + + # 时长控制 + duration_frame = ttk.Frame(self.settings_frame) + duration_frame.pack(fill="x", padx=5, pady=5) + + ttk.Label(duration_frame, text="时长控制(秒):").pack(side="left", padx=5) + ttk.Label(duration_frame, text="开始时间:").pack(side="left", padx=5) + self.start_time_var = tk.StringVar(value="0") + self.start_time_entry = ttk.Entry( + duration_frame, textvariable=self.start_time_var, width=8 + ) + self.start_time_entry.pack(side="left", padx=5) + + ttk.Label(duration_frame, text="持续时间:").pack(side="left", padx=5) + self.duration_var = tk.StringVar(value="") + self.duration_entry = ttk.Entry( + duration_frame, textvariable=self.duration_var, width=8 + ) + self.duration_entry.pack(side="left", padx=5) + ttk.Label(duration_frame, text="(留空表示全部)").pack(side="left", padx=5) + + # 质量设置 + quality_frame = ttk.Frame(self.settings_frame) + quality_frame.pack(fill="x", padx=5, pady=5) + + ttk.Label(quality_frame, text="质量设置:").pack(side="left", padx=5) + self.quality_var = tk.StringVar(value="medium") + ttk.Radiobutton( + quality_frame, text="高质量", variable=self.quality_var, value="high" + ).pack(side="left", padx=5) + ttk.Radiobutton( + quality_frame, text="中等", variable=self.quality_var, value="medium" + ).pack(side="left", padx=5) + ttk.Radiobutton( + quality_frame, text="低质量", variable=self.quality_var, value="low" + ).pack(side="left", padx=5) + + # 输出设置 + output_frame = ttk.Frame(self.settings_frame) + output_frame.pack(fill="x", padx=5, pady=5) + + ttk.Label(output_frame, text="输出位置:").pack(side="left", padx=5) + self.output_var = tk.StringVar(value="same") + ttk.Radiobutton( + output_frame, text="与视频相同目录", variable=self.output_var, value="same" + ).pack(side="left", padx=5) + ttk.Radiobutton( + output_frame, text="自定义目录", variable=self.output_var, value="custom" + ).pack(side="left", padx=5) + + self.output_path_var = tk.StringVar() + self.output_path_entry = ttk.Entry( + output_frame, textvariable=self.output_path_var, width=30 + ) + self.output_path_entry.pack(side="left", padx=5) + + self.browse_btn = ttk.Button( + output_frame, text="浏览", command=self.browse_output + ) + self.browse_btn.pack(side="left", padx=5) + + # 转换按钮 + self.convert_btn = ttk.Button( + self.root, text="开始转换", command=self.start_conversion + ) + self.convert_btn.pack(pady=10) + + # 进度条 + self.progress = ttk.Progressbar(self.root, mode="determinate") + self.progress.pack(fill="x", padx=10, pady=5) + + # 状态标签 + self.status_label = ttk.Label(self.root, text="就绪") + self.status_label.pack(pady=5) + + def show_context_menu(self, event): + """显示右键菜单""" + try: + self.files_list_menu.tk_popup(event.x_root, event.y_root) + finally: + self.files_list_menu.grab_release() + + def delete_selected(self): + """删除选中的文件""" + selection = self.files_list.curselection() + for index in reversed(selection): + self.files_list.delete(index) + + def browse_output(self): + directory = filedialog.askdirectory() + if directory: + self.output_path_var.set(directory) + + def select_files(self): + files = filedialog.askopenfilenames( + filetypes=[ + ("All supported formats", "*.mp4 *.avi *.mov *.mkv *.webm *.gif"), + ("Video files", "*.mp4 *.avi *.mov *.mkv *.webm"), + ("GIF files", "*.gif"), + ] + ) + self.files_list.delete(0, tk.END) + for file in files: + self.files_list.insert(tk.END, file) + + def get_quality_settings(self): + """根据质量设置返回 FFmpeg 参数""" + quality = self.quality_var.get() + if quality == "high": + return ["-quality", "100"] + elif quality == "medium": + return ["-quality", "75"] + else: + return ["-quality", "50"] + + def validate_inputs(self): + """验证输入参数""" + try: + # 验证FPS + fps = int(self.fps_var.get()) + if fps <= 0: + raise ValueError("FPS必须大于0") + + # 验证时间设置 + start_time = float(self.start_time_var.get() or 0) + if start_time < 0: + raise ValueError("开始时间不能为负数") + + if self.duration_var.get(): + duration = float(self.duration_var.get()) + if duration <= 0: + raise ValueError("持续时间必须大于0") + + # 验证自定义尺寸 + if self.size_var.get() == "custom": + width = int(self.width_var.get()) + if width <= 0: + raise ValueError("宽度必须大于0") + + height = self.height_var.get() + if height != "auto": + height = int(height) + if height <= 0: + raise ValueError("高度必须大于0") + + return True + except ValueError as e: + messagebox.showerror("输入错误", str(e)) + return False + + def convert_to_webp(self, input_path): + try: + # 验证输入 + if not self.validate_inputs(): + return False + + # 确定输出路径 + if self.output_var.get() == "same": + output_dir = os.path.dirname(input_path) + else: + output_dir = self.output_path_var.get() + if not output_dir: + raise ValueError("请选择输出目录") + + output_path = os.path.join( + output_dir, os.path.splitext(os.path.basename(input_path))[0] + ".webp" + ) + + # 获取设置值 + start_time = float(self.start_time_var.get() or 0) + duration = self.duration_var.get() + fps = int(self.fps_var.get()) + + # 获取CPU核心数 + cpu_count = os.cpu_count() or 1 + threads = max(1, min(cpu_count - 1, 8)) + + # 获取ffmpeg路径 + ffmpeg_exe = os.path.join( + ffmpeg_path, + "ffmpeg.exe" if platform.system().lower() == "windows" else "ffmpeg", + ) + + # 检查输入文件类型 + is_gif = input_path.lower().endswith(".gif") + + # 构建命令 + cmd = [ffmpeg_exe, "-y", "-threads", str(threads)] # 覆盖输出文件 + + # 添加时间控制 (仅对视频有效) + if not is_gif and start_time > 0: + cmd.extend(["-ss", str(start_time)]) + + cmd.extend(["-i", input_path]) + + if not is_gif and duration: + cmd.extend(["-t", str(float(duration))]) + + # 构建滤镜链 + filters = [] + + # FPS控制 + if ( + not is_gif or self.fps_var.get() != "10" + ): # GIF默认保持原FPS,除非用户修改 + filters.append(f"fps={fps}") + + # 尺寸控制 + if self.size_var.get() == "custom": + width = int(self.width_var.get()) + height = self.height_var.get() + height = -1 if height == "auto" else int(height) + filters.append(f"scale={width}:{height}") + + # 添加滤镜 + if filters: + cmd.extend(["-vf", ",".join(filters)]) + + # 添加质量设置 + quality = self.quality_var.get() + if quality == "high": + cmd.extend(["-quality", "90"]) # WebP质量0-100 + elif quality == "medium": + cmd.extend(["-quality", "75"]) + else: + cmd.extend(["-quality", "60"]) + + # 设置循环次数 (0表示无限循环) + cmd.extend(["-loop", "0"]) + + # 添加WebP编码器的特定参数 + cmd.extend( + [ + "-preset", + "picture", # 使用图片预设 + "-compression_level", + "4", # 压缩级别 (0-6) + "-lossless", + "0", # 使用有损压缩 + "-metadata", + "none", # 不包含元数据 + ] + ) + + # 添加输出文件 + cmd.append(output_path) + + # 获取subprocess配置 + subprocess_config = get_subprocess_config() + + # 打印命令用于调试 + print("WebP转换命令:", " ".join(cmd)) + + # 更新状态显示 + self.status_label.config(text=f"正在转换... {os.path.basename(input_path)}") + self.root.update() + + # 运行转换命令 + process = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **subprocess_config + ) + _, stderr = process.communicate() + + if process.returncode != 0: + raise RuntimeError(f"转换失败: {stderr.decode()}") + + return True + + except Exception as e: + error_msg = str(e) + print(f"Conversion error: {error_msg}") + print(f"Error type: {type(e)}") + traceback.print_exc() + messagebox.showerror("错误", f"转换失败:\n{error_msg}") + return False + + def start_conversion(self): + def convert(): + try: + files = self.files_list.get(0, tk.END) + if not files: + messagebox.showwarning("警告", "请先选择要转换的文件") + return + + total = len(files) + success_count = 0 + + for i, file in enumerate(files): + current = i + 1 + self.status_label.config( + text=f"正在转换: {os.path.basename(file)} ({current}/{total})" + ) + if self.convert_to_webp(file): # 使用新的转换函数 + success_count += 1 + progress = current / total * 100 + self.progress["value"] = progress + + self.status_label.config(text=f"转换完成 ({success_count}/{total})") + self.convert_btn["state"] = "normal" + + if success_count == total: + messagebox.showinfo( + "完成", f"所有文件转换完成!\n成功转换 {total} 个文件" + ) + else: + messagebox.showwarning( + "完成", + f"转换完成,但有部分失败。\n成功:{success_count}/{total}", + ) + + except Exception as e: + print(f"Conversion error: {str(e)}") + traceback.print_exc() + messagebox.showerror("错误", f"转换过程出错:\n{str(e)}") + finally: + self.convert_btn["state"] = "normal" + + self.convert_btn["state"] = "disabled" + self.progress["value"] = 0 + Thread(target=convert).start() + + def run(self): + self.root.mainloop() + + +if __name__ == "__main__": + try: + print("程序启动...") + + app = VideoToWebpConverter() + app.run() + except Exception as e: + print(f"程序运行出错: {str(e)}") + import traceback + + traceback.print_exc() # 打印详细错误信息 + sys.exit(1) diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000..385421c Binary files /dev/null and b/icon.ico differ diff --git a/icons/favicon.ico b/icons/favicon.ico new file mode 100644 index 0000000..385421c Binary files /dev/null and b/icons/favicon.ico differ diff --git a/icons/logo800.icns b/icons/logo800.icns new file mode 100644 index 0000000..15b564a Binary files /dev/null and b/icons/logo800.icns differ diff --git a/icons/logo800.jpg b/icons/logo800.jpg new file mode 100644 index 0000000..11f8a63 Binary files /dev/null and b/icons/logo800.jpg differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4ae851c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# 核心依赖 +Pillow>=9.0.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..5b4a4cc --- /dev/null +++ b/setup.cfg @@ -0,0 +1,17 @@ +[metadata] +name = video2webp +version = attr: video2webp.__version__ +description = A GUI tool to convert videos to WEBP +long_description = file: README.md +long_description_content_type = text/markdown +author = woodchen +author_email = wood@czl.net +url = https://github.com/woodchen-ink/video2webp + +[options] +packages = find: +python_requires = >=3.7 +install_requires = + ffmpeg-python>=0.2.0 + Pillow>=9.0.0 + tkinterdnd2>=0.3.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4075a99 --- /dev/null +++ b/setup.py @@ -0,0 +1,42 @@ +from setuptools import setup, find_packages + +setup( + name="video-to-webp-converter", + version="0.1.0", + packages=find_packages(), + install_requires=[ + "ffmpeg-python>=0.2.0", + "Pillow>=9.0.0", + ], + extras_require={ + "gui": [ + "ttkthemes>=3.2.0", + "ttkwidgets>=0.12.0", + ], + "dev": [ + "pytest>=7.0.0", + "black>=22.0.0", + "flake8>=4.0.0", + ], + }, + python_requires=">=3.7", + # 元数据 + author="Your Name", + author_email="your.email@example.com", + description="A simple video to WEBP converter with GUI", + long_description=open("README.md").read(), + long_description_content_type="text/markdown", + keywords="video, webp, converter, ffmpeg, gui", + url="https://github.com/yourusername/video-to-webp-converter", + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: End Users/Desktop", + "Topic :: Multimedia :: Video :: Conversion", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + ], +) diff --git a/video2gif.spec b/video2gif.spec new file mode 100644 index 0000000..7a2e4fc --- /dev/null +++ b/video2gif.spec @@ -0,0 +1,45 @@ +# video2webp.spec +block_cipher = None + +a = Analysis( + ['gui.py'], + pathex=[], + binaries=[], + datas=[ + ('ffmpeg/ffmpeg.exe', 'ffmpeg'), + ('ffmpeg/ffprobe.exe', 'ffmpeg'), + ('README.md', '.') + ], + hiddenimports=['ffmpeg', 'ffmpeg-python'], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='video2webp', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +)