import tkinter as tk
from tkinter import messagebox, filedialog
from tkinter import ttk
import requests
import re
import os
import subprocess
import json
from threading import Thread

# Hilfsfunktion zur Extraktion der Video-ID aus Twitch-URL
def extract_video_id(url):
    match = re.search(r"videos/(\d+)", url)
    return match.group(1) if match else None

# Video-Metadaten (Titel) via Twitch GraphQL API holen
def fetch_video_title(video_id):
    client_id = 'kimne78kx3ncx6brgo4mv6wki5h1ko'
    query = '''
        query VideoByID($id: ID!) {
            video(id: $id) {
                title
            }
        }
    '''
    payload = [{
        'operationName': 'VideoByID',
        'variables': {'id': video_id},
        'query': query
    }]
    headers = {'Client-ID': client_id, 'Content-Type': 'application/json'}
    try:
        res = requests.post('https://gql.twitch.tv/gql', headers=headers, data=json.dumps(payload))
        res.raise_for_status()
        data = res.json()
        return data[0]['data']['video']['title']
    except Exception:
        return video_id

# M3U8-Playlist abrufen über Twitch GraphQL API
def get_all_m3u8_urls(video_id):
    payload = [{
        "operationName": "PlaybackAccessToken_Template",
        "variables": {"vodID": video_id, "playerType": "embed"},
        "query": """
            query PlaybackAccessToken_Template($vodID: ID!, $playerType: String!) {
                videoPlaybackAccessToken(
                    id: $vodID,
                    params: { platform: \"web\", playerBackend: \"mediaplayer\", playerType: $playerType }
                ) {
                    signature
                    value
                }
            }
        """
    }]
    headers = {"Client-ID": "kimne78kx3ncx6brgo4mv6wki5h1ko", "Content-Type": "application/json"}
    res = requests.post("https://gql.twitch.tv/gql", headers=headers, data=json.dumps(payload))
    res.raise_for_status()
    data = res.json()[0]['data']['videoPlaybackAccessToken']
    sig, token = data['signature'], data['value']
    m3u8_url = f"https://usher.ttvnw.net/vod/{video_id}.m3u8"
    params = {"player": "twitchweb", "token": token, "sig": sig,
              "allow_source": "true", "allow_audio_only": "true", "playlist_include_framerate": "true"}
    playlist_resp = requests.get(m3u8_url, params=params)
    playlist_resp.raise_for_status()
    playlist = playlist_resp.text
    streams = re.findall(r"#EXT-X-STREAM-INF:.*RESOLUTION=(\d+x\d+).*\n(.*)", playlist, re.IGNORECASE)
    return {res: url for res, url in streams}

# Download-Funktion, Aufrufe GUI-aktualisierend via safe callbacks
def download_video(m3u8_url, output_path, safe_log, safe_progress):
    try:
        cmd = ["ffmpeg", "-i", m3u8_url, "-c", "copy", "-bsf:a", "aac_adtstoasc", output_path]
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', errors='ignore')
        for line in process.stdout:
            safe_log(line.strip())
        process.wait()
        safe_log(f"Video gespeichert: {output_path}")
        safe_progress(100)
    except Exception as e:
        safe_log(f"Fehler beim ffmpeg-Lauf: {e}")
        safe_progress(0)

# Hauptklasse für GUI
def main():
    root = tk.Tk()
    root.title("Twitch Downloader")

    # Widgets
    tk.Label(root, text="Twitch VOD URL:").pack(anchor='w', padx=10, pady=(10,0))
    url_entry = tk.Entry(root, width=60)
    url_entry.pack(padx=10)

    tk.Label(root, text="Video-Titel:").pack(anchor='w', padx=10, pady=(10,0))
    title_var = tk.StringVar()
    title_entry = tk.Entry(root, textvariable=title_var, width=60)
    title_entry.pack(padx=10)

    tk.Label(root, text="Qualität auswählen:").pack(anchor='w', padx=10, pady=(10,0))
    quality_var = tk.StringVar()
    quality_menu = tk.OptionMenu(root, quality_var, "Bitte URL eingeben")
    quality_menu.pack(padx=10)

    progress = tk.DoubleVar()
    progressbar = ttk.Progressbar(root, variable=progress, maximum=100)
    progressbar.pack(fill='x', padx=10, pady=5)

    log_text = tk.Text(root, height=8, width=70, state='disabled')
    log_text.pack(padx=10, pady=(0,10))

    output_path = [None]  # mutable container

    # Thread-safe GUI-Update-Funktionen
    def safe_log(msg):
        root.after(0, lambda: log(msg))
    def safe_progress(val):
        root.after(0, lambda: progress.set(val))

    def log(msg):
        log_text.configure(state='normal')
        log_text.insert(tk.END, msg+"\n")
        log_text.see(tk.END)
        log_text.configure(state='disabled')

    def on_url_change(event=None):
        vid = extract_video_id(url_entry.get().strip())
        if not vid:
            return
        title = fetch_video_title(vid)
        title_var.set(title)
        log(f"Titel geladen: {title}")
        streams = get_all_m3u8_urls(vid)
        menu = quality_menu['menu']
        menu.delete(0,'end')
        for r,u in streams.items():
            menu.add_command(label=r, command=lambda r=r: quality_var.set(r))
        if streams:
            q = next(iter(streams))
            quality_var.set(q)
            root.streams = streams
            log(f"Qualitäten: {', '.join(streams.keys())}")

    def save_as():
        fname = filedialog.asksaveasfilename(
            defaultextension='.mp4',
            filetypes=[('MP4','*.mp4'),('All','*.*')],
            initialfile=f"{title_var.get() or 'video'}.mp4"
        )
        if fname:
            output_path[0] = fname
            log(f"Speichern unter: {fname}")

    def start_dl():
        if not output_path[0]:
            messagebox.showerror('Fehler','Bitte über "Speichern unter..." einen Zielpfad wählen.')
            return
        vid = extract_video_id(url_entry.get().strip())
        q = quality_var.get()
        u = getattr(root,'streams',{}).get(q)
        if not all([vid,q,u]):
            messagebox.showerror('Fehler','Bitte URL eingeben und Qualität auswählen.')
            return
        log('Starte Download...')
        Thread(target=lambda: download_video(u, output_path[0], safe_log, safe_progress)).start()

    url_entry.bind('<FocusOut>', on_url_change)
    tk.Button(root, text='Speichern unter...', command=save_as).pack(pady=5)
    tk.Button(root, text='Download starten', command=start_dl).pack(pady=5)

    root.mainloop()

if __name__=='__main__':
    main()
