Skip to content

Instantly share code, notes, and snippets.

@kompowiec
Last active September 6, 2025 15:28
Show Gist options
  • Save kompowiec/9a3faa242e6c13bae87a9a83f7a23162 to your computer and use it in GitHub Desktop.
Save kompowiec/9a3faa242e6c13bae87a9a83f7a23162 to your computer and use it in GitHub Desktop.
implementation feature for linux: https://cdburnerxp.se/help/Data/disc-spanning
import os
import shutil
import subprocess
import tkinter as tk
from tkinter import filedialog, messagebox
from collections import defaultdict
def get_file_sizes(directory):
files = []
for root, _, filenames in os.walk(directory):
for filename in filenames:
filepath = os.path.join(root, filename)
files.append((filepath, os.path.getsize(filepath)))
return sorted(files, key=lambda x: x[1], reverse=True)
def span_discs(files, disc_size, method="fewest_discs"):
discs = []
current_disc = []
current_size = 0
if method == "fewest_discs":
for file, size in files:
if current_size + size > disc_size:
discs.append(current_disc)
current_disc = []
current_size = 0
current_disc.append(file)
current_size += size
if current_disc:
discs.append(current_disc)
elif method == "keep_folders":
folder_groups = defaultdict(list)
for file, size in files:
folder = os.path.dirname(file)
folder_groups[folder].append((file, size))
for folder, file_list in folder_groups.items():
folder_size = sum(size for _, size in file_list)
if folder_size > disc_size:
for file, size in file_list:
if current_size + size > disc_size:
discs.append(current_disc)
current_disc = []
current_size = 0
current_disc.append(file)
current_size += size
else:
if current_size + folder_size > disc_size:
discs.append(current_disc)
current_disc = []
current_size = 0
current_disc.extend([file for file, _ in file_list])
current_size += folder_size
if current_disc:
discs.append(current_disc)
return discs
def save_table_of_contents(discs, output_dir):
for i, disc in enumerate(discs, start=1):
disc_folder = os.path.join(output_dir, f"Disc_{i}")
os.makedirs(disc_folder, exist_ok=True)
toc_path = os.path.join(disc_folder, "table_of_contents.txt")
with open(toc_path, "w") as toc:
toc.write(f"Disc {i}:\n")
for file in disc:
toc.write(f" {file}\n")
toc.write("\n")
print(f"Table of contents saved to {toc_path}")
def copy_files_to_output(discs, output_dir, input_dir):
for i, disc in enumerate(discs, start=1):
disc_folder = os.path.join(output_dir, f"Disc_{i}")
os.makedirs(disc_folder, exist_ok=True)
for file in disc:
relative_path = os.path.relpath(file, input_dir)
dest_dir = os.path.join(disc_folder, os.path.dirname(relative_path))
os.makedirs(dest_dir, exist_ok=True)
dest = os.path.join(dest_dir, os.path.basename(file))
shutil.copy2(file, dest)
print(f"Disc {i} files copied to {disc_folder}")
def create_iso(discs, output_dir):
for i, _ in enumerate(discs, start=1):
disc_folder = os.path.join(output_dir, f"Disc_{i}")
iso_path = os.path.join(output_dir, f"Disc_{i}.iso")
try:
subprocess.run([
"mkisofs", "-o", iso_path, "-joliet-long", "-R", disc_folder
], check=True)
print(f"ISO created: {iso_path}")
shutil.rmtree(disc_folder)
print(f"Deleted folder: {disc_folder}")
except FileNotFoundError:
print("Error: mkisofs command not found. Please install cdrkit (Linux) or use an equivalent tool.")
except subprocess.CalledProcessError as e:
print(f"Error creating ISO for {disc_folder}: {e}")
def run_gui():
root = tk.Tk()
root.title("Disc Spanning and ISO Creator")
def browse_input():
input_path.set(filedialog.askdirectory())
def browse_output():
output_path.set(filedialog.askdirectory())
def update_custom_entry(*args):
if disc_size.get() == "Custom":
custom_size_entry.config(state="normal")
else:
custom_size_entry.config(state="disabled")
disc_size_value.set(disc_size.get().split()[0])
def start_process():
input_dir = input_path.get()
output_dir = output_path.get()
disc_size_mb = float(disc_size_value.get())
ecc_option = ecc_var.get()
if ecc_option == "Normal":
disc_size_mb *= (1 - 0.143)
elif ecc_option == "High":
disc_size_mb *= (1 - 0.335)
disc_size_bytes = disc_size_mb * 1024 * 1024
method_choice = method_var.get()
files = get_file_sizes(input_dir)
discs = span_discs(files, disc_size_bytes, method_choice)
if toc_var.get(): # Only save TOC if checkbox is checked
save_table_of_contents(discs, output_dir)
if iso_var.get(): # Only create ISO if checkbox is checked
copy_files_to_output(discs, output_dir, input_dir)
create_iso(discs, output_dir)
messagebox.showinfo("Success", "Disc spanning and ISO creation complete!")
input_path = tk.StringVar()
output_path = tk.StringVar()
disc_size = tk.StringVar(value="700 (CD)")
disc_size_value = tk.StringVar(value="700")
ecc_var = tk.StringVar(value="None")
method_var = tk.StringVar(value="fewest_discs")
toc_var = tk.BooleanVar(value=True) # Default: generate TOC
iso_var = tk.BooleanVar(value=False) # Default: don't create ISO files
tk.Label(root, text="Input Directory:").pack()
tk.Entry(root, textvariable=input_path, width=50).pack()
tk.Button(root, text="Browse", command=browse_input).pack()
tk.Label(root, text="Output Directory:").pack()
tk.Entry(root, textvariable=output_path, width=50).pack()
tk.Button(root, text="Browse", command=browse_output).pack()
tk.Label(root, text="Disc Size (MiB):").pack()
sizes = ["1.2 (Floppy 1.2 MB)",
"1.44 (Floppy 1.4 MB)",
"100 (Zip Drive)",
"185 (CD 21 min)",
"250 (Zip Drive)",
"553 (CD 63 min)",
"650 (CD 74 min)",
"700 (CD 80 min)",
"750 (Zip Drive)",
"790 (CD 90 min)",
"870 (CD 99 min)",
"1392.36 (DVD-1)",
"2527.23 (DVD-2 DL)",
"4489.25 (DVD-5)",
"4482.625 (DVD+5)",
"8147.875 (DVD-9 DL)",
"8152 (DVD+9 DL)",
"14378.12 (HD DVD)",
"31846.49 (HD DVD-DL)",
"25600 (BD)",
"51200 (BD-DL)",
"102400 (BD-XL)",
"131072 (BD-XL)",
"Custom"]
disc_size_menu = tk.OptionMenu(root, disc_size, *sizes, command=update_custom_entry)
disc_size_menu.pack()
custom_size_entry = tk.Entry(root, textvariable=disc_size_value, state="disabled")
custom_size_entry.pack()
tk.Label(root, text="Error Correction Code:").pack()
tk.OptionMenu(root, ecc_var, "None", "Normal", "High").pack()
tk.Label(root, text="Method:").pack()
tk.OptionMenu(root, method_var, "fewest_discs", "keep_folders").pack()
tk.Checkbutton(root, text="Generate Table of Contents for each disc", variable=toc_var).pack()
tk.Checkbutton(root, text="Create ISO files for each disc", variable=iso_var).pack()
tk.Button(root, text="Start", command=start_process).pack()
root.mainloop()
if __name__ == "__main__":
run_gui()
@kompowiec
Copy link
Author

kompowiec commented Mar 14, 2025

screenshot:
Disc spanning GUI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment