cmd:pip install tkinterdnd2 pysrt
#pip install tkinterdnd2 pysrt
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from tkinterdnd2 import DND_FILES, TkinterDnD
import os
import subprocess
import threading
from concurrent.futures import ThreadPoolExecutor
import pysrt
from datetime import timedelta
class Mp3SrtSplitterApp:
def __init__(self, root):
self.root = root
self.root.title("mp3 & srt splitter")
self.root.geometry("600x700")
self.files = {"mp3": [], "srt": []}
self.progress = 0
self.lock = threading.Lock()
self.create_widgets()
def create_widgets(self):
dnd_frame = ttk.LabelFrame(self.root, text="drop test1.mp3&test1.srt test2.mp3&test2.srt hint here")
dnd_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.listbox = tk.Listbox(dnd_frame, selectmode=tk.MULTIPLE)
self.listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.listbox.drop_target_register(DND_FILES)
self.listbox.dnd_bind('<<Drop>>', self.drop)
self.progress_bar = ttk.Progressbar(self.root, orient="horizontal", length=400, mode="determinate")
self.progress_bar.pack(pady=10)
self.console_output = tk.Text(self.root, height=8, state='disabled')
self.console_output.pack(fill=tk.BOTH, padx=10, pady=5)
button_frame = ttk.Frame(self.root)
button_frame.pack(fill=tk.X, padx=10, pady=5)
add_button = ttk.Button(button_frame, text="add files", command=self.add_files)
add_button.pack(side=tk.LEFT, padx=5)
split_button = ttk.Button(button_frame, text="split ", command=self.start_splitting)
split_button.pack(side=tk.LEFT, padx=5)
clear_button = ttk.Button(button_frame, text="clear", command=self.clear_list)
clear_button.pack(side=tk.LEFT, padx=5)
def add_files(self):
file_paths = filedialog.askopenfilenames(
title="choice .mp3 .srt files",
filetypes=[("MP3 and SRT files", "*.mp3 *.srt")])
self.process_files(file_paths)
def drop(self, event):
files = self.root.splitlist(event.data)
self.process_files(files)
def process_files(self, file_paths):
for file_path in file_paths:
file_path = file_path.strip('{}')
if os.path.isfile(file_path):
if file_path.lower().endswith('.mp3'):
if file_path not in self.files["mp3"]:
self.files["mp3"].append(file_path)
self.listbox.insert(tk.END, file_path)
elif file_path.lower().endswith('.srt'):
if file_path not in self.files["srt"]:
self.files["srt"].append(file_path)
self.listbox.insert(tk.END, file_path)
def clear_list(self):
self.files = {"mp3": [], "srt": []}
self.listbox.delete(0, tk.END)
self.progress_bar["value"] = 0
self.update_console("")
def start_splitting(self):
if not self.files["mp3"] or not self.files["srt"]:
messagebox.showwarning("No files found", "Please drag-and-drop or add .mp3 and .srt files to split.")
return
matched_pairs = self.match_files()
if not matched_pairs:
messagebox.showwarning("No matching files found", "No matching .mp3 and .srt files were found.")
return
self.progress_bar["maximum"] = len(matched_pairs)
self.progress = 0
threading.Thread(target=self.split_files, args=(matched_pairs,), daemon=True).start()
def match_files(self):
pairs = []
for mp3_file in self.files["mp3"]:
base_name = os.path.splitext(os.path.basename(mp3_file))[0]
for srt_file in self.files["srt"]:
if os.path.splitext(os.path.basename(srt_file))[0] == base_name:
pairs.append((mp3_file, srt_file))
break
return pairs
def split_files(self, pairs):
with ThreadPoolExecutor(max_workers=4) as executor:
futures = []
for mp3_file, srt_file in pairs:
futures.append(executor.submit(self.split_mp3_and_srt, mp3_file, srt_file))
for future in futures:
future.result()
def split_mp3_and_srt(self, mp3_file, srt_file):
try:
subs = pysrt.open(srt_file)
total_duration = self.get_mp3_duration(mp3_file)
target_seconds = total_duration / 2
nearest_time = self.find_nearest_split_time(subs, target_seconds)
# Split the .srt file and the .mp3 file at the nearest time
self.split_srt(srt_file, nearest_time)
self.split_mp3(mp3_file, nearest_time)
self.update_console_threadsafe(f"Done: {mp3_file} and {srt_file}")
except Exception as e:
self.update_console_threadsafe(f"Error: {mp3_file} and {srt_file}\nError Message: {e}")
def get_mp3_duration(self, mp3_file):
result = subprocess.run(
["ffprobe", "-i", mp3_file, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
return float(result.stdout.strip())
def find_nearest_split_time(self, subs, target_seconds):
min_diff = float('inf')
nearest_time = None
for sub in subs:
# Calculating the total end time in seconds without using timedelta
end_seconds = (sub.end.hours * 3600) + (sub.end.minutes * 60) + sub.end.seconds + (sub.end.milliseconds / 1000.0)
diff = abs(end_seconds - target_seconds)
if diff < min_diff:
min_diff = diff
nearest_time = sub.end
return nearest_time
def split_srt(self, srt_file, split_time):
subs = pysrt.open(srt_file)
part1 = subs.slice(ends_before=split_time)
part2 = subs.slice(starts_after=split_time)
# Calculate the offset as seconds to avoid using timedelta
offset_seconds = (split_time.hours * 3600) + (split_time.minutes * 60) + split_time.seconds + (split_time.milliseconds / 1000.0)
part2.shift(seconds=-offset_seconds) # Use seconds directly for shifting
base_name = os.path.splitext(srt_file)[0]
part1.save(f"{base_name}_part1.srt")
part2.save(f"{base_name}_part2.srt")
def split_mp3(self, mp3_file, split_time):
hours = split_time.hours
minutes = split_time.minutes
seconds = split_time.seconds
milliseconds = split_time.milliseconds
# Convert milliseconds to fractional seconds
frac_seconds = seconds + milliseconds / 1000.0
split_time_str = f"{hours:02}:{minutes:02}:{frac_seconds:06.3f}"
base_name = os.path.splitext(mp3_file)[0]
part1_path = f"{base_name}_part1.mp3"
part2_path = f"{base_name}_part2.mp3"
subprocess.run(["ffmpeg", "-i", mp3_file, "-to", split_time_str, "-c", "copy", part1_path])
subprocess.run(["ffmpeg", "-i", mp3_file, "-ss", split_time_str, "-c", "copy", part2_path])
def update_console_threadsafe(self, message):
self.root.after(0, self.update_console, message)
def update_console(self, message):
self.console_output.config(state='normal')
self.console_output.insert(tk.END, message + "\n")
self.console_output.see(tk.END)
self.console_output.config(state='disabled')
def main():
root = TkinterDnD.Tk()
app = Mp3SrtSplitterApp(root)
root.mainloop()
if __name__ == "__main__":
main()