MP4 video splitter in Python using ffmpeg
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
5.7 KiB

#!/usr/bin/env python3
import sys
import os
import subprocess
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton,
QFileDialog, QLabel, QLineEdit, QProgressBar)
from PyQt6.QtCore import QThread, pyqtSignal
class SplitWorker(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal()
def __init__(self, input_file, size_limit_mb, output_dir):
super().__init__()
self.input_file = input_file
self.size_limit = size_limit_mb * 1000000 # Convert MB to bytes
self.output_dir = output_dir
def run(self):
# Extract basename and set output pattern
basename = os.path.splitext(os.path.basename(self.input_file))[0]
output_pattern = os.path.join(self.output_dir, f"{basename}-%d.mp4")
# Get total duration
duration_cmd = [
"ffprobe", "-i", self.input_file, "-show_entries", "format=duration",
"-v", "quiet", "-of", "default=noprint_wrappers=1:nokey=1"
]
duration = float(subprocess.check_output(duration_cmd).decode().strip().split(".")[0])
cur_duration = 0
i = 1
while cur_duration < duration:
output_file = os.path.join(self.output_dir, f"{basename}-{i}.mp4")
ffmpeg_cmd = [
"ffmpeg", "-ss", str(cur_duration), "-i", self.input_file,
"-fs", str(self.size_limit), "-c", "copy", "-y", output_file
]
subprocess.run(ffmpeg_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Get duration of the new chunk
new_duration_cmd = [
"ffprobe", "-i", output_file, "-show_entries", "format=duration",
"-v", "quiet", "-of", "default=noprint_wrappers=1:nokey=1"
]
new_duration = float(subprocess.check_output(new_duration_cmd).decode().strip().split(".")[0])
cur_duration += new_duration
self.progress.emit(f"Created chunk {i} ({cur_duration}/{duration} seconds processed)")
i += 1
self.finished.emit()
class VideoSplitterGUI(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Video Splitter")
self.setGeometry(100, 100, 400, 300)
# Main widget and layout
widget = QWidget()
self.setCentralWidget(widget)
layout = QVBoxLayout()
widget.setLayout(layout)
# Input file selection
self.input_label = QLabel("No file selected")
self.select_input_btn = QPushButton("Select MP4 File")
self.select_input_btn.clicked.connect(self.select_input_file)
layout.addWidget(self.input_label)
layout.addWidget(self.select_input_btn)
# Size limit input (in MB)
self.size_label = QLabel("Chunk Size (MB):")
self.size_input = QLineEdit("110") # Default 110 MB
layout.addWidget(self.size_label)
layout.addWidget(self.size_input)
# Output directory selection
self.output_label = QLabel("Output Directory: Current directory")
self.select_output_btn = QPushButton("Select Output Directory")
self.select_output_btn.clicked.connect(self.select_output_dir)
layout.addWidget(self.output_label)
layout.addWidget(self.select_output_btn)
# Start button
self.start_btn = QPushButton("Start Splitting")
self.start_btn.clicked.connect(self.start_splitting)
layout.addWidget(self.start_btn)
# Progress
self.progress_label = QLabel("Ready")
self.progress_bar = QProgressBar()
self.progress_bar.setMinimum(0)
self.progress_bar.setMaximum(100)
layout.addWidget(self.progress_label)
layout.addWidget(self.progress_bar)
# Stretch to push content up
layout.addStretch()
# Variables
self.input_file = ""
self.output_dir = os.getcwd()
def select_input_file(self):
file, _ = QFileDialog.getOpenFileName(self, "Select MP4 File", "", "MP4 Files (*.mp4)")
if file:
self.input_file = file
self.input_label.setText(f"Selected: {os.path.basename(file)}")
def select_output_dir(self):
dir = QFileDialog.getExistingDirectory(self, "Select Output Directory")
if dir:
self.output_dir = dir
self.output_label.setText(f"Output Directory: {dir}")
def start_splitting(self):
if not self.input_file:
self.progress_label.setText("Error: No input file selected")
return
size_limit_mb = self.size_input.text()
try:
size_limit_mb = float(size_limit_mb) # Allow decimals like 110.5 MB
if size_limit_mb <= 0:
raise ValueError
except ValueError:
self.progress_label.setText("Error: Invalid size limit (must be a positive number)")
return
self.start_btn.setEnabled(False)
self.progress_label.setText("Splitting in progress...")
self.worker = SplitWorker(self.input_file, size_limit_mb, self.output_dir)
self.worker.progress.connect(self.update_progress)
self.worker.finished.connect(self.split_finished)
self.worker.start()
def update_progress(self, message):
self.progress_label.setText(message)
# Simple progress approximation (could be enhanced with actual file size checks)
self.progress_bar.setValue(self.progress_bar.value() + 10)
def split_finished(self):
self.progress_label.setText("Splitting complete!")
self.progress_bar.setValue(100)
self.start_btn.setEnabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = VideoSplitterGUI()
window.show()
sys.exit(app.exec())