Skip to content

Instantly share code, notes, and snippets.

@turicas
Created December 13, 2024 17:44
Show Gist options
  • Save turicas/7b1ed8982da59d8db71033b94d47c1c3 to your computer and use it in GitHub Desktop.
Save turicas/7b1ed8982da59d8db71033b94d47c1c3 to your computer and use it in GitHub Desktop.
Python script with Tkinter interface to extract clip/timeline data from Kdenlive project file and create a "cue sheet" (no external dependencies)
#!/usr/bin/env python3
import csv
import os
import tkinter as tk
import xml.etree.ElementTree as ET
from pathlib import Path
from tkinter import filedialog, messagebox
def get_properties(element):
props = {}
for prop in element.findall("property"):
props[prop.get("name")] = prop.text if prop.text is not None else ""
return props
def parse_timecode(tc):
return tc
def infer_file_type(filepath, props):
stream_type = props.get("meta.media.0.stream.type", "").lower()
if stream_type in ["video", "audio", "image"]:
return stream_type
ext = os.path.splitext(filepath)[1].lower()
if ext in [".jpg", ".jpeg", ".png", ".tif", ".tiff", ".bmp"]:
return "image"
elif ext in [".mp4", ".mov", ".mkv", ".avi", ".mxf", ".wmv"]:
return "video"
elif ext in [".wav", ".mp3", ".aac", ".flac", ".ogg", ".m4a"]:
return "audio"
return "unknown"
def parse_kdenlive_file(filename: Path):
tree = ET.parse(filename)
root = tree.getroot()
id_to_props = {}
for elem in root.findall("./chain"):
cid = elem.get("id")
if cid:
id_to_props[cid] = get_properties(elem)
for elem in root.findall("./producer"):
pid = elem.get("id")
if pid:
id_to_props[pid] = get_properties(elem)
entries_info = []
for playlist in root.findall("./playlist"):
for entry in playlist.findall("./entry"):
producer_id = entry.get("producer")
if not producer_id:
continue
in_time = entry.get("in", "00:00:00.000")
out_time = entry.get("out", "00:00:00.000")
if producer_id in id_to_props:
props = id_to_props[producer_id]
filepath = props.get("kdenlive:originalurl", props.get("resource", ""))
if not os.path.isabs(filepath):
project_root = root.get("root", "")
if project_root:
filepath = os.path.join(project_root, filepath)
else:
filepath = filename.parent / filepath
filetype = infer_file_type(filepath, props)
entries_info.append(
{
"full_filename": str(filepath),
"name": Path(filepath).name,
"file_type": filetype,
"start": in_time,
"end": out_time,
}
)
return entries_info
def run_extraction(kdenlive_file, csv_file):
entries = parse_kdenlive_file(Path(kdenlive_file))
with open(csv_file, "w", newline="", encoding="utf-8") as fobj:
writer = None
for entry in entries:
if writer is None:
writer = csv.DictWriter(fobj, fieldnames=list(entry.keys()))
writer.writeheader()
writer.writerow(entry)
messagebox.showinfo("Concluído", f"O arquivo CSV foi gerado em:\n{csv_file}")
def select_kdenlive():
filename = filedialog.askopenfilename(
title="Selecione o arquivo .kdenlive",
filetypes=[("Kdenlive Projects", "*.kdenlive"), ("All files", "*.*")]
)
if filename:
kdenlive_var.set(filename)
def select_csv():
filename = filedialog.asksaveasfilename(
title="Selecione o arquivo CSV de saída",
defaultextension=".csv",
filetypes=[("CSV Files", "*.csv"), ("All files", "*.*")]
)
if filename:
csv_var.set(filename)
def execute():
kdenlive_file = kdenlive_var.get()
csv_file = csv_var.get()
if not kdenlive_file or not os.path.exists(kdenlive_file):
messagebox.showerror("Erro", "Por favor, selecione um arquivo .kdenlive válido.")
return
if not csv_file:
messagebox.showerror("Erro", "Por favor, selecione o arquivo CSV de saída.")
return
run_extraction(kdenlive_file, csv_file)
root = tk.Tk()
root.title("Extrair informações do Kdenlive")
kdenlive_var = tk.StringVar()
csv_var = tk.StringVar()
frame = tk.Frame(root, padx=10, pady=10)
frame.pack()
tk.Label(
frame,
text=(
"Este programa lê um arquivo de projeto do Kdenlive, identifica tudo que está na timeline \n"
"e cria uma planilha 'cue sheet' com as seguintes informações das mídias presentes:\n"
"- Nome completo do arquivo\n"
"- Nome do arquivo\n"
"- Tipo (áudio, vídeo, imagem)\n"
"- Horário de início na timeline\n"
"- Horário de fim na timeline"
),
font=("Helvetica", 20)
).grid(row=0, column=0, columnspan=3, pady=(0, 10))
tk.Label(frame, text="Arquivo .kdenlive:").grid(row=1, column=0, sticky="e")
tk.Entry(frame, textvariable=kdenlive_var, width=50).grid(row=1, column=1, padx=5, pady=5)
tk.Button(frame, text="Selecionar...", command=select_kdenlive).grid(row=1, column=2, padx=5, pady=5)
tk.Label(frame, text="Arquivo CSV de saída:").grid(row=2, column=0, sticky="e")
tk.Entry(frame, textvariable=csv_var, width=50).grid(row=2, column=1, padx=5, pady=5)
tk.Button(frame, text="Selecionar...", command=select_csv).grid(row=2, column=2, padx=5, pady=5)
tk.Button(frame, text="Executar", command=execute, bg="#5f5", width=20).grid(row=3, column=1, pady=10)
root.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment