245 lines
8.9 KiB
Python
245 lines
8.9 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import queue
|
|
import threading
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from tkinter import END, LEFT, RIGHT, W, filedialog, messagebox
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
|
|
from hosters import UploadError, UploadResult, build_uploaders
|
|
|
|
|
|
APP_DIR = Path(__file__).resolve().parent
|
|
CONFIG_PATH = APP_DIR / "config.json"
|
|
|
|
|
|
@dataclass
|
|
class HostRow:
|
|
host: str
|
|
enabled: tk.BooleanVar
|
|
credential: tk.StringVar
|
|
|
|
|
|
class UploaderApp:
|
|
def __init__(self, root: tk.Tk) -> None:
|
|
self.root = root
|
|
self.root.title("Multi Hoster Uploader")
|
|
self.root.geometry("980x680")
|
|
|
|
self.uploaders = build_uploaders()
|
|
self.files: list[Path] = []
|
|
self.log_queue: queue.Queue[str] = queue.Queue()
|
|
self.link_lines: list[str] = []
|
|
|
|
self.host_rows: dict[str, HostRow] = {}
|
|
self._build_ui()
|
|
self._load_config()
|
|
self._poll_logs()
|
|
|
|
def _build_ui(self) -> None:
|
|
wrapper = ttk.Frame(self.root, padding=12)
|
|
wrapper.pack(fill=tk.BOTH, expand=True)
|
|
|
|
title = ttk.Label(wrapper, text="Uploader fuer doodstream / voe / vidmoly / byse", font=("Segoe UI", 13, "bold"))
|
|
title.pack(anchor=W)
|
|
|
|
subtitle = ttk.Label(
|
|
wrapper,
|
|
text="Nutze deine eigenen Accounts/API Keys. Das Tool nutzt nur offizielle oder kompatible Upload-Endpunkte.",
|
|
)
|
|
subtitle.pack(anchor=W, pady=(2, 10))
|
|
|
|
hosts_frame = ttk.LabelFrame(wrapper, text="Hoster Login / API Key", padding=10)
|
|
hosts_frame.pack(fill=tk.X)
|
|
|
|
for idx, host in enumerate(self.uploaders.keys()):
|
|
enabled = tk.BooleanVar(value=True if host in ("doodstream.com", "voe.sx") else False)
|
|
credential = tk.StringVar()
|
|
|
|
chk = ttk.Checkbutton(hosts_frame, text=host, variable=enabled)
|
|
chk.grid(row=idx, column=0, sticky=W, padx=(0, 10), pady=4)
|
|
|
|
entry = ttk.Entry(hosts_frame, width=70, textvariable=credential, show="*")
|
|
entry.grid(row=idx, column=1, sticky="we", pady=4)
|
|
|
|
self.host_rows[host] = HostRow(host=host, enabled=enabled, credential=credential)
|
|
|
|
hosts_frame.columnconfigure(1, weight=1)
|
|
|
|
files_frame = ttk.LabelFrame(wrapper, text="Dateien", padding=10)
|
|
files_frame.pack(fill=tk.BOTH, expand=False, pady=(10, 0))
|
|
|
|
btn_row = ttk.Frame(files_frame)
|
|
btn_row.pack(fill=tk.X)
|
|
|
|
ttk.Button(btn_row, text="Dateien waehlen", command=self._pick_files).pack(side=LEFT)
|
|
ttk.Button(btn_row, text="Auswahl loeschen", command=self._clear_files).pack(side=LEFT, padx=8)
|
|
|
|
self.upload_btn = ttk.Button(btn_row, text="Upload starten", command=self._start_upload)
|
|
self.upload_btn.pack(side=RIGHT)
|
|
|
|
self.file_list = tk.Listbox(files_frame, height=8)
|
|
self.file_list.pack(fill=tk.X, pady=(8, 0))
|
|
|
|
output_pane = ttk.PanedWindow(wrapper, orient=tk.HORIZONTAL)
|
|
output_pane.pack(fill=tk.BOTH, expand=True, pady=(10, 0))
|
|
|
|
log_frame = ttk.LabelFrame(output_pane, text="Log", padding=8)
|
|
links_frame = ttk.LabelFrame(output_pane, text="Output Links", padding=8)
|
|
output_pane.add(log_frame, weight=1)
|
|
output_pane.add(links_frame, weight=1)
|
|
|
|
self.log_text = tk.Text(log_frame, height=14, wrap=tk.WORD)
|
|
self.log_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
self.links_text = tk.Text(links_frame, height=14, wrap=tk.WORD)
|
|
self.links_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
action_row = ttk.Frame(wrapper)
|
|
action_row.pack(fill=tk.X, pady=(8, 0))
|
|
|
|
ttk.Button(action_row, text="Links kopieren", command=self._copy_links).pack(side=LEFT)
|
|
ttk.Button(action_row, text="Config speichern", command=self._save_config).pack(side=LEFT, padx=8)
|
|
|
|
def _pick_files(self) -> None:
|
|
selected = filedialog.askopenfilenames(title="Dateien zum Upload auswaehlen")
|
|
if not selected:
|
|
return
|
|
for item in selected:
|
|
path = Path(item)
|
|
if path not in self.files:
|
|
self.files.append(path)
|
|
self.file_list.insert(END, str(path))
|
|
|
|
def _clear_files(self) -> None:
|
|
self.files = []
|
|
self.file_list.delete(0, END)
|
|
|
|
def _copy_links(self) -> None:
|
|
text = self.links_text.get("1.0", END).strip()
|
|
if not text:
|
|
messagebox.showinfo("Info", "Noch keine Links vorhanden.")
|
|
return
|
|
self.root.clipboard_clear()
|
|
self.root.clipboard_append(text)
|
|
self.root.update_idletasks()
|
|
messagebox.showinfo("Fertig", "Links in Zwischenablage kopiert.")
|
|
|
|
def _append_log(self, line: str) -> None:
|
|
self.log_text.insert(END, line + "\n")
|
|
self.log_text.see(END)
|
|
|
|
def _append_result(self, result: UploadResult) -> None:
|
|
lines = [
|
|
f"[{result.host}] {result.file_path.name}",
|
|
f" file_code: {result.file_code or '-'}",
|
|
f" download: {result.download_url or '-'}",
|
|
f" embed: {result.embed_url or '-'}",
|
|
"",
|
|
]
|
|
for line in lines:
|
|
self.link_lines.append(line)
|
|
|
|
self.links_text.delete("1.0", END)
|
|
self.links_text.insert("1.0", "\n".join(self.link_lines).strip() + "\n")
|
|
|
|
def _save_config(self) -> None:
|
|
payload = {
|
|
"hosts": {
|
|
host: {
|
|
"enabled": row.enabled.get(),
|
|
"credential": row.credential.get(),
|
|
}
|
|
for host, row in self.host_rows.items()
|
|
}
|
|
}
|
|
CONFIG_PATH.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
messagebox.showinfo("Gespeichert", f"Config gespeichert: {CONFIG_PATH}")
|
|
|
|
def _load_config(self) -> None:
|
|
if not CONFIG_PATH.exists():
|
|
return
|
|
try:
|
|
data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return
|
|
hosts = data.get("hosts", {})
|
|
if not isinstance(hosts, dict):
|
|
return
|
|
for host, row in self.host_rows.items():
|
|
host_cfg = hosts.get(host, {})
|
|
if not isinstance(host_cfg, dict):
|
|
continue
|
|
if "enabled" in host_cfg:
|
|
row.enabled.set(bool(host_cfg["enabled"]))
|
|
if "credential" in host_cfg and isinstance(host_cfg["credential"], str):
|
|
row.credential.set(host_cfg["credential"])
|
|
|
|
def _start_upload(self) -> None:
|
|
enabled_hosts = [row for row in self.host_rows.values() if row.enabled.get()]
|
|
if not enabled_hosts:
|
|
messagebox.showwarning("Fehlt", "Bitte mindestens einen Hoster aktivieren.")
|
|
return
|
|
if not self.files:
|
|
messagebox.showwarning("Fehlt", "Bitte mindestens eine Datei auswaehlen.")
|
|
return
|
|
|
|
self.upload_btn.config(state=tk.DISABLED)
|
|
worker = threading.Thread(target=self._run_upload, args=(enabled_hosts, list(self.files)), daemon=True)
|
|
worker.start()
|
|
|
|
def _run_upload(self, enabled_hosts: list[HostRow], files: list[Path]) -> None:
|
|
self.log_queue.put("Upload gestartet...")
|
|
ok_count = 0
|
|
fail_count = 0
|
|
|
|
for host_row in enabled_hosts:
|
|
uploader = self.uploaders[host_row.host]
|
|
credential = host_row.credential.get().strip()
|
|
if not credential:
|
|
self.log_queue.put(f"[{host_row.host}] uebersprungen: Kein Login/API Key hinterlegt")
|
|
fail_count += len(files)
|
|
continue
|
|
|
|
for file_path in files:
|
|
self.log_queue.put(f"[{host_row.host}] Upload: {file_path.name}")
|
|
try:
|
|
result = uploader.upload_file(file_path, credential)
|
|
self.root.after(0, self._append_result, result)
|
|
self.log_queue.put(f"[{host_row.host}] OK: {file_path.name}")
|
|
ok_count += 1
|
|
except (UploadError, OSError, ValueError) as exc:
|
|
self.log_queue.put(f"[{host_row.host}] FEHLER: {file_path.name} -> {exc}")
|
|
fail_count += 1
|
|
except Exception as exc:
|
|
self.log_queue.put(f"[{host_row.host}] FEHLER (unerwartet): {file_path.name} -> {exc}")
|
|
fail_count += 1
|
|
|
|
self.log_queue.put(f"Fertig. Erfolgreich: {ok_count}, Fehler: {fail_count}")
|
|
self.root.after(0, lambda: self.upload_btn.config(state=tk.NORMAL))
|
|
|
|
def _poll_logs(self) -> None:
|
|
while True:
|
|
try:
|
|
line = self.log_queue.get_nowait()
|
|
self._append_log(line)
|
|
except queue.Empty:
|
|
break
|
|
self.root.after(120, self._poll_logs)
|
|
|
|
|
|
def main() -> None:
|
|
root = tk.Tk()
|
|
style = ttk.Style(root)
|
|
if "vista" in style.theme_names():
|
|
style.theme_use("vista")
|
|
app = UploaderApp(root)
|
|
root.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|