/**
 * Video-Bibliothek Frontend
 * Bereiche pro Scan-Pfad, CRUD, Clean, Import, TVDB-Metadaten
 */

let libraryPaths = [];
let sectionStates = {};  // pathId -> {tab, page, limit}
let activePathId = null;  // null = alle anzeigen
let filterTimeout = null;
let currentSeriesId = null;
let currentMovieId = null;
let currentDetailTab = "episodes";
let currentImportJobId = null;
let cleanData = [];

// === Initialisierung ===

document.addEventListener("DOMContentLoaded", function () {
    // Gespeicherten UI-State wiederherstellen
    _restoreUIState();
    loadStats();
    loadFilterPresets();
    loadLibraryPaths();
});

// === UI-State in localStorage ===

function _saveUIState() {
    try {
        localStorage.setItem("vk_activePathId", JSON.stringify(activePathId));
        // Aktuelle Filter-Werte speichern
        const filterState = {};
        const fields = [
            "filter-search", "filter-codec", "filter-container",
            "filter-audio-lang", "filter-audio-ch", "filter-resolution",
            "filter-sort", "filter-order"
        ];
        for (const f of fields) {
            const el = document.getElementById(f);
            if (el) filterState[f] = el.value;
        }
        const cb10bit = document.getElementById("filter-10bit");
        if (cb10bit) filterState["filter-10bit"] = cb10bit.checked;
        const cbNotConv = document.getElementById("filter-not-converted");
        if (cbNotConv) filterState["filter-not-converted"] = cbNotConv.checked;
        localStorage.setItem("vk_filterState", JSON.stringify(filterState));
        // Tab-States pro Pfad speichern
        const tabStates = {};
        for (const [pid, st] of Object.entries(sectionStates)) {
            tabStates[pid] = st.tab;
        }
        localStorage.setItem("vk_tabStates", JSON.stringify(tabStates));
    } catch (e) { /* localStorage nicht verfuegbar */ }
}

function _restoreUIState() {
    try {
        const stored = localStorage.getItem("vk_activePathId");
        if (stored !== null) activePathId = JSON.parse(stored);
    } catch (e) { /* ignorieren */ }
}

function _restoreFilterState() {
    try {
        const raw = localStorage.getItem("vk_filterState");
        if (!raw) return;
        const fs = JSON.parse(raw);
        const fields = [
            "filter-search", "filter-codec", "filter-container",
            "filter-audio-lang", "filter-audio-ch", "filter-resolution",
            "filter-sort", "filter-order"
        ];
        for (const f of fields) {
            const el = document.getElementById(f);
            if (el && fs[f] !== undefined) el.value = fs[f];
        }
        const cb10bit = document.getElementById("filter-10bit");
        if (cb10bit && fs["filter-10bit"] !== undefined) cb10bit.checked = fs["filter-10bit"];
        const cbNotConv = document.getElementById("filter-not-converted");
        if (cbNotConv && fs["filter-not-converted"] !== undefined) cbNotConv.checked = fs["filter-not-converted"];
    } catch (e) { /* ignorieren */ }
}

function _restoreTabStates() {
    try {
        const raw = localStorage.getItem("vk_tabStates");
        if (!raw) return;
        const ts = JSON.parse(raw);
        for (const [pid, tab] of Object.entries(ts)) {
            if (sectionStates[pid]) {
                sectionStates[pid].tab = tab;
            }
        }
    } catch (e) { /* ignorieren */ }
}

// === Statistiken ===

function loadStats() {
    fetch("/api/library/stats")
        .then(r => r.json())
        .then(data => {
            document.getElementById("stat-videos").textContent = data.total_videos || 0;
            document.getElementById("stat-series").textContent = data.total_series || 0;
            document.getElementById("stat-size").textContent = formatSize(data.total_size || 0);
            document.getElementById("stat-duration").textContent = formatDuration(data.total_duration || 0);
        })
        .catch(() => {});
}

// === Library-Bereiche laden ===

function loadLibraryPaths() {
    fetch("/api/library/paths")
        .then(r => r.json())
        .then(data => {
            libraryPaths = data.paths || [];
            renderPathNav();
            renderLibrarySections();
        })
        .catch(() => {
            document.getElementById("library-content").innerHTML =
                '<div class="loading-msg">Fehler beim Laden der Bibliothek</div>';
        });
}

// === Pfad-Navigation (links) ===

function renderPathNav() {
    const nav = document.getElementById("nav-paths-list");
    if (!nav) return;
    const enabled = libraryPaths.filter(p => p.enabled);
    if (!enabled.length) {
        nav.innerHTML = '<div style="font-size:0.75rem;color:#666;padding:0.3rem">Keine Pfade</div>';
        return;
    }

    let html = '';
    // "Alle" Eintrag
    html += `<div class="nav-path-item ${activePathId === null ? 'active' : ''}" onclick="selectLibraryPath(null)">
        <span class="nav-path-icon">&#128218;</span>
        <span class="nav-path-name">Alle</span>
    </div>`;

    for (const lp of enabled) {
        const icon = lp.media_type === 'series' ? '&#127916;' : '&#127910;';
        const isActive = activePathId === lp.id;
        html += `<div class="nav-path-item ${isActive ? 'active' : ''}" onclick="selectLibraryPath(${lp.id})">
            <span class="nav-path-icon">${icon}</span>
            <span class="nav-path-name" title="${escapeHtml(lp.path)}">${escapeHtml(lp.name)}</span>
        </div>`;
    }
    nav.innerHTML = html;
}

function selectLibraryPath(pathId) {
    activePathId = pathId;
    _saveUIState();
    renderPathNav();
    renderLibrarySections();
}

function renderLibrarySections() {
    const container = document.getElementById("library-content");
    if (!libraryPaths.length) {
        container.innerHTML = '<div class="loading-msg">Keine Scan-Pfade konfiguriert. Klicke "Pfade verwalten" um zu starten.</div>';
        return;
    }

    // Welche Pfade anzeigen?
    const visiblePaths = libraryPaths.filter(lp => {
        if (!lp.enabled) return false;
        if (activePathId !== null && lp.id !== activePathId) return false;
        return true;
    });

    if (!visiblePaths.length) {
        container.innerHTML = '<div class="loading-msg">Kein aktiver Pfad ausgewaehlt</div>';
        return;
    }

    let html = "";
    for (const lp of visiblePaths) {
        const pid = lp.id;
        const isSeriesLib = lp.media_type === "series";
        if (!sectionStates[pid]) {
            sectionStates[pid] = {
                tab: "videos",
                page: 1,
                limit: 50,
            };
        }
        const st = sectionStates[pid];

        html += `<div class="lib-section" id="section-${pid}">`;
        html += `<div class="lib-section-header">`;
        html += `<h3>${escapeHtml(lp.name)}</h3>`;
        html += `<span class="text-muted" style="font-size:0.75rem">${escapeHtml(lp.path)}</span>`;
        html += `<div class="lib-section-actions">`;
        html += `<button class="btn-small btn-secondary" onclick="scanSinglePath(${pid})">Scannen</button>`;
        html += `</div></div>`;

        // Tabs - Serien-Pfad: Videos+Serien+Ordner / Film-Pfad: Videos+Filme+Ordner
        html += `<div class="library-tabs" id="tabs-${pid}">`;
        html += `<button class="tab-btn ${st.tab === 'videos' ? 'active' : ''}" data-tab="videos" onclick="switchSectionTab(${pid}, 'videos')">Videos</button>`;
        if (isSeriesLib) {
            html += `<button class="tab-btn ${st.tab === 'series' ? 'active' : ''}" data-tab="series" onclick="switchSectionTab(${pid}, 'series')">Serien</button>`;
        } else {
            html += `<button class="tab-btn ${st.tab === 'movies' ? 'active' : ''}" data-tab="movies" onclick="switchSectionTab(${pid}, 'movies')">Filme</button>`;
        }
        html += `<button class="tab-btn ${st.tab === 'browser' ? 'active' : ''}" data-tab="browser" onclick="switchSectionTab(${pid}, 'browser')">Ordner</button>`;
        html += `</div>`;

        // Tab-Content
        html += `<div class="section-content" id="content-${pid}">`;
        html += `<div class="loading-msg">Lade...</div>`;
        html += `</div>`;

        html += `</div>`;
    }
    container.innerHTML = html;

    // Gespeicherte Tab-States wiederherstellen
    _restoreTabStates();

    // Daten laden fuer sichtbare Bereiche
    for (const lp of visiblePaths) {
        loadSectionData(lp.id);
    }
}

function switchSectionTab(pathId, tab) {
    sectionStates[pathId].tab = tab;
    sectionStates[pathId].page = 1;
    _saveUIState();
    // Tab-Buttons aktualisieren
    const tabBar = document.getElementById("tabs-" + pathId);
    if (tabBar) {
        tabBar.querySelectorAll(".tab-btn").forEach(b => {
            b.classList.toggle("active", b.getAttribute("data-tab") === tab);
        });
    }
    loadSectionData(pathId);
}

function loadSectionData(pathId) {
    const st = sectionStates[pathId];
    switch (st.tab) {
        case "videos": loadSectionVideos(pathId); break;
        case "series": loadSectionSeries(pathId); break;
        case "movies": loadSectionMovies(pathId); break;
        case "browser": loadSectionBrowser(pathId); break;
    }
}

// === Videos pro Bereich ===

function loadSectionVideos(pathId, page) {
    const st = sectionStates[pathId];
    if (page) st.page = page;
    const params = buildFilterParams();
    params.set("library_path_id", pathId);
    params.set("page", st.page);
    params.set("limit", st.limit);

    const content = document.getElementById("content-" + pathId);
    fetch("/api/library/videos?" + params.toString())
        .then(r => r.json())
        .then(data => {
            let html = '<div class="table-wrapper">';
            html += renderVideoTable(data.items || []);
            html += '</div>';
            html += renderPagination(data.total || 0, data.page || 1, data.pages || 1, pathId, "videos");
            content.innerHTML = html;
        })
        .catch(() => { content.innerHTML = '<div class="loading-msg">Fehler</div>'; });
}

// === Filme pro Bereich ===

function loadSectionMovies(pathId) {
    const content = document.getElementById("content-" + pathId);
    fetch("/api/library/movies-list?path_id=" + pathId)
        .then(r => r.json())
        .then(data => {
            content.innerHTML = renderMovieGrid(data.movies || []);
        })
        .catch(() => { content.innerHTML = '<div class="loading-msg">Fehler</div>'; });
}

function renderMovieGrid(movies) {
    if (!movies.length) return '<div class="loading-msg">Keine Filme gefunden</div>';

    let html = '<div class="movie-grid">';
    for (const m of movies) {
        const poster = m.poster_url
            ? `<img src="${m.poster_url}" alt="" class="movie-poster" loading="lazy">`
            : '<div class="movie-poster-placeholder">Kein Poster</div>';
        const year = m.year ? `<span class="tag">${m.year}</span>` : "";
        const genres = m.genres ? `<div class="movie-genres">${escapeHtml(m.genres)}</div>` : "";
        const duration = m.duration_sec ? formatDuration(m.duration_sec) : "";
        const size = m.total_size ? formatSize(m.total_size) : "";
        const tvdbBtn = m.tvdb_id
            ? '<span class="tag ok">TVDB</span>'
            : `<button class="btn-small btn-secondary" onclick="event.stopPropagation(); openMovieTvdbModal(${m.id}, '${escapeAttr(m.title || m.folder_name)}')">TVDB zuordnen</button>`;
        const overview = m.overview
            ? `<p class="movie-overview">${escapeHtml(m.overview.substring(0, 120))}${m.overview.length > 120 ? '...' : ''}</p>`
            : "";

        html += `<div class="movie-card" onclick="openMovieDetail(${m.id})">
            ${poster}
            <div class="movie-info">
                <h4 title="${escapeHtml(m.folder_path || '')}">${escapeHtml(m.title || m.folder_name)}</h4>
                ${genres}
                ${overview}
                <div class="movie-meta">
                    ${year}
                    ${duration ? `<span class="text-muted">${duration}</span>` : ""}
                    ${size ? `<span class="text-muted">${size}</span>` : ""}
                    <span class="text-muted">${m.video_count || 0} Dateien</span>
                    ${tvdbBtn}
                </div>
            </div>
        </div>`;
    }
    html += '</div>';
    return html;
}

// === Serien pro Bereich ===

function loadSectionSeries(pathId) {
    const content = document.getElementById("content-" + pathId);
    fetch("/api/library/series?path_id=" + pathId)
        .then(r => r.json())
        .then(data => {
            content.innerHTML = renderSeriesGrid(data.series || []);
        })
        .catch(() => { content.innerHTML = '<div class="loading-msg">Fehler</div>'; });
}

// === Ordner pro Bereich ===

let _browserLoading = false;

function loadSectionBrowser(pathId, subPath) {
    // Doppelklick-Schutz: Zweiten Aufruf ignorieren solange geladen wird
    if (_browserLoading) return;
    _browserLoading = true;

    const content = document.getElementById("content-" + pathId);
    content.innerHTML = '<div class="loading-msg">Lade Ordner...</div>';

    const params = new URLSearchParams();
    if (subPath) params.set("path", subPath);
    else {
        // Basispfad der Library verwenden
        const lp = libraryPaths.find(p => p.id === pathId);
        if (lp) params.set("path", lp.path);
    }

    fetch("/api/library/browse?" + params.toString())
        .then(r => r.json())
        .then(data => {
            let html = renderBreadcrumb(data.breadcrumb || [], pathId);
            html += renderBrowser(data.folders || [], data.videos || [], pathId);
            content.innerHTML = html;
        })
        .catch(() => { content.innerHTML = '<div class="loading-msg">Fehler</div>'; })
        .finally(() => { _browserLoading = false; });
}

// === Video-Tabelle (gemeinsam genutzt) ===

function renderVideoTable(items) {
    if (!items.length) return '<div class="loading-msg">Keine Videos gefunden</div>';

    let html = '<table class="data-table"><thead><tr>';
    html += '<th>Dateiname</th><th>Aufl.</th><th>Codec</th><th>Audio</th>';
    html += '<th>Untertitel</th><th>Groesse</th><th>Dauer</th><th>Container</th><th>Aktion</th>';
    html += '</tr></thead><tbody>';

    for (const v of items) {
        const audioInfo = (v.audio_tracks || []).map(a => {
            const lang = (a.lang || "?").toUpperCase().substring(0, 3);
            const ch = channelLayout(a.channels);
            return `<span class="tag">${lang} ${ch}</span>`;
        }).join(" ");
        const subInfo = (v.subtitle_tracks || []).map(s =>
            `<span class="tag">${(s.lang || "?").toUpperCase().substring(0, 3)}</span>`
        ).join(" ");
        const res = v.width && v.height ? resolutionLabel(v.width, v.height) : "-";
        const is10bit = v.is_10bit ? ' <span class="tag hdr">10bit</span>' : "";

        const vidTitle = v.file_name || "Video";
        html += `<tr>
            <td class="td-name" title="${escapeHtml(v.file_path || '')}">${escapeHtml(v.file_name || "-")}</td>
            <td>${res}${is10bit}</td>
            <td><span class="tag codec">${v.video_codec || "-"}</span></td>
            <td class="td-audio">${audioInfo || "-"}</td>
            <td class="td-sub">${subInfo || "-"}</td>
            <td>${formatSize(v.file_size || 0)}</td>
            <td>${formatDuration(v.duration_sec || 0)}</td>
            <td><span class="tag">${(v.container || "-").toUpperCase()}</span></td>
            <td>
                <button class="btn-small btn-play" onclick="playVideo(${v.id}, '${escapeAttr(vidTitle)}')" title="Abspielen">&#9654;</button>
                <button class="btn-small btn-primary" onclick="convertVideo(${v.id})">Conv</button>
                <button class="btn-small btn-danger" onclick="deleteVideo(${v.id}, '${escapeAttr(vidTitle)}')" title="Loeschen">&#10005;</button>
            </td>
        </tr>`;
    }
    html += '</tbody></table>';
    return html;
}

function renderPagination(total, page, pages, pathId, tabType) {
    let html = '<div class="pagination-row">';
    html += '<div class="pagination">';
    if (pages <= 1) {
        html += `<span class="page-info">${total} Videos</span>`;
    } else {
        html += `<span class="page-info">${total} Videos | Seite ${page}/${pages}</span> `;
        if (page > 1) {
            html += `<button class="btn-small btn-secondary" onclick="loadSection${tabType === 'movies' ? 'Movies' : 'Videos'}(${pathId}, ${page - 1})">&larr;</button> `;
        }
        if (page < pages) {
            html += `<button class="btn-small btn-secondary" onclick="loadSection${tabType === 'movies' ? 'Movies' : 'Videos'}(${pathId}, ${page + 1})">&rarr;</button>`;
        }
    }
    html += '</div>';
    html += `<div class="page-limit">
        <label>Eintraege:</label>
        <select onchange="changeSectionLimit(${pathId}, this.value)">
            <option value="25" ${sectionStates[pathId].limit === 25 ? 'selected' : ''}>25</option>
            <option value="50" ${sectionStates[pathId].limit === 50 ? 'selected' : ''}>50</option>
            <option value="100" ${sectionStates[pathId].limit === 100 ? 'selected' : ''}>100</option>
            <option value="200" ${sectionStates[pathId].limit === 200 ? 'selected' : ''}>200</option>
        </select>
    </div>`;
    html += '</div>';
    return html;
}

function changeSectionLimit(pathId, val) {
    sectionStates[pathId].limit = parseInt(val) || 50;
    sectionStates[pathId].page = 1;
    loadSectionData(pathId);
}

// === Serien-Grid ===

function renderSeriesGrid(series) {
    if (!series.length) return '<div class="loading-msg">Keine Serien gefunden</div>';

    let html = '<div class="series-grid">';
    for (const s of series) {
        const poster = s.poster_url
            ? `<img src="${s.poster_url}" alt="" class="series-poster" loading="lazy">`
            : '<div class="series-poster-placeholder">Kein Poster</div>';
        const missing = s.missing_episodes > 0
            ? `<span class="status-badge warn">${s.missing_episodes} fehlend</span>` : "";
        const redundant = s.redundant_files > 0
            ? `<span class="status-badge info" title="Mehrere Dateien fuer gleiche Episode">${s.redundant_files} redundant</span>` : "";
        const genres = s.genres ? `<div class="series-genres">${escapeHtml(s.genres)}</div>` : "";
        const tvdbBtn = s.tvdb_id
            ? `<span class="tag ok">TVDB</span>`
            : `<button class="btn-small btn-secondary" onclick="event.stopPropagation(); openTvdbModal(${s.id}, '${escapeAttr(s.folder_name)}')">TVDB zuordnen</button>`;
        const codecBadge = s.codec_badge
            ? `<span class="series-codec-badge">${escapeHtml(s.codec_badge)}</span>` : "";

        html += `<div class="series-card" onclick="openSeriesDetail(${s.id})">
            <div class="series-poster-wrap">
                ${poster}
                ${codecBadge}
            </div>
            <div class="series-info">
                <h4 title="${escapeHtml(s.folder_path || '')}">${escapeHtml(s.title || s.folder_name)}</h4>
                ${genres}
                <div class="series-meta">
                    <span>${s.local_episodes || 0} Episoden</span>
                    ${missing}
                    ${redundant}
                    ${tvdbBtn}
                </div>
                ${s.status ? `<span class="tag">${s.status}</span>` : ""}
            </div>
        </div>`;
    }
    html += '</div>';
    return html;
}

// === Ordner-Ansicht ===

function renderBreadcrumb(crumbs, pathId) {
    let html = '<div class="browser-breadcrumb">';
    html += `<a class="breadcrumb-link" href="#" onclick="loadSectionBrowser(${pathId}); return false;">Basis</a>`;
    for (const c of crumbs) {
        html += ' <span class="breadcrumb-sep">/</span> ';
        html += `<a class="breadcrumb-link" href="#" onclick="loadSectionBrowser(${pathId}, '${escapeAttr(c.path)}'); return false;">${escapeHtml(c.name)}</a>`;
    }
    html += '</div>';
    return html;
}

function renderBrowser(folders, videos, pathId) {
    if (!folders.length && !videos.length) return '<div class="loading-msg">Leerer Ordner</div>';

    let html = "";
    if (folders.length) {
        html += '<div class="browser-folders">';
        for (const f of folders) {
            const size = formatSize(f.total_size || 0);
            const pathEsc = f.path.replace(/'/g, "\\'");
            html += `<div class="browser-folder">
                <div class="folder-main" onclick="loadSectionBrowser(${pathId}, '${pathEsc}')">
                    <span class="folder-icon">&#128193;</span>
                    <div class="folder-info">
                        <span class="folder-name">${escapeHtml(f.name)}</span>
                        <span class="folder-meta">${f.video_count} Videos, ${size}</span>
                    </div>
                </div>
                <button class="btn-folder-delete" onclick="event.stopPropagation(); showDeleteFolderDialog('${pathEsc}', ${pathId}, ${f.video_count})" title="Ordner loeschen">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"/>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
                        <line x1="10" y1="11" x2="10" y2="17"/>
                        <line x1="14" y1="11" x2="14" y2="17"/>
                    </svg>
                </button>
            </div>`;
        }
        html += '</div>';
    }

    if (videos.length) {
        html += '<div class="browser-videos">';
        html += renderVideoTable(videos);
        html += '</div>';
    }
    return html;
}

// === Serien-Detail ===

function openSeriesDetail(seriesId) {
    if (event) event.stopPropagation();
    currentSeriesId = seriesId;
    currentDetailTab = "episodes";
    document.getElementById("series-modal").style.display = "flex";

    // Tabs zuruecksetzen
    document.querySelectorAll(".detail-tab").forEach(b => b.classList.remove("active"));
    document.querySelector('.detail-tab[onclick*="episodes"]').classList.add("active");

    fetch(`/api/library/series/${seriesId}`)
        .then(r => r.json())
        .then(data => {
            document.getElementById("series-modal-title").textContent = data.title || data.folder_name;
            document.getElementById("series-modal-genres").textContent = data.genres || "";
            // Aktions-Buttons anzeigen
            document.getElementById("btn-tvdb-refresh").style.display = data.tvdb_id ? "" : "none";
            document.getElementById("btn-tvdb-unlink").style.display = data.tvdb_id ? "" : "none";
            document.getElementById("btn-metadata-dl").style.display = data.tvdb_id ? "" : "none";
            renderEpisodesTab(data);
        })
        .catch(() => {
            document.getElementById("series-modal-body").innerHTML =
                '<div class="loading-msg">Fehler beim Laden</div>';
        });
}

function switchDetailTab(tab) {
    currentDetailTab = tab;
    document.querySelectorAll(".detail-tab").forEach(b => b.classList.remove("active"));
    document.querySelector(`.detail-tab[onclick*="${tab}"]`).classList.add("active");

    if (tab === "episodes") {
        fetch(`/api/library/series/${currentSeriesId}`)
            .then(r => r.json())
            .then(data => renderEpisodesTab(data))
            .catch(() => {});
    } else if (tab === "cast") {
        loadCast();
    } else if (tab === "artworks") {
        loadArtworks();
    }
}

function renderEpisodesTab(series) {
    const body = document.getElementById("series-modal-body");
    let html = '<div class="series-detail-header">';
    if (series.poster_url) {
        html += `<img src="${series.poster_url}" alt="" class="series-detail-poster">`;
    }
    html += '<div class="series-detail-info">';
    if (series.overview) html += `<p class="series-overview">${escapeHtml(series.overview)}</p>`;
    html += '<div class="series-detail-meta">';
    if (series.first_aired) html += `<span class="tag">${series.first_aired}</span>`;
    if (series.status) html += `<span class="tag">${series.status}</span>`;
    html += `<span class="tag">${series.local_episodes || 0} lokal</span>`;
    if (series.total_episodes) html += `<span class="tag">${series.total_episodes} gesamt</span>`;
    if (series.missing_episodes > 0) html += `<span class="status-badge warn">${series.missing_episodes} fehlend</span>`;
    if (series.redundant_files > 0) html += `<span class="status-badge info" title="Mehrere Dateien fuer gleiche Episode (z.B. .mkv und .webm)">${series.redundant_files} redundant</span>`;
    html += '</div></div></div>';

    // Episoden nach Staffeln
    const episodes = series.episodes || [];
    const tvdbEpisodes = series.tvdb_episodes || [];
    const seasons = {};

    for (const ep of episodes) {
        const s = ep.season_number || 0;
        if (!seasons[s]) seasons[s] = {local: [], missing: []};
        seasons[s].local.push(ep);
    }
    if (tvdbEpisodes.length) {
        const localSet = new Set(episodes.map(e => `${e.season_number}-${e.episode_number}`));
        for (const ep of tvdbEpisodes) {
            const key = `${ep.season_number}-${ep.episode_number}`;
            if (!localSet.has(key) && ep.season_number > 0) {
                const s = ep.season_number;
                if (!seasons[s]) seasons[s] = {local: [], missing: []};
                seasons[s].missing.push(ep);
            }
        }
    }

    const sortedSeasons = Object.keys(seasons).map(Number).sort((a, b) => a - b);
    for (const sNum of sortedSeasons) {
        const sData = seasons[sNum];
        html += `<details class="season-details" ${sNum === sortedSeasons[0] ? "open" : ""}>`;
        html += `<summary>Staffel ${sNum || "Unbekannt"} (${sData.local.length} vorhanden`;
        if (sData.missing.length) html += `, <span class="text-warn">${sData.missing.length} fehlend</span>`;
        html += ')</summary>';

        html += '<table class="data-table season-table"><thead><tr>';
        html += '<th>Nr</th><th>Titel</th><th>Aufl.</th><th>Codec</th><th>Typ</th><th>Audio</th><th>Aktion</th>';
        html += '</tr></thead><tbody>';

        const allEps = [];
        for (const ep of sData.local) allEps.push({...ep, _type: "local"});
        for (const ep of sData.missing) allEps.push({...ep, _type: "missing"});
        allEps.sort((a, b) => (a.episode_number || 0) - (b.episode_number || 0));

        // Redundante Dateien erkennen: gleiche Episode-Nummer mehrfach vorhanden
        // Die "beste" Datei behalten (kleinere Datei bei gleichem Codec, neueres Format bevorzugt)
        const epGroups = {};
        for (const ep of allEps) {
            if (ep._type !== "local" || !ep.episode_number) continue;
            const key = `${ep.season_number || 0}-${ep.episode_number}`;
            if (!epGroups[key]) epGroups[key] = [];
            epGroups[key].push(ep);
        }
        const redundantIds = new Set();
        const codecRank = {av1: 4, hevc: 3, h265: 3, h264: 2, x264: 2, mpeg4: 1, mpeg2video: 0};
        for (const key of Object.keys(epGroups)) {
            const group = epGroups[key];
            if (group.length <= 1) continue;
            // Sortiere: neuerer Codec besser, bei gleichem Codec kleinere Datei besser
            group.sort((a, b) => {
                const ra = codecRank[(a.video_codec || "").toLowerCase()] || 0;
                const rb = codecRank[(b.video_codec || "").toLowerCase()] || 0;
                if (ra !== rb) return rb - ra;
                return (a.file_size || 0) - (b.file_size || 0);
            });
            // Alle ausser dem ersten sind redundant
            for (let i = 1; i < group.length; i++) {
                redundantIds.add(group[i].id);
            }
        }

        for (const ep of allEps) {
            if (ep._type === "missing") {
                html += `<tr class="row-missing">
                    <td>${ep.episode_number || "-"}</td>
                    <td>${escapeHtml(ep.episode_name || "-")}</td>
                    <td colspan="4" class="text-muted">Nicht vorhanden</td>
                    <td><span class="status-badge error">FEHLT</span></td>
                </tr>`;
            } else {
                const isRedundant = redundantIds.has(ep.id);
                const audioInfo = (ep.audio_tracks || []).map(a => {
                    const lang = (a.lang || "?").toUpperCase().substring(0, 3);
                    return `<span class="tag">${lang} ${channelLayout(a.channels)}</span>`;
                }).join(" ");
                const res = ep.width && ep.height ? resolutionLabel(ep.width, ep.height) : "-";
                const epTitle = ep.episode_title || ep.file_name || "Episode";
                const fileExt = (ep.file_name || "").split(".").pop().toUpperCase() || "-";
                const redundantBadge = isRedundant ? ' <span class="status-badge warn" title="Duplikat - kann geloescht werden">REDUNDANT</span>' : '';
                html += `<tr data-video-id="${ep.id}" class="${isRedundant ? 'row-redundant' : ''}">
                    <td>${ep.episode_number || "-"}</td>
                    <td title="${escapeHtml(ep.file_name || '')}">${escapeHtml(epTitle)}${redundantBadge}</td>
                    <td>${res}</td>
                    <td><span class="tag codec">${ep.video_codec || "-"}</span></td>
                    <td><span class="tag">${fileExt}</span></td>
                    <td class="td-audio">${audioInfo || "-"}</td>
                    <td>
                        <button class="btn-small btn-play" onclick="playVideo(${ep.id}, '${escapeAttr(epTitle)}')" title="Abspielen">&#9654;</button>
                        <button class="btn-small btn-primary" onclick="convertVideo(${ep.id})">Conv</button>
                        <button class="btn-small btn-danger" onclick="deleteVideo(${ep.id}, '${escapeAttr(epTitle)}', 'series')" title="Loeschen">&#10005;</button>
                    </td>
                </tr>`;
            }
        }
        html += '</tbody></table></details>';
    }

    if (!sortedSeasons.length) html += '<div class="loading-msg">Keine Episoden gefunden</div>';
    body.innerHTML = html;
}

function loadCast() {
    const body = document.getElementById("series-modal-body");
    body.innerHTML = '<div class="loading-msg">Lade Darsteller...</div>';

    fetch(`/api/library/series/${currentSeriesId}/cast`)
        .then(r => r.json())
        .then(data => {
            const cast = data.cast || [];
            if (!cast.length) {
                body.innerHTML = '<div class="loading-msg">Keine Darsteller-Daten vorhanden</div>';
                return;
            }
            let html = '<div class="cast-grid">';
            for (const c of cast) {
                const img = c.person_image_url || c.image_url;
                const imgTag = img
                    ? `<img src="${img}" alt="" class="cast-photo" loading="lazy">`
                    : '<div class="cast-photo-placeholder">?</div>';
                html += `<div class="cast-card">
                    ${imgTag}
                    <div class="cast-info">
                        <strong>${escapeHtml(c.person_name)}</strong>
                        <span class="text-muted">${escapeHtml(c.character_name || "")}</span>
                    </div>
                </div>`;
            }
            html += '</div>';
            body.innerHTML = html;
        })
        .catch(() => { body.innerHTML = '<div class="loading-msg">Fehler</div>'; });
}

function loadArtworks() {
    const body = document.getElementById("series-modal-body");
    body.innerHTML = '<div class="loading-msg">Lade Bilder...</div>';

    fetch(`/api/library/series/${currentSeriesId}/artworks`)
        .then(r => r.json())
        .then(data => {
            const artworks = data.artworks || [];
            if (!artworks.length) {
                body.innerHTML = '<div class="loading-msg">Keine Bilder vorhanden</div>';
                return;
            }
            // Nach Typ gruppieren
            const groups = {};
            for (const a of artworks) {
                const t = a.artwork_type || "sonstige";
                if (!groups[t]) groups[t] = [];
                groups[t].push(a);
            }
            let html = '';
            for (const [type, items] of Object.entries(groups)) {
                html += `<h4 class="artwork-type-header">${escapeHtml(type.charAt(0).toUpperCase() + type.slice(1))} (${items.length})</h4>`;
                html += '<div class="artwork-gallery">';
                for (const a of items) {
                    const src = a.thumbnail_url || a.image_url;
                    if (!src) continue;
                    html += `<a href="${a.image_url}" target="_blank" class="artwork-item">
                        <img src="${src}" alt="${type}" loading="lazy">
                        ${a.width && a.height ? `<span class="artwork-size">${a.width}x${a.height}</span>` : ""}
                    </a>`;
                }
                html += '</div>';
            }
            body.innerHTML = html;
        })
        .catch(() => { body.innerHTML = '<div class="loading-msg">Fehler</div>'; });
}

function closeSeriesModal() {
    document.getElementById("series-modal").style.display = "none";
    currentSeriesId = null;
}

// === Serien-Aktionen ===

function tvdbRefresh() {
    if (!currentSeriesId) return;
    fetch(`/api/library/series/${currentSeriesId}/tvdb-refresh`, {method: "POST"})
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else { showToast("TVDB aktualisiert: " + (data.name || ""), "success"); openSeriesDetail(currentSeriesId); }
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

async function tvdbUnlink() {
    if (!currentSeriesId) return;
    if (!await showConfirm("TVDB-Zuordnung wirklich loesen?")) return;
    fetch(`/api/library/series/${currentSeriesId}/tvdb`, {method: "DELETE"})
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else { closeSeriesModal(); reloadAllSections(); }
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

function downloadMetadata() {
    if (!currentSeriesId) return;
    const btn = document.getElementById("btn-metadata-dl");
    btn.textContent = "Laden...";
    btn.disabled = true;
    fetch(`/api/library/series/${currentSeriesId}/metadata-download`, {method: "POST"})
        .then(r => r.json())
        .then(data => {
            btn.textContent = "Metadaten laden";
            btn.disabled = false;
            if (data.error) showToast("Fehler: " + data.error, "error");
            else showToast(`${data.downloaded || 0} Dateien heruntergeladen, ${data.errors || 0} Fehler`, "success");
        })
        .catch(e => { btn.textContent = "Metadaten laden"; btn.disabled = false; showToast("Fehler: " + e, "error"); });
}

async function deleteSeries(withFiles) {
    if (!currentSeriesId) return;
    if (withFiles) {
        if (!await showConfirm("Alle Dateien und Ordner werden <strong>UNWIDERRUFLICH</strong> geloescht!", {title: "Serie komplett loeschen", icon: "danger", okText: "Endgueltig loeschen", danger: true})) return;
    } else {
        if (!await showConfirm("Serie aus der Datenbank loeschen?", {detail: "Dateien bleiben erhalten", okText: "Aus DB loeschen", danger: true})) return;
    }
    const url = withFiles
        ? `/api/library/series/${currentSeriesId}?delete_files=1`
        : `/api/library/series/${currentSeriesId}`;
    fetch(url, {method: "DELETE"})
        .then(r => r.json())
        .then(data => {
            if (data.error) { showToast("Fehler: " + data.error, "error"); return; }
            let msg = "Serie aus DB geloescht.";
            if (data.deleted_folder) msg += " Ordner geloescht.";
            showToast(msg, "success");
            if (data.folder_error) showToast("Ordner-Fehler: " + data.folder_error, "error");
            closeSeriesModal();
            reloadAllSections();
            loadStats();
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

function showDeleteFolderDialog(folderPath, pathId, videoCount) {
    const folderName = folderPath.split('/').pop();
    document.getElementById("confirm-title").textContent = "Ordner loeschen";
    document.getElementById("confirm-icon").innerHTML = `
        <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#e74c3c" stroke-width="1.5">
            <polyline points="3 6 5 6 21 6"/>
            <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
            <line x1="10" y1="11" x2="10" y2="17"/>
            <line x1="14" y1="11" x2="14" y2="17"/>
        </svg>`;
    document.getElementById("confirm-message").innerHTML = `
        <strong>${escapeHtml(folderName)}</strong><br>
        wirklich loeschen?`;
    document.getElementById("confirm-detail").innerHTML = `
        ${videoCount} Video${videoCount !== 1 ? 's' : ''} werden unwiderruflich geloescht.<br>
        <span style="color:#e74c3c">Dieser Vorgang kann nicht rueckgaengig gemacht werden!</span>`;
    document.getElementById("confirm-btn-ok").textContent = "Endgueltig loeschen";
    document.getElementById("confirm-modal").style.display = "flex";

    pendingConfirmAction = () => executeDeleteFolder(folderPath, pathId);
}

function executeDeleteFolder(folderPath, pathId) {
    fetch("/api/library/delete-folder", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({folder_path: folderPath})
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                return;
            }
            const msg = `${data.deleted_files || 0} Dateien geloescht`;
            showToast(msg, "success");
            if (pathId) loadSectionData(pathId);
            loadStats();
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Film-Detail ===

function openMovieDetail(movieId) {
    if (event) event.stopPropagation();
    currentMovieId = movieId;
    document.getElementById("movie-modal").style.display = "flex";

    fetch(`/api/library/movies/${movieId}`)
        .then(r => r.json())
        .then(data => {
            document.getElementById("movie-modal-title").textContent =
                data.title || data.folder_name;
            document.getElementById("movie-modal-genres").textContent =
                data.genres || "";
            // Aktions-Buttons
            document.getElementById("btn-movie-tvdb-unlink").style.display =
                data.tvdb_id ? "" : "none";

            let html = '<div class="movie-detail-header">';
            if (data.poster_url) {
                html += `<img src="${data.poster_url}" alt="" class="movie-detail-poster">`;
            }
            html += '<div class="movie-detail-info">';
            if (data.overview) html += `<p class="series-overview">${escapeHtml(data.overview)}</p>`;
            html += '<div class="series-detail-meta">';
            if (data.year) html += `<span class="tag">${data.year}</span>`;
            if (data.runtime) html += `<span class="tag">${data.runtime} min</span>`;
            if (data.status) html += `<span class="tag">${data.status}</span>`;
            html += `<span class="tag">${data.video_count || 0} Dateien</span>`;
            if (data.total_size) html += `<span class="tag">${formatSize(data.total_size)}</span>`;
            html += '</div></div></div>';

            // Video-Dateien des Films
            const videos = data.videos || [];
            if (videos.length) {
                html += '<h4 style="margin:1rem 0 0.5rem;font-size:0.9rem;color:#fff">Video-Dateien</h4>';
                html += '<table class="data-table"><thead><tr>';
                html += '<th>Datei</th><th>Aufl.</th><th>Codec</th><th>Typ</th><th>Audio</th><th>Groesse</th><th>Dauer</th><th>Aktion</th>';
                html += '</tr></thead><tbody>';
                for (const v of videos) {
                    const audioInfo = (v.audio_tracks || []).map(a => {
                        const lang = (a.lang || "?").toUpperCase().substring(0, 3);
                        return `<span class="tag">${lang} ${channelLayout(a.channels)}</span>`;
                    }).join(" ");
                    const res = v.width && v.height ? resolutionLabel(v.width, v.height) : "-";
                    const movieTitle = v.file_name || "Video";
                    const fileExt = (v.file_name || "").split(".").pop().toUpperCase() || "-";
                    html += `<tr data-video-id="${v.id}">
                        <td class="td-name" title="${escapeHtml(v.file_path || '')}">${escapeHtml(v.file_name || "-")}</td>
                        <td>${res}${v.is_10bit ? ' <span class="tag hdr">10bit</span>' : ''}</td>
                        <td><span class="tag codec">${v.video_codec || "-"}</span></td>
                        <td><span class="tag">${fileExt}</span></td>
                        <td class="td-audio">${audioInfo || "-"}</td>
                        <td>${formatSize(v.file_size || 0)}</td>
                        <td>${formatDuration(v.duration_sec || 0)}</td>
                        <td>
                            <button class="btn-small btn-play" onclick="playVideo(${v.id}, '${escapeAttr(movieTitle)}')" title="Abspielen">&#9654;</button>
                            <button class="btn-small btn-primary" onclick="convertVideo(${v.id})">Conv</button>
                            <button class="btn-small btn-danger" onclick="deleteVideo(${v.id}, '${escapeAttr(movieTitle)}', 'movie')" title="Loeschen">&#10005;</button>
                        </td>
                    </tr>`;
                }
                html += '</tbody></table>';
            }

            document.getElementById("movie-modal-body").innerHTML = html;
        })
        .catch(() => {
            document.getElementById("movie-modal-body").innerHTML =
                '<div class="loading-msg">Fehler beim Laden</div>';
        });
}

function closeMovieModal() {
    document.getElementById("movie-modal").style.display = "none";
    currentMovieId = null;
}

async function movieTvdbUnlink() {
    if (!currentMovieId) return;
    if (!await showConfirm("TVDB-Zuordnung wirklich loesen?", {title: "TVDB trennen", okText: "Trennen"})) return;
    fetch(`/api/library/movies/${currentMovieId}/tvdb`, {method: "DELETE"})
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else { closeMovieModal(); reloadAllSections(); }
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

async function deleteMovie(withFiles) {
    if (!currentMovieId) return;
    if (withFiles) {
        if (!await showConfirm("Film komplett loeschen?<br><br>Alle Dateien werden <strong>UNWIDERRUFLICH</strong> geloescht!", {title: "Film loeschen", okText: "Endgueltig loeschen", icon: "danger", danger: true})) return;
        if (!await showConfirm("Wirklich sicher?", {title: "Letzte Warnung", okText: "Ja, loeschen", icon: "danger", danger: true})) return;
    } else {
        if (!await showConfirm("Film aus der Datenbank loeschen?<br>(Dateien bleiben erhalten)", {title: "Film entfernen", okText: "Aus DB loeschen"})) return;
    }
    const url = withFiles
        ? `/api/library/movies/${currentMovieId}?delete_files=1`
        : `/api/library/movies/${currentMovieId}`;
    fetch(url, {method: "DELETE"})
        .then(r => r.json())
        .then(data => {
            if (data.error) { showToast("Fehler: " + data.error, "error"); return; }
            showToast("Film aus DB geloescht." + (data.deleted_folder ? " Ordner geloescht." : ""), "success");
            closeMovieModal();
            reloadAllSections();
            loadStats();
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Film-TVDB-Zuordnung ===

function openMovieTvdbModal(movieId, title) {
    document.getElementById("movie-tvdb-modal").style.display = "flex";
    document.getElementById("movie-tvdb-id").value = movieId;
    document.getElementById("movie-tvdb-search-input").value = cleanSearchTitle(title);
    document.getElementById("movie-tvdb-results").innerHTML = "";
    searchMovieTvdb();
}

function closeMovieTvdbModal() {
    document.getElementById("movie-tvdb-modal").style.display = "none";
}

function searchMovieTvdb() {
    const query = document.getElementById("movie-tvdb-search-input").value.trim();
    if (!query) return;

    const results = document.getElementById("movie-tvdb-results");
    results.innerHTML = '<div class="loading-msg">Suche...</div>';

    fetch(`/api/tvdb/search-movies?q=${encodeURIComponent(query)}`)
        .then(r => r.json())
        .then(data => {
            if (data.error) { results.innerHTML = `<div class="loading-msg">${escapeHtml(data.error)}</div>`; return; }
            if (!data.results || !data.results.length) { results.innerHTML = '<div class="loading-msg">Keine Ergebnisse</div>'; return; }
            results.innerHTML = data.results.map(r => `
                <div class="tvdb-result" onclick="matchMovieTvdb(${r.tvdb_id})">
                    ${r.poster ? `<img src="${r.poster}" alt="" class="tvdb-thumb">` : ""}
                    <div>
                        <strong>${escapeHtml(r.name)}</strong>
                        <span class="text-muted">${r.year || ""}</span>
                        <p class="tvdb-overview">${escapeHtml((r.overview || "").substring(0, 150))}</p>
                    </div>
                </div>
            `).join("");
        })
        .catch(e => { results.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

function matchMovieTvdb(tvdbId) {
    const movieId = document.getElementById("movie-tvdb-id").value;
    const results = document.getElementById("movie-tvdb-results");
    results.innerHTML = '<div class="loading-msg">Verknuepfe...</div>';

    fetch(`/api/library/movies/${movieId}/tvdb-match`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({tvdb_id: tvdbId}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) { results.innerHTML = `<div class="loading-msg">${escapeHtml(data.error)}</div>`; }
            else { closeMovieTvdbModal(); reloadAllSections(); }
        })
        .catch(e => { results.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

let movieTvdbSearchTimer = null;
function debounceMovieTvdbSearch() {
    if (movieTvdbSearchTimer) clearTimeout(movieTvdbSearchTimer);
    movieTvdbSearchTimer = setTimeout(searchMovieTvdb, 500);
}

// === Filter ===

let filterPresets = {};
let defaultView = "all";

function buildFilterParams() {
    const params = new URLSearchParams();
    const search = document.getElementById("filter-search").value.trim();
    if (search) params.set("search", search);
    const codec = document.getElementById("filter-codec").value;
    if (codec) params.set("video_codec", codec);
    const container = document.getElementById("filter-container").value;
    if (container) params.set("container", container);
    const audioLang = document.getElementById("filter-audio-lang").value;
    if (audioLang) params.set("audio_lang", audioLang);
    const audioCh = document.getElementById("filter-audio-ch").value;
    if (audioCh) params.set("audio_channels", audioCh);
    if (document.getElementById("filter-10bit").checked) params.set("is_10bit", "1");
    if (document.getElementById("filter-not-converted").checked) params.set("not_converted", "1");
    const resolution = document.getElementById("filter-resolution").value;
    if (resolution) params.set("min_width", resolution);
    const sort = document.getElementById("filter-sort").value;
    if (sort) params.set("sort", sort);
    const order = document.getElementById("filter-order").value;
    if (order) params.set("order", order);
    return params;
}

function applyFilters() {
    // Preset-Auswahl zuruecksetzen wenn manuell gefiltert wird
    document.getElementById("filter-preset").value = "";
    _saveUIState();
    for (const pid of Object.keys(sectionStates)) {
        sectionStates[pid].page = 1;
        loadSectionData(parseInt(pid));
    }
}

function debounceFilter() {
    if (filterTimeout) clearTimeout(filterTimeout);
    filterTimeout = setTimeout(applyFilters, 400);
}

// Filter-Presets laden
async function loadFilterPresets() {
    try {
        const resp = await fetch("/api/library/filter-presets");
        const data = await resp.json();
        filterPresets = data.presets || {};
        defaultView = data.default_view || "all";

        // Preset-Dropdown befuellen
        const select = document.getElementById("filter-preset");
        // Bestehende Custom-Presets entfernen (4 feste Optionen behalten: Alle, Nicht konvertiert, Alte Formate, Fehlende Episoden)
        while (select.options.length > 4) {
            select.remove(4);
        }
        // Gespeicherte Presets hinzufuegen
        for (const [id, preset] of Object.entries(filterPresets)) {
            const opt = document.createElement("option");
            opt.value = id;
            opt.textContent = preset.name || id;
            select.appendChild(opt);
        }

        // Gespeicherten Filter-State wiederherstellen (hat Vorrang)
        _restoreFilterState();

        // Standard-Ansicht nur beim ersten Besuch anwenden
        if (!localStorage.getItem("vk_filterState") && defaultView && defaultView !== "all") {
            applyPreset(defaultView);
        }
    } catch (e) {
        console.error("Filter-Presets laden fehlgeschlagen:", e);
    }
}

// Preset anwenden
let showMissingMode = false;

function applyPreset(presetId) {
    const id = presetId || document.getElementById("filter-preset").value;

    // Alle Filter zuruecksetzen
    resetFiltersQuiet();
    showMissingMode = false;

    if (!id) {
        // "Alle anzeigen"
        _updateDeletePresetBtn();
        applyFilters();
        return;
    }

    // Eingebaute Presets
    if (id === "not_converted") {
        document.getElementById("filter-not-converted").checked = true;
    } else if (id === "old_formats") {
        // Alte Formate = alles ausser AV1
        document.getElementById("filter-codec").value = "";
        document.getElementById("filter-not-converted").checked = true;
    } else if (id === "missing_episodes") {
        // Fehlende Episoden - spezieller Modus
        showMissingMode = true;
        loadMissingEpisodes();
        document.getElementById("filter-preset").value = id;
        return;
    } else if (filterPresets[id]) {
        // Custom Preset
        const p = filterPresets[id];
        if (p.video_codec) document.getElementById("filter-codec").value = p.video_codec;
        if (p.container) document.getElementById("filter-container").value = p.container;
        if (p.min_width) document.getElementById("filter-resolution").value = p.min_width;
        if (p.audio_lang) document.getElementById("filter-audio-lang").value = p.audio_lang;
        if (p.is_10bit) document.getElementById("filter-10bit").checked = true;
        if (p.not_converted) document.getElementById("filter-not-converted").checked = true;
        if (p.show_missing) {
            showMissingMode = true;
            loadMissingEpisodes();
            document.getElementById("filter-preset").value = id;
            return;
        }
    }

    document.getElementById("filter-preset").value = id;
    _updateDeletePresetBtn();
    _saveUIState();

    for (const pid of Object.keys(sectionStates)) {
        sectionStates[pid].page = 1;
        loadSectionData(parseInt(pid));
    }
}

// Fehlende Episoden laden und anzeigen
let missingPage = 1;
async function loadMissingEpisodes(page = 1) {
    missingPage = page;
    const container = document.getElementById("library-content");
    container.innerHTML = '<div class="loading-msg">Lade fehlende Episoden...</div>';

    try {
        const resp = await fetch(`/api/library/missing-episodes?page=${page}&limit=50`);
        const data = await resp.json();

        if (!data.items || data.items.length === 0) {
            container.innerHTML = '<div class="loading-msg">Keine fehlenden Episoden gefunden. Alle Serien sind vollstaendig!</div>';
            return;
        }

        // Gruppiere nach Serie
        const bySeries = {};
        for (const ep of data.items) {
            const key = ep.series_id;
            if (!bySeries[key]) {
                bySeries[key] = {
                    series_id: ep.series_id,
                    series_title: ep.series_title,
                    poster_url: ep.poster_url,
                    episodes: []
                };
            }
            bySeries[key].episodes.push(ep);
        }

        let html = `<div class="missing-episodes-view">
            <h3>Fehlende Episoden (${data.total} insgesamt)</h3>`;

        for (const series of Object.values(bySeries)) {
            html += `<div class="missing-series-block">
                <div class="missing-series-header">
                    ${series.poster_url ? `<img src="${series.poster_url}" class="missing-poster" alt="">` : ''}
                    <h4>${series.series_title}</h4>
                    <span class="missing-count">${series.episodes.length} fehlend</span>
                </div>
                <div class="missing-episodes-list">`;

            for (const ep of series.episodes) {
                const aired = ep.aired ? ` (${ep.aired})` : '';
                html += `<div class="missing-episode">
                    <span class="ep-num">S${String(ep.season_number).padStart(2,'0')}E${String(ep.episode_number).padStart(2,'0')}</span>
                    <span class="ep-name">${ep.episode_name || 'Unbekannt'}${aired}</span>
                </div>`;
            }

            html += `</div></div>`;
        }

        // Pagination
        if (data.pages > 1) {
            html += '<div class="missing-pagination">';
            if (page > 1) {
                html += `<button class="btn-small" onclick="loadMissingEpisodes(${page - 1})">Zurueck</button>`;
            }
            html += ` Seite ${page} von ${data.pages} `;
            if (page < data.pages) {
                html += `<button class="btn-small" onclick="loadMissingEpisodes(${page + 1})">Weiter</button>`;
            }
            html += '</div>';
        }

        html += '</div>';
        container.innerHTML = html;

    } catch (e) {
        container.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`;
    }
}

// Filter zuruecksetzen (ohne Reload)
function resetFiltersQuiet() {
    document.getElementById("filter-search").value = "";
    document.getElementById("filter-codec").value = "";
    document.getElementById("filter-container").value = "";
    document.getElementById("filter-resolution").value = "";
    document.getElementById("filter-audio-lang").value = "";
    document.getElementById("filter-audio-ch").value = "";
    document.getElementById("filter-10bit").checked = false;
    document.getElementById("filter-not-converted").checked = false;
    document.getElementById("filter-sort").value = "file_name";
    document.getElementById("filter-order").value = "asc";
}

// Filter zuruecksetzen (mit Reload)
function resetFilters() {
    resetFiltersQuiet();
    document.getElementById("filter-preset").value = "";
    applyFilters();
}

// Aktuellen Filter als Preset speichern
async function saveCurrentFilter() {
    const name = await showPrompt("Name fuer diesen Filter:", {title: "Filter speichern", placeholder: "z.B. Nur 4K", okText: "Speichern"});
    if (!name) return;

    const id = name.toLowerCase().replace(/[^a-z0-9]/g, "_");
    const filters = {};

    const codec = document.getElementById("filter-codec").value;
    if (codec) filters.video_codec = codec;
    const container = document.getElementById("filter-container").value;
    if (container) filters.container = container;
    const resolution = document.getElementById("filter-resolution").value;
    if (resolution) filters.min_width = resolution;
    const audioLang = document.getElementById("filter-audio-lang").value;
    if (audioLang) filters.audio_lang = audioLang;
    if (document.getElementById("filter-10bit").checked) filters.is_10bit = true;
    if (document.getElementById("filter-not-converted").checked) filters.not_converted = true;

    try {
        const resp = await fetch("/api/library/filter-presets", {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({id, name, filters}),
        });
        if (resp.ok) {
            showToast(`Filter "${name}" gespeichert`, "success");
            loadFilterPresets();
        } else {
            showToast("Fehler beim Speichern", "error");
        }
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

// Aktuellen Filter als Standard setzen
async function setAsDefault() {
    const presetId = document.getElementById("filter-preset").value || "all";
    try {
        const resp = await fetch("/api/library/default-view", {
            method: "PUT",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({default_view: presetId}),
        });
        if (resp.ok) {
            defaultView = presetId;
            showToast("Standard-Ansicht gespeichert", "success");
        } else {
            showToast("Fehler beim Speichern", "error");
        }
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

// Aktuelles Custom-Preset loeschen
async function deleteCurrentPreset() {
    const select = document.getElementById("filter-preset");
    const presetId = select.value;
    // Nur Custom-Presets loeschen (nicht die eingebauten)
    const builtIn = ["", "not_converted", "old_formats", "missing_episodes"];
    if (builtIn.includes(presetId)) return;

    if (!await showConfirm(`Filter "${escapeHtml(select.options[select.selectedIndex].text)}" wirklich loeschen?`, {title: "Filter loeschen", okText: "Loeschen", icon: "danger", danger: true})) return;

    try {
        const resp = await fetch(`/api/library/filter-presets/${presetId}`, {
            method: "DELETE",
        });
        if (resp.ok) {
            showToast("Filter geloescht", "success");
            select.value = "";
            applyPreset("");
            loadFilterPresets();
        } else {
            showToast("Fehler beim Loeschen", "error");
        }
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

// Loeschen-Button je nach Preset ein-/ausblenden
function _updateDeletePresetBtn() {
    const btn = document.getElementById("btn-delete-preset");
    if (!btn) return;
    const presetId = document.getElementById("filter-preset").value;
    const builtIn = ["", "not_converted", "old_formats", "missing_episodes"];
    btn.style.display = builtIn.includes(presetId) ? "none" : "";
}

// === Scan ===

function startScan() {
    fetch("/api/library/scan", {method: "POST"})
        .catch(e => console.error("Scan-Fehler:", e));
    // Progress-Updates kommen via WebSocket -> globaler Progress-Balken in base.html
}

function scanSinglePath(pathId) {
    fetch(`/api/library/scan/${pathId}`, {method: "POST"})
        .catch(e => console.error("Scan-Fehler:", e));
    // Progress-Updates kommen via WebSocket -> globaler Progress-Balken in base.html
}

function reloadAllSections() {
    for (const pid of Object.keys(sectionStates)) {
        loadSectionData(parseInt(pid));
    }
}

// === TVDB Auto-Match (Review-Modus) ===

let tvdbReviewData = []; // Vorschlaege die noch geprueft werden muessen

async function startAutoMatch() {
    if (!await showConfirm("TVDB-Vorschlaege fuer alle nicht-zugeordneten Serien und Filme sammeln?", {title: "Auto-Match starten", detail: "Das kann einige Minuten dauern. Du kannst danach jeden Vorschlag pruefen und bestaetigen.", okText: "Starten", icon: "info"})) return;

    _gpShow("automatch", "Auto-Match", "Suche TVDB-Vorschlaege...", 0);

    fetch("/api/library/tvdb-auto-match?type=all", {method: "POST"})
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                _gpShow("automatch", "Auto-Match", "Fehler: " + data.error, 0);
                _gpHideDelayed("automatch");
                return;
            }
            pollAutoMatchStatus();
        })
        .catch(e => {
            _gpShow("automatch", "Auto-Match", "Fehler: " + e, 0);
            _gpHideDelayed("automatch");
        });
}

function pollAutoMatchStatus() {
    const interval = setInterval(() => {
        fetch("/api/library/tvdb-auto-match-status")
            .then(r => r.json())
            .then(data => {
                if (data.phase === "done") {
                    clearInterval(interval);
                    const suggestions = data.suggestions || [];
                    const withSuggestions = suggestions.filter(s => s.suggestions && s.suggestions.length > 0);
                    const noResults = suggestions.length - withSuggestions.length;
                    _gpShow("automatch", "Auto-Match",
                        withSuggestions.length + " Vorschlaege, " + noResults + " ohne Ergebnis", 100);
                    _gpHideDelayed("automatch");

                    if (withSuggestions.length > 0) {
                        openTvdbReviewModal(withSuggestions);
                    }
                } else if (data.phase === "error") {
                    clearInterval(interval);
                    _gpShow("automatch", "Auto-Match", "Fehler", 0);
                    _gpHideDelayed("automatch");
                } else if (!data.active && data.phase !== "done") {
                    clearInterval(interval);
                    _gpHide("automatch");
                } else {
                    const pct = data.total > 0 ? Math.round((data.done / data.total) * 100) : 0;
                    const phase = data.phase === "series" ? "Serien" : "Filme";
                    _gpShow("automatch", "Auto-Match",
                        phase + ": " + (data.current || "") + " (" + data.done + "/" + data.total + ")", pct);
                }
            })
            .catch(() => clearInterval(interval));
    }, 5000);
}

// === TVDB Review-Modal ===

function openTvdbReviewModal(suggestions) {
    tvdbReviewData = suggestions;
    document.getElementById("tvdb-review-modal").style.display = "flex";
    renderTvdbReviewList();
}

function closeTvdbReviewModal() {
    document.getElementById("tvdb-review-modal").style.display = "none";
    tvdbReviewData = [];
    reloadAllSections();
    loadStats();
}

function renderTvdbReviewList() {
    const list = document.getElementById("tvdb-review-list");
    const remaining = tvdbReviewData.filter(item => !item._confirmed && !item._skipped);
    const confirmed = tvdbReviewData.filter(item => item._confirmed);
    const skipped = tvdbReviewData.filter(item => item._skipped);

    document.getElementById("tvdb-review-info").textContent =
        `${remaining.length} offen, ${confirmed.length} zugeordnet, ${skipped.length} uebersprungen`;

    if (!tvdbReviewData.length) {
        list.innerHTML = '<div class="loading-msg">Keine Vorschlaege vorhanden</div>';
        return;
    }

    let html = '';
    for (let i = 0; i < tvdbReviewData.length; i++) {
        const item = tvdbReviewData[i];
        const typeLabel = item.type === "series" ? "Serie" : "Film";
        const typeClass = item.type === "series" ? "tag-series" : "tag-movie";

        // Status-Klasse
        let statusClass = "";
        let statusHtml = "";
        if (item._confirmed) {
            statusClass = "review-item-done";
            statusHtml = `<span class="status-badge ok">Zugeordnet: ${escapeHtml(item._confirmedName || "")}</span>`;
        } else if (item._skipped) {
            statusClass = "review-item-skipped";
            statusHtml = '<span class="status-badge">Uebersprungen</span>';
        }

        html += `<div class="review-item ${statusClass}" id="review-item-${i}">`;
        html += `<div class="review-item-header">`;
        html += `<span class="tag ${typeClass}">${typeLabel}</span>`;
        html += `<strong class="review-local-name">${escapeHtml(item.local_name)}</strong>`;
        if (item.year) html += `<span class="text-muted">(${item.year})</span>`;
        if (statusHtml) html += statusHtml;
        if (!item._confirmed && !item._skipped) {
            html += `<button class="btn-small btn-secondary review-skip-btn" onclick="skipReviewItem(${i})">Ueberspringen</button>`;
            html += `<button class="btn-small btn-secondary review-search-btn" onclick="manualTvdbSearchReview(${i})">Manuell suchen</button>`;
        }
        html += `</div>`;

        // Vorschlaege anzeigen (nur wenn noch nicht bestaetigt/uebersprungen)
        if (!item._confirmed && !item._skipped) {
            html += `<div class="review-suggestions">`;
            if (!item.suggestions || !item.suggestions.length) {
                html += '<span class="text-muted">Keine Vorschlaege gefunden</span>';
            } else {
                for (const s of item.suggestions) {
                    const poster = s.poster
                        ? `<img src="${s.poster}" alt="" class="review-poster" loading="lazy">`
                        : '<div class="review-poster-placeholder">?</div>';
                    html += `<div class="review-suggestion" onclick="confirmReviewItem(${i}, ${s.tvdb_id}, '${escapeAttr(s.name)}')">`;
                    html += poster;
                    html += `<div class="review-suggestion-info">`;
                    html += `<strong>${escapeHtml(s.name)}</strong>`;
                    if (s.year) html += ` <span class="text-muted">(${s.year})</span>`;
                    if (s.overview) html += `<p class="review-overview">${escapeHtml(s.overview)}</p>`;
                    html += `</div>`;
                    html += `</div>`;
                }
            }
            // Manuelles Suchfeld (versteckt, wird bei Klick auf "Manuell suchen" angezeigt)
            html += `<div class="review-manual-search" id="review-manual-${i}" style="display:none">`;
            html += `<input type="text" class="review-search-input" id="review-search-input-${i}" placeholder="${item.type === 'series' ? 'Serienname' : 'Filmname'}..." value="${escapeHtml(item.local_name)}" onkeydown="if(event.key==='Enter')executeManualReviewSearch(${i})">`;
            html += `<button class="btn-small btn-primary" onclick="executeManualReviewSearch(${i})">Suchen</button>`;
            html += `<div id="review-search-results-${i}" class="review-search-results"></div>`;
            html += `</div>`;
            html += `</div>`;
        }

        html += `</div>`;
    }
    list.innerHTML = html;
}

function confirmReviewItem(index, tvdbId, name) {
    const item = tvdbReviewData[index];
    if (item._confirmed || item._skipped) return;

    // Visuelles Feedback
    const el = document.getElementById("review-item-" + index);
    if (el) el.classList.add("review-item-loading");

    fetch("/api/library/tvdb-confirm", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({id: item.id, type: item.type, tvdb_id: tvdbId}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                if (el) el.classList.remove("review-item-loading");
                return;
            }
            item._confirmed = true;
            item._confirmedName = data.name || name;
            renderTvdbReviewList();
        })
        .catch(e => {
            showToast("Fehler: " + e, "error");
            if (el) el.classList.remove("review-item-loading");
        });
}

function skipReviewItem(index) {
    tvdbReviewData[index]._skipped = true;
    renderTvdbReviewList();
}

function skipAllReviewItems() {
    for (const item of tvdbReviewData) {
        if (!item._confirmed) item._skipped = true;
    }
    renderTvdbReviewList();
}

function manualTvdbSearchReview(index) {
    const el = document.getElementById("review-manual-" + index);
    if (el) {
        el.style.display = el.style.display === "none" ? "flex" : "none";
    }
}

function executeManualReviewSearch(index) {
    const item = tvdbReviewData[index];
    const input = document.getElementById("review-search-input-" + index);
    const results = document.getElementById("review-search-results-" + index);
    const query = input ? input.value.trim() : "";
    if (!query) return;

    results.innerHTML = '<div class="loading-msg" style="padding:0.3rem">Suche...</div>';

    const endpoint = item.type === "series"
        ? `/api/tvdb/search?q=${encodeURIComponent(query)}`
        : `/api/tvdb/search-movies?q=${encodeURIComponent(query)}`;

    fetch(endpoint)
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                results.innerHTML = `<div class="loading-msg">${escapeHtml(data.error)}</div>`;
                return;
            }
            const list = data.results || [];
            if (!list.length) {
                results.innerHTML = '<div class="loading-msg">Keine Ergebnisse</div>';
                return;
            }
            results.innerHTML = list.map(r => `
                <div class="review-suggestion" onclick="confirmReviewItem(${index}, ${r.tvdb_id}, '${escapeAttr(r.name)}')">
                    ${r.poster ? `<img src="${r.poster}" alt="" class="review-poster" loading="lazy">` : '<div class="review-poster-placeholder">?</div>'}
                    <div class="review-suggestion-info">
                        <strong>${escapeHtml(r.name)}</strong>
                        ${r.year ? `<span class="text-muted">(${r.year})</span>` : ""}
                        <p class="review-overview">${escapeHtml((r.overview || "").substring(0, 120))}</p>
                    </div>
                </div>
            `).join("");
        })
        .catch(e => { results.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

// === TVDB Modal ===

function openTvdbModal(seriesId, folderName) {
    document.getElementById("tvdb-modal").style.display = "flex";
    document.getElementById("tvdb-series-id").value = seriesId;
    document.getElementById("tvdb-search-input").value = cleanSearchTitle(folderName);
    document.getElementById("tvdb-results").innerHTML = "";
    // Checkbox zuruecksetzen
    const engCheckbox = document.getElementById("tvdb-search-english");
    if (engCheckbox) engCheckbox.checked = false;
    searchTvdb();
}

function closeTvdbModal() {
    document.getElementById("tvdb-modal").style.display = "none";
}

function searchTvdb() {
    const query = document.getElementById("tvdb-search-input").value.trim();
    if (!query) return;

    const results = document.getElementById("tvdb-results");
    results.innerHTML = '<div class="loading-msg">Suche...</div>';

    // Sprache: eng wenn Checkbox aktiv, sonst Standard (deu)
    const useEnglish = document.getElementById("tvdb-search-english")?.checked;
    const langParam = useEnglish ? "&lang=eng" : "";

    fetch(`/api/tvdb/search?q=${encodeURIComponent(query)}${langParam}`)
        .then(r => r.json())
        .then(data => {
            if (data.error) { results.innerHTML = `<div class="loading-msg">${escapeHtml(data.error)}</div>`; return; }
            if (!data.results || !data.results.length) { results.innerHTML = '<div class="loading-msg">Keine Ergebnisse</div>'; return; }
            results.innerHTML = data.results.map(r => {
                // Zeige Original-Namen wenn vorhanden und unterschiedlich
                const origName = r.original_name && r.original_name !== r.name
                    ? `<span class="text-muted" style="font-size:0.85em">(${escapeHtml(r.original_name)})</span>`
                    : "";
                return `
                <div class="tvdb-result" onclick="matchTvdb(${r.tvdb_id})">
                    ${r.poster ? `<img src="${r.poster}" alt="" class="tvdb-thumb">` : ""}
                    <div>
                        <strong>${escapeHtml(r.name)}</strong> ${origName}
                        <span class="text-muted">${r.year || ""}</span>
                        <p class="tvdb-overview">${escapeHtml((r.overview || "").substring(0, 150))}</p>
                    </div>
                </div>
            `}).join("");
        })
        .catch(e => { results.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

function matchTvdb(tvdbId) {
    const seriesId = document.getElementById("tvdb-series-id").value;
    const results = document.getElementById("tvdb-results");
    results.innerHTML = '<div class="loading-msg">Verknuepfe...</div>';

    fetch(`/api/library/series/${seriesId}/tvdb-match`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({tvdb_id: tvdbId}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) { results.innerHTML = `<div class="loading-msg">${escapeHtml(data.error)}</div>`; }
            else { closeTvdbModal(); reloadAllSections(); }
        })
        .catch(e => { results.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

let tvdbSearchTimer = null;
function debounceTvdbSearch() {
    if (tvdbSearchTimer) clearTimeout(tvdbSearchTimer);
    tvdbSearchTimer = setTimeout(searchTvdb, 500);
}

// === Konvertierung ===

async function convertVideo(videoId) {
    if (event) event.stopPropagation();
    if (!await showConfirm("Video zur Konvertierung senden?", {title: "Konvertieren", okText: "Starten", icon: "info"})) return;

    fetch(`/api/library/videos/${videoId}/convert`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else showToast(data.message || "Job erstellt", "success");
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Serie komplett konvertieren ===

function openConvertSeriesModal() {
    if (!currentSeriesId) return;

    document.getElementById("convert-series-modal").style.display = "flex";
    document.getElementById("convert-series-status").innerHTML =
        '<div class="loading-msg">Lade Codec-Status...</div>';

    // Codec-Status laden
    fetch(`/api/library/series/${currentSeriesId}/convert-status`)
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                document.getElementById("convert-series-status").innerHTML =
                    `<div class="loading-msg">${escapeHtml(data.error)}</div>`;
                return;
            }

            let html = `<div style="margin-bottom:0.5rem"><strong>${data.total} Episoden</strong></div>`;
            html += '<div class="codec-stats">';
            for (const [codec, count] of Object.entries(data.codec_counts || {})) {
                const isTarget = codec.includes("av1") || codec.includes("hevc");
                const cls = isTarget ? "tag ok" : "tag";
                html += `<span class="${cls}">${codec}: ${count}</span> `;
            }
            html += '</div>';
            document.getElementById("convert-series-status").innerHTML = html;
        })
        .catch(e => {
            document.getElementById("convert-series-status").innerHTML =
                `<div class="loading-msg">Fehler: ${e}</div>`;
        });
}

function closeConvertSeriesModal() {
    document.getElementById("convert-series-modal").style.display = "none";
}

function executeConvertSeries() {
    if (!currentSeriesId) return;

    const targetCodec = document.getElementById("convert-target-codec").value;
    const forceAll = document.getElementById("convert-force-all").checked;
    const deleteOld = document.getElementById("convert-delete-old").checked;

    const btn = document.querySelector("#convert-series-modal .btn-primary");
    btn.textContent = "Wird gestartet...";
    btn.disabled = true;

    fetch(`/api/library/series/${currentSeriesId}/convert`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({
            target_codec: targetCodec,
            force_all: forceAll,
            delete_old: deleteOld,
        }),
    })
        .then(r => r.json())
        .then(data => {
            btn.textContent = "Konvertierung starten";
            btn.disabled = false;

            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                return;
            }

            let msg = data.message || "Konvertierung gestartet";
            if (data.already_done > 0) {
                msg += ` (${data.already_done} bereits im Zielformat)`;
            }
            showToast(msg, "success");
            closeConvertSeriesModal();
        })
        .catch(e => {
            btn.textContent = "Konvertierung starten";
            btn.disabled = false;
            showToast("Fehler: " + e, "error");
        });
}

// === Serien-Ordner aufraeumen ===

async function cleanupSeriesFolder() {
    if (!currentSeriesId) return;

    if (!await showConfirm("Alle Dateien im Serien-Ordner loeschen, die <strong>NICHT</strong> in der Bibliothek sind?", {
        title: "Ordner aufraeumen",
        detail: "Behalten werden:<br>- Alle Videos in der Bibliothek<br>- .metadata Ordner<br>- .nfo, .jpg, .png Dateien<br><br><span style='color:#e74c3c'>Dies kann nicht rueckgaengig gemacht werden!</span>",
        okText: "Aufraeumen",
        icon: "danger",
        danger: true
    })) return;

    fetch(`/api/library/series/${currentSeriesId}/cleanup`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                return;
            }
            let msg = `${data.deleted} Dateien geloescht.`;
            if (data.errors > 0) msg += ` ${data.errors} Fehler.`;
            showToast(msg, data.errors > 0 ? "warning" : "success");
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Duplikate ===

function showDuplicates() {
    document.getElementById("duplicates-modal").style.display = "flex";
    const list = document.getElementById("duplicates-list");
    list.innerHTML = '<div class="loading-msg">Suche Duplikate...</div>';

    fetch("/api/library/duplicates")
        .then(r => r.json())
        .then(data => {
            const dupes = data.duplicates || [];
            if (!dupes.length) { list.innerHTML = '<div class="loading-msg">Keine Duplikate gefunden</div>'; return; }
            list.innerHTML = dupes.map(d => `
                <div class="duplicate-pair">
                    <div class="dupe-item">
                        <span class="dupe-name">${escapeHtml(d.name1)}</span>
                        <span class="tag codec">${d.codec1}</span>
                        <span class="tag">${d.width1}x${d.height1}</span>
                        <span class="text-muted">${formatSize(d.size1)}</span>
                    </div>
                    <div class="dupe-vs">vs</div>
                    <div class="dupe-item">
                        <span class="dupe-name">${escapeHtml(d.name2)}</span>
                        <span class="tag codec">${d.codec2}</span>
                        <span class="tag">${d.width2}x${d.height2}</span>
                        <span class="text-muted">${formatSize(d.size2)}</span>
                    </div>
                </div>
            `).join("");
        })
        .catch(e => { list.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

function closeDuplicatesModal() {
    document.getElementById("duplicates-modal").style.display = "none";
}

// === Pfade-Verwaltung ===

function openPathsModal() {
    document.getElementById("paths-modal").style.display = "flex";
    loadPathsList();
}

function closePathsModal() {
    document.getElementById("paths-modal").style.display = "none";
}

function loadPathsList() {
    fetch("/api/library/paths")
        .then(r => r.json())
        .then(data => {
            const paths = data.paths || [];
            const list = document.getElementById("paths-list");
            if (!paths.length) {
                list.innerHTML = '<div class="loading-msg">Keine Pfade konfiguriert</div>';
                return;
            }
            list.innerHTML = paths.map(p => `
                <div class="path-item">
                    <div class="path-info">
                        <strong>${escapeHtml(p.name)}</strong>
                        <span class="text-muted" style="font-size:0.75rem">${escapeHtml(p.path)}</span>
                        <span class="tag">${p.media_type === 'series' ? 'Serien' : 'Filme'}</span>
                        ${p.enabled ? '<span class="tag ok">Aktiv</span>' : '<span class="tag">Deaktiviert</span>'}
                        ${p.video_count !== undefined ? `<span class="text-muted" style="font-size:0.75rem">${p.video_count} Videos</span>` : ""}
                    </div>
                    <div class="path-actions">
                        <button class="btn-small btn-secondary" onclick="togglePath(${p.id}, ${!p.enabled})">${p.enabled ? 'Deaktivieren' : 'Aktivieren'}</button>
                        <button class="btn-small btn-danger" onclick="deletePath(${p.id})">Loeschen</button>
                    </div>
                </div>
            `).join("");
        })
        .catch(() => {});
}

function addPath() {
    const name = document.getElementById("new-path-name").value.trim();
    const path = document.getElementById("new-path-path").value.trim();
    const media_type = document.getElementById("new-path-type").value;
    if (!name || !path) { showToast("Name und Pfad erforderlich", "error"); return; }

    fetch("/api/library/paths", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({name, path, media_type}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else {
                document.getElementById("new-path-name").value = "";
                document.getElementById("new-path-path").value = "";
                showToast("Pfad hinzugefuegt", "success");
                loadPathsList();
                loadLibraryPaths();
            }
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

function togglePath(pathId, enabled) {
    fetch(`/api/library/paths/${pathId}`, {
        method: "PUT",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({enabled}),
    })
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else { loadPathsList(); loadLibraryPaths(); }
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

async function deletePath(pathId) {
    if (!await showConfirm("Pfad wirklich loeschen?<br>(Videos bleiben erhalten, aber werden aus DB entfernt)", {title: "Pfad loeschen", okText: "Loeschen", icon: "danger", danger: true})) return;
    fetch(`/api/library/paths/${pathId}`, {method: "DELETE"})
        .then(r => r.json())
        .then(data => {
            if (data.error) showToast("Fehler: " + data.error, "error");
            else { showToast("Pfad geloescht", "success"); loadPathsList(); loadLibraryPaths(); }
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Clean-Modal ===

function openCleanModal() {
    document.getElementById("clean-modal").style.display = "flex";
    document.getElementById("clean-list").innerHTML =
        '<div class="loading-msg">Klicke "Junk scannen" um zu starten</div>';
    document.getElementById("clean-info").textContent = "";
    cleanData = [];
}

function closeCleanModal() {
    document.getElementById("clean-modal").style.display = "none";
}

function scanForJunk() {
    const list = document.getElementById("clean-list");
    list.innerHTML = '<div class="loading-msg">Scanne...</div>';

    fetch("/api/library/clean/scan")
        .then(r => r.json())
        .then(data => {
            cleanData = data.files || [];
            document.getElementById("clean-info").textContent =
                `${data.total_count || 0} Dateien, ${formatSize(data.total_size || 0)}`;

            // Extensions-Filter fuellen
            const exts = new Set(cleanData.map(f => f.extension));
            const select = document.getElementById("clean-ext-filter");
            select.innerHTML = '<option value="">Alle</option>';
            for (const ext of [...exts].sort()) {
                select.innerHTML += `<option value="${ext}">${ext}</option>`;
            }

            renderCleanList(cleanData);
        })
        .catch(e => { list.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

function renderCleanList(files) {
    const list = document.getElementById("clean-list");
    if (!files.length) {
        list.innerHTML = '<div class="loading-msg">Keine Junk-Dateien gefunden</div>';
        return;
    }

    let html = '<table class="data-table"><thead><tr>';
    html += '<th><input type="checkbox" id="clean-select-all-header" onchange="toggleCleanSelectAll()"></th>';
    html += '<th>Dateiname</th><th>Serie/Ordner</th><th>Extension</th><th>Groesse</th>';
    html += '</tr></thead><tbody>';

    for (let i = 0; i < files.length; i++) {
        const f = files[i];
        html += `<tr>
            <td><input type="checkbox" class="clean-check" data-idx="${i}"></td>
            <td class="td-name" title="${escapeHtml(f.path)}">${escapeHtml(f.name)}</td>
            <td>${escapeHtml(f.parent_series || "-")}</td>
            <td><span class="tag">${escapeHtml(f.extension)}</span></td>
            <td>${formatSize(f.size)}</td>
        </tr>`;
    }
    html += '</tbody></table>';
    list.innerHTML = html;
}

function filterCleanList() {
    const ext = document.getElementById("clean-ext-filter").value;
    if (!ext) { renderCleanList(cleanData); return; }
    renderCleanList(cleanData.filter(f => f.extension === ext));
}

function toggleCleanSelectAll() {
    const checked = document.getElementById("clean-select-all")?.checked
        || document.getElementById("clean-select-all-header")?.checked || false;
    document.querySelectorAll(".clean-check").forEach(cb => cb.checked = checked);
}

async function deleteSelectedJunk() {
    const checked = document.querySelectorAll(".clean-check:checked");
    if (!checked.length) { showToast("Keine Dateien ausgewaehlt", "error"); return; }
    if (!await showConfirm(`${checked.length} Dateien wirklich loeschen?`, {title: "Junk loeschen", okText: "Loeschen", icon: "danger", danger: true})) return;

    const files = [];
    checked.forEach(cb => {
        const idx = parseInt(cb.getAttribute("data-idx"));
        if (cleanData[idx]) files.push(cleanData[idx].path);
    });

    fetch("/api/library/clean/delete", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({files}),
    })
        .then(r => r.json())
        .then(data => {
            showToast(`${data.deleted || 0} geloescht, ${data.failed || 0} fehlgeschlagen`, data.failed > 0 ? "warning" : "success");
            if (data.errors && data.errors.length) console.warn("Clean-Fehler:", data.errors);
            scanForJunk();
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

function deleteEmptyDirs() {
    fetch("/api/library/clean/empty-dirs", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({}),
    })
        .then(r => r.json())
        .then(data => showToast(`${data.deleted_dirs || 0} leere Ordner geloescht`, "success"))
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Import-Modal ===

let importBrowseTimer = null;

function openImportModal() {
    document.getElementById("import-modal").style.display = "flex";
    document.getElementById("import-setup").style.display = "";
    document.getElementById("import-preview").style.display = "none";
    document.getElementById("import-progress").style.display = "none";
    document.getElementById("import-source").value = "";
    document.getElementById("import-folder-info").textContent = "";
    document.getElementById("btn-analyze-import").disabled = true;
    currentImportJobId = null;

    // Ziel-Libraries laden
    fetch("/api/library/paths")
        .then(r => r.json())
        .then(data => {
            const select = document.getElementById("import-target");
            select.innerHTML = (data.paths || []).map(p =>
                `<option value="${p.id}" ${activePathId === p.id ? 'selected' : ''}>${escapeHtml(p.name)} (${p.media_type === 'series' ? 'Serien' : 'Filme'})</option>`
            ).join("");
        })
        .catch(() => {});

    // Bestehende Import-Jobs laden
    loadExistingImportJobs();

    // Standard-Pfad im Filebrowser oeffnen
    importBrowse("/mnt");
}

function loadExistingImportJobs() {
    fetch("/api/library/import")
        .then(r => r.json())
        .then(data => {
            const jobs = (data.jobs || []).filter(j => j.status !== 'done');
            const container = document.getElementById("import-existing");
            const list = document.getElementById("import-jobs-list");

            if (!jobs.length) {
                container.style.display = "none";
                return;
            }

            container.style.display = "";
            list.innerHTML = jobs.map(j => {
                const statusClass = j.status === 'ready' ? 'tag-success' :
                                    j.status === 'error' ? 'tag-error' :
                                    j.status === 'importing' ? 'tag-warning' : '';
                const statusText = j.status === 'ready' ? 'Bereit' :
                                   j.status === 'analyzing' ? 'Analyse...' :
                                   j.status === 'importing' ? 'Laeuft' :
                                   j.status === 'error' ? 'Fehler' : j.status;
                const sourceName = j.source_path.split('/').pop();
                return `<span style="display:inline-flex;align-items:center;gap:0.25rem">
                    <button class="btn-small ${j.status === 'ready' ? 'btn-primary' : 'btn-secondary'}"
                            onclick="loadImportJob(${j.id})"
                            title="${escapeHtml(j.source_path)}">
                        ${escapeHtml(sourceName)} (${j.processed_files}/${j.total_files})
                        <span class="tag ${statusClass}" style="margin-left:0.3rem;font-size:0.7rem">${statusText}</span>
                    </button>
                    ${j.status !== 'importing' ? `<button class="btn-small btn-danger" onclick="deleteImportJob(${j.id}, event)" title="Job loeschen">&times;</button>` : ''}
                </span>`;
            }).join("");
        })
        .catch(() => {
            document.getElementById("import-existing").style.display = "none";
        });
}

async function deleteImportJob(jobId, ev) {
    if (ev) ev.stopPropagation();
    if (!await showConfirm("Import-Job wirklich loeschen?", {title: "Job loeschen", okText: "Loeschen", icon: "danger", danger: true})) return;
    fetch(`/api/library/import/${jobId}`, { method: "DELETE" })
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                return;
            }
            showToast("Import-Job geloescht", "success");
            loadExistingImportJobs();
        })
        .catch(() => showToast("Loeschen fehlgeschlagen", "error"));
}

async function deleteCurrentImportJob() {
    if (!currentImportJobId) return;
    if (!await showConfirm("Import-Job wirklich loeschen?", {title: "Job loeschen", okText: "Loeschen", icon: "danger", danger: true})) return;
    fetch(`/api/library/import/${currentImportJobId}`, { method: "DELETE" })
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                return;
            }
            showToast("Import-Job geloescht", "success");
            resetImport();
        })
        .catch(() => showToast("Loeschen fehlgeschlagen", "error"));
}

function loadImportJob(jobId) {
    currentImportJobId = jobId;
    document.getElementById("import-setup").style.display = "none";
    document.getElementById("import-existing").style.display = "none";
    document.getElementById("import-series-assign").style.display = "none";
    document.getElementById("import-preview").style.display = "none";

    fetch(`/api/library/import/${jobId}`)
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                showToast("Fehler: " + data.error, "error");
                resetImport();
                return;
            }

            const job = data.job || {};

            // Je nach Status richtigen Bereich anzeigen
            if (job.status === 'importing') {
                document.getElementById("import-progress").style.display = "";
                startImportPolling();
            } else if (job.status === 'pending_assignment') {
                document.getElementById("import-series-assign").style.display = "";
                loadPendingSeries();
            } else {
                document.getElementById("import-preview").style.display = "";
                renderImportItems(data);
            }
        })
        .catch(e => {
            showToast("Fehler beim Laden: " + e, "error");
            resetImport();
        });
}

function closeImportModal() {
    document.getElementById("import-modal").style.display = "none";
}

function resetImport() {
    document.getElementById("import-setup").style.display = "";
    document.getElementById("import-series-assign").style.display = "none";
    document.getElementById("import-preview").style.display = "none";
    document.getElementById("import-progress").style.display = "none";
    currentImportJobId = null;
}

function debounceImportPath() {
    if (importBrowseTimer) clearTimeout(importBrowseTimer);
    importBrowseTimer = setTimeout(() => {
        const val = document.getElementById("import-source").value.trim();
        if (val && val.startsWith("/")) importBrowse(val);
    }, 600);
}

function importBrowse(path) {
    const browser = document.getElementById("import-browser");
    browser.innerHTML = '<div class="loading-msg" style="padding:0.8rem">Lade...</div>';

    fetch("/api/library/browse-fs?path=" + encodeURIComponent(path))
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                browser.innerHTML = `<div class="loading-msg" style="padding:0.8rem">${escapeHtml(data.error)}</div>`;
                return;
            }

            let html = '';

            // Breadcrumb
            html += '<div class="fb-breadcrumb">';
            html += `<a href="#" onclick="importBrowse('/mnt'); return false;">/mnt</a>`;
            for (const c of (data.breadcrumb || [])) {
                if (c.path === "/mnt" || c.path.length < 5) continue;
                html += ` <span style="color:#555">/</span> `;
                html += `<a href="#" onclick="importBrowse('${escapeAttr(c.path)}'); return false;">${escapeHtml(c.name)}</a>`;
            }
            html += '</div>';

            // Eltern-Ordner
            const parts = data.current_path.split("/");
            if (parts.length > 2) {
                const parentPath = parts.slice(0, -1).join("/") || "/mnt";
                html += `<div class="import-browser-folder" onclick="importBrowse('${escapeAttr(parentPath)}')">
                    <span class="fb-icon">&#128281;</span>
                    <span class="fb-name">..</span>
                </div>`;
            }

            // Unterordner: Einfachklick = auswaehlen, Doppelklick = navigieren
            for (const f of (data.folders || [])) {
                const meta = f.video_count > 0 ? `${f.video_count} Videos` : "";
                html += `<div class="import-browser-folder" onclick="importFolderClick('${escapeAttr(f.path)}', this)">
                    <span class="fb-icon">&#128193;</span>
                    <span class="fb-name">${escapeHtml(f.name)}</span>
                    <span class="fb-meta">${meta}</span>
                </div>`;
            }

            if (!data.folders?.length && !data.video_count) {
                html += '<div class="loading-msg" style="padding:0.8rem">Leerer Ordner</div>';
            }

            browser.innerHTML = html;

            // Pfad-Input aktualisieren und Video-Info anzeigen
            updateImportFolderInfo(data);
        })
        .catch(e => {
            browser.innerHTML = `<div class="loading-msg" style="padding:0.8rem">Fehler: ${e}</div>`;
        });
}

// Klick-Handler: Einfachklick = auswaehlen, Doppelklick = navigieren
let _importClickTimer = null;
function importFolderClick(path, el) {
    if (_importClickTimer) {
        // Zweiter Klick innerhalb 300ms -> Doppelklick -> navigieren
        clearTimeout(_importClickTimer);
        _importClickTimer = null;
        importBrowse(path);
    } else {
        // Erster Klick -> kurz warten ob Doppelklick kommt
        _importClickTimer = setTimeout(() => {
            _importClickTimer = null;
            importSelectFolder(path, el);
        }, 250);
    }
}

function importSelectFolder(path, el) {
    // Vorherige Auswahl entfernen
    document.querySelectorAll(".import-browser-folder.selected").forEach(
        f => f.classList.remove("selected")
    );
    el.classList.add("selected");
    document.getElementById("import-source").value = path;

    // Video-Info fuer den ausgewaehlten Ordner laden
    fetch("/api/library/browse-fs?path=" + encodeURIComponent(path))
        .then(r => r.json())
        .then(data => {
            if (!data.error) updateImportFolderInfo(data);
        })
        .catch(() => {});
}

function updateImportFolderInfo(data) {
    const info = document.getElementById("import-folder-info");
    const btn = document.getElementById("btn-analyze-import");
    const totalVids = (data.video_count || 0);
    // Auch Unterordner zaehlen
    let subVids = 0;
    for (const f of (data.folders || [])) subVids += (f.video_count || 0);
    const allVids = totalVids + subVids;

    if (allVids > 0) {
        info.textContent = `${allVids} Videos gefunden`;
        btn.disabled = false;
    } else {
        info.textContent = "Keine Videos im Ordner";
        btn.disabled = true;
    }
}

async function createImportJob() {
    const source = document.getElementById("import-source").value.trim();
    const target = document.getElementById("import-target").value;
    const mode = document.getElementById("import-mode").value;
    if (!source || !target) { showToast("Quellordner und Ziel erforderlich", "error"); return; }

    const btn = document.getElementById("btn-analyze-import");
    btn.textContent = "Analysiere...";
    btn.disabled = true;

    try {
        // Job erstellen
        let resp = await fetch("/api/library/import", {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({source_path: source, target_library_id: parseInt(target), mode}),
        });
        let data = await resp.json();
        if (data.error) {
            showToast("Fehler: " + data.error, "error");
            btn.textContent = "Analysieren";
            btn.disabled = false;
            return;
        }
        currentImportJobId = data.job_id;

        // Analyse starten (nur Dateinamen-Erkennung, KEIN TVDB)
        resp = await fetch(`/api/library/import/${data.job_id}/analyze`, {method: "POST"});
        data = await resp.json();
        btn.textContent = "Analysieren";
        btn.disabled = false;

        if (data.error) {
            showToast("Analyse-Fehler: " + data.error, "error");
            return;
        }

        // Pruefen ob Serien zugeordnet werden muessen
        const job = data.job || {};
        if (job.status === "pending_assignment") {
            // Phase 2: Serien-Zuordnung anzeigen
            document.getElementById("import-setup").style.display = "none";
            document.getElementById("import-series-assign").style.display = "";
            document.getElementById("import-preview").style.display = "none";
            await loadPendingSeries();
        } else {
            // Direkt zu Konflikt-Ansicht
            document.getElementById("import-setup").style.display = "none";
            document.getElementById("import-series-assign").style.display = "none";
            document.getElementById("import-preview").style.display = "";
            renderImportItems(data);
        }
    } catch (e) {
        btn.textContent = "Analysieren";
        btn.disabled = false;
        showToast("Fehler: " + e, "error");
    }
}

// === Serien-Zuordnung (Phase 2) ===

async function loadPendingSeries() {
    if (!currentImportJobId) return;
    try {
        const resp = await fetch(`/api/library/import/${currentImportJobId}/pending-series`);
        const data = await resp.json();
        renderPendingSeries(data.series || []);
    } catch (e) {
        showToast("Fehler beim Laden: " + e, "error");
    }
}

function renderPendingSeries(series) {
    const container = document.getElementById("import-series-list");
    if (!series.length) {
        // Keine Serien mehr -> weiter zu Konflikt-Check
        finishSeriesAssignment();
        return;
    }

    let html = '<div style="margin-bottom:1rem;color:#aaa">';
    html += 'Die folgenden Serien wurden erkannt. Bitte ordne sie der richtigen TVDB-Serie zu:';
    html += '</div>';

    for (const s of series) {
        html += `<div class="import-series-row" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem;margin-bottom:0.3rem;background:#1a1a2e;border-radius:4px">`;
        html += `<span style="flex:1"><strong>${escapeHtml(s.detected_name)}</strong>`;
        html += ` <span class="text-muted">(${s.count} Folgen, Staffel ${s.seasons})</span></span>`;
        html += `<button class="btn-small btn-primary import-assign-btn" data-series="${escapeHtml(s.detected_name)}" data-count="${s.count}">TVDB zuordnen</button>`;
        html += `<button class="btn-small btn-secondary import-skip-btn" data-series="${escapeHtml(s.detected_name)}">Ueberspringen</button>`;
        html += `</div>`;
    }
    container.innerHTML = html;

    // Event-Listener hinzufuegen
    container.querySelectorAll(".import-assign-btn").forEach(btn => {
        btn.addEventListener("click", () => {
            openSeriesAssignModal(btn.dataset.series, parseInt(btn.dataset.count));
        });
    });
    container.querySelectorAll(".import-skip-btn").forEach(btn => {
        btn.addEventListener("click", () => {
            skipImportSeries(btn.dataset.series);
        });
    });
}

async function finishSeriesAssignment() {
    // Alle Serien zugeordnet -> Job-Status abrufen und Konflikte anzeigen
    if (!currentImportJobId) return;
    try {
        const resp = await fetch(`/api/library/import/${currentImportJobId}`);
        const data = await resp.json();

        document.getElementById("import-series-assign").style.display = "none";
        document.getElementById("import-preview").style.display = "";
        renderImportItems(data);
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

function renderImportItems(data) {
    const items = data.items || [];
    const job = data.job || {};
    const list = document.getElementById("import-items-list");

    const matched = items.filter(i => i.status === "matched").length;
    const conflicts = items.filter(i => i.status === "conflict").length;
    const unresolvedConflicts = items.filter(i => i.status === "conflict" && !i.user_action).length;
    const pending = items.filter(i => i.status === "pending").length;
    const pendingSeries = items.filter(i => i.status === "pending_series").length;
    const skipped = items.filter(i => i.status === "skipped").length;

    document.getElementById("import-info").textContent =
        `${items.length} Dateien: ${matched} bereit, ${conflicts} Konflikte, ${skipped} uebersprungen`;

    // Start-Button nur wenn keine ungeloesten Konflikte UND keine pending Items
    const hasUnresolved = items.some(i =>
        (i.status === "conflict" && !i.user_action) ||
        i.status === "pending" ||
        i.status === "pending_series"
    );
    const btn = document.getElementById("btn-start-import");
    btn.disabled = hasUnresolved;
    btn.title = hasUnresolved
        ? `${unresolvedConflicts + pending + pendingSeries} Dateien muessen erst bearbeitet werden`
        : "Import starten";

    if (!items.length) {
        list.innerHTML = '<div class="loading-msg">Keine Dateien gefunden</div>';
        return;
    }

    let html = "";

    // Massen-Aktionen fuer Konflikte wenn welche vorhanden
    if (unresolvedConflicts > 0) {
        html += '<div style="padding:0.6rem;margin-bottom:0.5rem;background:#2a2010;border:1px solid #443020;border-radius:6px">';
        html += `<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">`;
        html += `<span style="font-weight:600;color:#ffcc80">${unresolvedConflicts} Konflikte</span>`;
        html += `<button class="btn-small btn-primary" onclick="resolveAllConflicts('overwrite')">Alle ueberschreiben</button>`;
        html += `<button class="btn-small btn-secondary" onclick="resolveAllConflicts('skip')">Alle ueberspringen</button>`;
        html += `<label style="display:flex;align-items:center;gap:0.3rem;margin-left:auto">`;
        html += `<input type="checkbox" id="import-overwrite-all" onchange="setOverwriteMode(this.checked)"${job.overwrite_all ? ' checked' : ''}>`;
        html += `<span>Immer ueberschreiben</span></label>`;
        html += `</div></div>`;
    }

    // Tabelle mit allen Items
    html += '<table class="data-table"><thead><tr>';
    html += '<th>Quelldatei</th><th>Serie</th><th>S/E</th><th>Titel</th><th>Ziel</th><th>Status</th>';
    html += '</tr></thead><tbody>';

    for (const item of items) {
        const statusClass = item.status === "conflict" ? "status-badge warn"
            : item.status === "matched" ? "status-badge ok"
            : item.status === "done" ? "status-badge ok"
            : item.status === "pending" ? "status-badge error"
            : item.status === "pending_series" ? "status-badge error"
            : item.status === "skipped" ? "status-badge"
            : "status-badge";
        let statusText = item.status === "conflict" ? "Konflikt"
            : item.status === "matched" ? "OK"
            : item.status === "done" ? "Fertig"
            : item.status === "skipped" ? "Skip"
            : item.status === "pending" ? "Nicht erkannt"
            : item.status === "pending_series" ? "Serie fehlt"
            : item.status;
        if (item.user_action === "overwrite") statusText = "Ueberschreiben";
        if (item.user_action === "skip") statusText = "Skip";

        const sourceName = item.source_file ? item.source_file.split("/").pop() : "-";
        const se = (item.detected_season && item.detected_episode)
            ? `S${String(item.detected_season).padStart(2, "0")}E${String(item.detected_episode).padStart(2, "0")}`
            : "-";

        const rowClass = item.status === "conflict" && !item.user_action ? "row-conflict"
            : (item.status === "pending" || item.status === "pending_series") ? "row-pending"
            : "";

        html += `<tr class="${rowClass}">
            <td class="td-name" title="${escapeHtml(item.source_file || '')}">${escapeHtml(sourceName)}</td>
            <td>${escapeHtml(item.tvdb_series_name || item.detected_series || "-")}</td>
            <td>${se}</td>
            <td>${escapeHtml(item.tvdb_episode_title || "-")}</td>
            <td class="td-name" title="${escapeHtml((item.target_path || '') + '/' + (item.target_filename || ''))}">${escapeHtml(item.target_filename || "-")}</td>
            <td><span class="${statusClass}">${statusText}</span>
                ${item.conflict_reason && !item.user_action ? `<div class="text-muted" style="font-size:0.7rem">${escapeHtml(item.conflict_reason)}</div>` : ""}
            </td>
        </tr>`;
    }
    html += '</tbody></table>';
    list.innerHTML = html;
}

// Massen-Aktionen fuer Konflikte
async function resolveAllConflicts(action) {
    if (!currentImportJobId) return;
    try {
        const resp = await fetch(`/api/library/import/${currentImportJobId}/resolve-all-conflicts`, {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({action}),
        });
        const data = await resp.json();
        if (data.error) {
            showToast("Fehler: " + data.error, "error");
            return;
        }
        showToast(`${data.updated} Konflikte: ${action}`, "success");
        refreshImportPreview();
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

async function setOverwriteMode(overwrite) {
    if (!currentImportJobId) return;
    try {
        await fetch(`/api/library/import/${currentImportJobId}/overwrite-mode`, {
            method: "PUT",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({overwrite}),
        });
        if (overwrite) {
            refreshImportPreview();
        }
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

function resolveImportConflict(itemId, action) {
    fetch(`/api/library/import/items/${itemId}/resolve`, {
        method: "PUT",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({action}),
    })
        .then(r => r.json())
        .then(() => refreshImportPreview())
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Import-Zuordnungs-Modal (Einzel + Serie) ===

let _assignItemId = null;
let _assignTvdbId = null;
let _assignSeriesName = "";
let _assignSearchTimer = null;
let _assignSeriesMode = false; // true = Serien-Zuordnung (alle Folgen)
let _assignDetectedSeries = ""; // Original detected_series fuer Batch

function openImportAssignModal(itemId, filename) {
    _assignItemId = itemId;
    _assignTvdbId = null;
    _assignSeriesName = "";
    _assignSeriesMode = false;
    _assignDetectedSeries = "";

    const modal = document.getElementById("import-assign-modal");
    modal.style.display = "flex";
    document.getElementById("import-assign-filename").textContent = filename;
    document.getElementById("import-assign-search").value = "";
    document.getElementById("import-assign-results").innerHTML = "";
    document.getElementById("import-assign-selected").style.display = "none";
    document.getElementById("import-assign-season").value = "";
    document.getElementById("import-assign-episode").value = "";
    // Staffel/Episode einblenden (Einzelmodus)
    const seFields = document.getElementById("import-assign-se-fields");
    if (seFields) seFields.style.display = "";
    document.getElementById("import-assign-search").focus();
}

function openSeriesAssignModal(detectedSeries, count) {
    _assignItemId = null;
    _assignTvdbId = null;
    _assignSeriesName = "";
    _assignSeriesMode = true;
    _assignDetectedSeries = detectedSeries;

    const modal = document.getElementById("import-assign-modal");
    modal.style.display = "flex";
    document.getElementById("import-assign-filename").textContent =
        `Serie: ${detectedSeries} (${count} Folgen)`;
    document.getElementById("import-assign-search").value = detectedSeries;
    document.getElementById("import-assign-results").innerHTML = "";
    document.getElementById("import-assign-selected").style.display = "none";
    // Staffel/Episode verstecken (Serienmodus)
    const seFields = document.getElementById("import-assign-se-fields");
    if (seFields) seFields.style.display = "none";
    // Sofort TVDB-Suche starten
    searchAssignTvdb();
}

function closeImportAssignModal() {
    document.getElementById("import-assign-modal").style.display = "none";
    _assignItemId = null;
    _assignSeriesMode = false;
    _assignDetectedSeries = "";
    // Staffel/Episode wieder einblenden
    const seFields = document.getElementById("import-assign-se-fields");
    if (seFields) seFields.style.display = "";
}

async function skipImportSeries(detectedSeries) {
    if (!currentImportJobId) return;
    if (!await showConfirm(`Alle Folgen von "<strong>${escapeHtml(detectedSeries)}</strong>" ueberspringen?`, {title: "Serie ueberspringen", okText: "Alle Skip"})) return;

    try {
        const r = await fetch(`/api/library/import/${currentImportJobId}/skip-series`, {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({detected_series: detectedSeries}),
        });
        const data = await r.json();
        if (data.error) { showToast("Fehler: " + data.error, "error"); return; }
        showToast(`${data.skipped} Folgen uebersprungen`, "success");

        // Pruefen ob wir in der Serien-Zuordnung sind
        const seriesAssignEl = document.getElementById("import-series-assign");
        if (seriesAssignEl && seriesAssignEl.style.display !== "none") {
            // Noch in Phase 2 -> Serien-Liste aktualisieren
            loadPendingSeries();
        } else {
            // In Konflikt-Ansicht -> Preview aktualisieren
            refreshImportPreview();
        }
    } catch (e) {
        showToast("Fehler: " + e, "error");
    }
}

function debounceAssignSearch() {
    if (_assignSearchTimer) clearTimeout(_assignSearchTimer);
    _assignSearchTimer = setTimeout(searchAssignTvdb, 500);
}

function searchAssignTvdb() {
    const query = document.getElementById("import-assign-search").value.trim();
    if (!query) return;

    const results = document.getElementById("import-assign-results");
    results.innerHTML = '<div class="loading-msg">Suche...</div>';

    fetch(`/api/tvdb/search?q=${encodeURIComponent(query)}`)
        .then(r => r.json())
        .then(data => {
            if (data.error) { results.innerHTML = `<div class="loading-msg">${escapeHtml(data.error)}</div>`; return; }
            if (!data.results || !data.results.length) { results.innerHTML = '<div class="loading-msg">Keine Ergebnisse</div>'; return; }
            results.innerHTML = data.results.slice(0, 8).map(r => `
                <div class="tvdb-result" onclick="selectAssignSeries(${r.tvdb_id}, '${escapeAttr(r.name)}')">
                    ${r.poster ? `<img src="${r.poster}" alt="" class="tvdb-thumb">` : ""}
                    <div>
                        <strong>${escapeHtml(r.name)}</strong>
                        <span class="text-muted">${r.year || ""}</span>
                        <p class="tvdb-overview">${escapeHtml((r.overview || "").substring(0, 120))}</p>
                    </div>
                </div>
            `).join("");
        })
        .catch(e => { results.innerHTML = `<div class="loading-msg">Fehler: ${e}</div>`; });
}

function selectAssignSeries(tvdbId, name) {
    _assignTvdbId = tvdbId;
    _assignSeriesName = name;
    document.getElementById("import-assign-results").innerHTML = "";
    document.getElementById("import-assign-selected").style.display = "";
    document.getElementById("import-assign-selected-name").textContent = name;
    document.getElementById("import-assign-search").value = "";
}

function submitImportAssign() {
    const manualName = document.getElementById("import-assign-search").value.trim();
    const seriesName = _assignSeriesName || manualName;

    if (!seriesName) { showToast("Serie auswaehlen oder Namen eingeben", "error"); return; }

    const btn = document.querySelector("#import-assign-modal .btn-primary");
    btn.disabled = true;

    if (_assignSeriesMode && _assignDetectedSeries) {
        // Serien-Zuordnung: Alle Folgen auf einmal (neue API)
        btn.textContent = "Zuordne alle...";

        fetch(`/api/library/import/${currentImportJobId}/assign-series`, {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({
                detected_series: _assignDetectedSeries,
                tvdb_id: _assignTvdbId || null,
                tvdb_name: seriesName,
            }),
        })
            .then(r => r.json())
            .then(data => {
                btn.disabled = false;
                btn.textContent = "Zuordnen";
                if (data.error) { showToast("Fehler: " + data.error, "error"); return; }
                showToast(`${data.updated} Folgen zugeordnet`, "success");
                closeImportAssignModal();

                // Pruefen ob noch Serien zugeordnet werden muessen
                if (data.remaining_series > 0) {
                    // Noch Serien uebrig -> Liste aktualisieren
                    loadPendingSeries();
                } else {
                    // Alle Serien zugeordnet -> weiter zu Konflikten
                    finishSeriesAssignment();
                }
            })
            .catch(e => { btn.disabled = false; btn.textContent = "Zuordnen"; showToast("Fehler: " + e, "error"); });
        return;
    }

    // Einzel-Zuordnung
    if (!_assignItemId) return;
    const season = parseInt(document.getElementById("import-assign-season").value);
    const episode = parseInt(document.getElementById("import-assign-episode").value);
    if (isNaN(season) || isNaN(episode)) { showToast("Staffel und Episode eingeben", "error"); return; }

    btn.textContent = "Zuordne...";

    fetch(`/api/library/import/items/${_assignItemId}/reassign`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({
            series_name: seriesName,
            season: season,
            episode: episode,
            tvdb_id: _assignTvdbId || null,
        }),
    })
        .then(r => r.json())
        .then(data => {
            btn.disabled = false;
            btn.textContent = "Zuordnen";
            if (data.error) { showToast("Fehler: " + data.error, "error"); return; }
            closeImportAssignModal();
            refreshImportPreview();
        })
        .catch(e => { btn.disabled = false; btn.textContent = "Zuordnen"; showToast("Fehler: " + e, "error"); });
}

function skipImportItem(itemId) {
    fetch(`/api/library/import/items/${itemId}/skip`, {method: "POST"})
        .then(r => r.json())
        .then(() => refreshImportPreview())
        .catch(e => showToast("Fehler: " + e, "error"));
}

function refreshImportPreview() {
    if (!currentImportJobId) return;
    fetch(`/api/library/import/${currentImportJobId}`)
        .then(r => r.json())
        .then(data => renderImportItems(data))
        .catch(() => {});
}

let importPollingId = null;
let _importWsActive = false;  // WebSocket liefert Updates?

async function executeImport() {
    if (!currentImportJobId) return;

    // Job-ID merken bevor resetImport() sie loescht
    const jobId = currentImportJobId;

    // Modal schliessen - Fortschritt laeuft ueber globalen Progress-Balken
    closeImportModal();
    resetImport();

    // Starte Import (non-blocking - Server antwortet sofort)
    fetch(`/api/library/import/${jobId}/execute`, {method: "POST"});
}

// WebSocket-Handler fuer Import-Fortschritt
function handleImportWS(data) {
    if (!data || !data.job_id) return;

    _importWsActive = true;
    stopImportPolling();

    // Import-Progress-Container sichtbar machen
    const progressEl = document.getElementById("import-progress");
    if (progressEl) progressEl.style.display = "";

    // Pro Job ein eigenes Element erstellen/finden
    const container = document.getElementById("import-jobs-container");
    if (!container) return;
    let jobEl = document.getElementById("import-job-" + data.job_id);
    if (!jobEl) {
        jobEl = document.createElement("div");
        jobEl.id = "import-job-" + data.job_id;
        jobEl.className = "import-job-progress";
        const jobName = data.source_path || data.job_name || "Job #" + data.job_id;
        const shortName = jobName.split("/").pop() || jobName;
        jobEl.innerHTML =
            '<div class="import-job-header">' +
                '<span class="import-job-name" title="' + escapeHtml(jobName) + '">' + escapeHtml(shortName) + '</span>' +
                '<span class="import-job-status"></span>' +
            '</div>' +
            '<div class="progress-container"><div class="progress-bar import-job-bar"></div></div>' +
            '<span class="text-muted import-job-text">Starte...</span>';
        container.appendChild(jobEl);
    }

    const bar = jobEl.querySelector(".import-job-bar");
    const statusText = jobEl.querySelector(".import-job-text");
    const statusBadge = jobEl.querySelector(".import-job-status");

    const status = data.status || "";
    const total = data.total || 1;
    const processed = data.processed || 0;
    const curFile = data.current_file || "";
    const bytesDone = data.bytes_done || 0;
    const bytesTotal = data.bytes_total || 0;

    // Prozent berechnen
    let pct = (processed / total) * 100;
    if (bytesTotal > 0 && processed < total) {
        pct += (bytesDone / bytesTotal) * (100 / total);
    }
    pct = Math.min(Math.round(pct), 100);
    if (bar) bar.style.width = pct + "%";

    if (status === "analyzing") {
        if (statusText) statusText.textContent =
            `Analysiere: ${processed} / ${total} - ${curFile}`;
        if (statusBadge) statusBadge.textContent = "Analyse";
    } else if (status === "embedding") {
        if (statusText) statusText.textContent =
            `Metadaten: ${curFile ? curFile.substring(0, 50) : ""} (${processed}/${total})`;
        if (statusBadge) statusBadge.textContent = "Metadaten";
    } else if (status === "importing") {
        let txt = `${processed} / ${total} Dateien`;
        if (curFile && bytesTotal > 0 && processed < total) {
            const curPct = Math.round((bytesDone / bytesTotal) * 100);
            txt += ` - ${curFile.substring(0, 40)}... (${formatSize(bytesDone)}/${formatSize(bytesTotal)})`;
        } else {
            txt += ` (${pct}%)`;
        }
        if (statusText) statusText.textContent = txt;
        if (statusBadge) statusBadge.textContent = pct + "%";
    } else if (status === "done" || status === "error") {
        if (bar) bar.style.width = "100%";
        if (status === "done") {
            if (statusBadge) { statusBadge.textContent = "Fertig"; statusBadge.style.color = "#4caf50"; }
        } else {
            if (statusBadge) { statusBadge.textContent = "Fehler"; statusBadge.style.color = "#f44336"; }
        }

        // Ergebnis per REST holen
        fetch(`/api/library/import/${data.job_id}`)
            .then(r => r.json())
            .then(result => {
                const items = result.items || [];
                const imported = items.filter(i => i.status === "done").length;
                const errors = items.filter(i => i.status === "error").length;
                const skipped = items.filter(i => i.status === "skipped").length;
                if (statusText) statusText.textContent =
                    `${imported} importiert, ${skipped} uebersprungen, ${errors} Fehler`;

                const job = result.job;
                if (job && job.target_library_id && imported > 0) {
                    fetch(`/api/library/scan/${job.target_library_id}`, {method: "POST"})
                        .then(() => setTimeout(() => {
                            loadSectionData(job.target_library_id);
                            loadStats();
                        }, 2000))
                        .catch(() => { reloadAllSections(); loadStats(); });
                } else {
                    reloadAllSections();
                    loadStats();
                }
            })
            .catch(() => {});
    }
}

// WebSocket-Handler fuer Scan-Fortschritt
// Aktualisiert Bibliothek wenn Scan fertig ist (globaler Progress-Balken in base.html)
function handleScanWS(data) {
    if (!data) return;
    if (data.status === "idle") {
        loadStats();
        reloadAllSections();
    }
}

function startImportPolling() {
    if (importPollingId) clearInterval(importPollingId);

    importPollingId = setInterval(async () => {
        try {
            const r = await fetch(`/api/library/import/${currentImportJobId}`);
            const data = await r.json();

            if (data.error) {
                stopImportPolling();
                return;
            }

            const job = data.job;
            if (!job) return;

            // handleImportWS wiederverwenden (rendert in den neuen Container)
            handleImportWS({
                job_id: currentImportJobId,
                status: job.status,
                total: job.total_files || 1,
                processed: job.processed_files || 0,
                current_file: job.current_file_name || "",
                bytes_done: job.current_file_bytes || 0,
                bytes_total: job.current_file_total || 0,
                source_path: job.source_path || "",
            });

            // Fertig? (handleImportWS uebernimmt Ergebnis-Laden und Scan)
            if (job.status === "done" || job.status === "error") {
                stopImportPolling();
            }
        } catch (e) {
            console.error("Import-Polling Fehler:", e);
        }
    }, 5000);  // 5s statt 500ms - nur Fallback
}

function stopImportPolling() {
    if (importPollingId) {
        clearInterval(importPollingId);
        importPollingId = null;
    }
}

// === Hilfsfunktionen ===

function formatSize(bytes) {
    if (!bytes) return "0 B";
    const units = ["B", "KiB", "MiB", "GiB", "TiB"];
    let i = 0;
    let size = bytes;
    while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; }
    return size.toFixed(i > 1 ? 1 : 0) + " " + units[i];
}

function formatDuration(sec) {
    if (!sec || sec <= 0) return "-";
    const d = Math.floor(sec / 86400);
    const h = Math.floor((sec % 86400) / 3600);
    const m = Math.floor((sec % 3600) / 60);
    const parts = [];
    if (d) parts.push(d + "d");
    if (h) parts.push(h + "h");
    if (m || !parts.length) parts.push(m + "m");
    return parts.join(" ");
}

function resolutionLabel(w, h) {
    if (w >= 3840) return "4K";
    if (w >= 1920) return "1080p";
    if (w >= 1280) return "720p";
    if (w >= 720) return "576p";
    return w + "x" + h;
}

function channelLayout(ch) {
    const layouts = {1: "Mono", 2: "2.0", 3: "2.1", 6: "5.1", 8: "7.1"};
    return layouts[ch] || ch + "ch";
}

function escapeHtml(str) {
    if (!str) return "";
    return str.replace(/&/g, "&amp;").replace(/</g, "&lt;")
              .replace(/>/g, "&gt;").replace(/"/g, "&quot;")
              .replace(/'/g, "&#39;");
}

function escapeAttr(str) {
    // String fuer HTML-Attribute (onclick) sicher machen
    // Escaped: ' (fuer JS-String), &, <
    return (str || "")
        .replace(/\\/g, "\\\\")
        .replace(/'/g, "\\'")
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;");
}

function cleanSearchTitle(title) {
    // Fuehrende Sortier-Nummern und Aufloesung-Suffixe entfernen
    return title
        .replace(/^\d{1,2}\s+/, '')
        .replace(/\s*(720p|1080p|2160p|4k|bluray|bdrip|webrip|web-dl|hdtv|x264|x265|hevc|aac|dts|remux)\s*/gi, ' ')
        .trim();
}

// === Video-Player ===

let _playerVideoId = null;

function playVideo(videoId, title) {
    const modal = document.getElementById("player-modal");
    const video = document.getElementById("player-video");
    document.getElementById("player-title").textContent = title || "Video";
    _playerVideoId = videoId;

    // Alte Quelle stoppen
    video.pause();
    video.removeAttribute("src");
    video.load();

    // Neue Quelle setzen (ffmpeg-Transcoding-Stream)
    video.src = `/api/library/videos/${videoId}/stream`;
    modal.style.display = "flex";

    video.play().catch(() => {
        // Autoplay blockiert - User muss manuell starten
    });
}

function closePlayer() {
    const video = document.getElementById("player-video");
    video.pause();
    video.removeAttribute("src");
    video.load();
    _playerVideoId = null;
    document.getElementById("player-modal").style.display = "none";
}

// ESC schliesst den Player
document.addEventListener("keydown", function(e) {
    if (e.key === "Escape") {
        const player = document.getElementById("player-modal");
        if (player && player.style.display === "flex") {
            closePlayer();
            e.stopPropagation();
        }
    }
});

// === Video loeschen ===

async function deleteVideo(videoId, title, context) {
    if (!await showConfirm(`"<strong>${escapeHtml(title)}</strong>" wirklich loeschen?`, {title: "Video loeschen", detail: "Datei wird unwiderruflich entfernt!", okText: "Loeschen", icon: "danger", danger: true})) return;

    fetch(`/api/library/videos/${videoId}?delete_file=1`, {method: "DELETE"})
        .then(r => r.json())
        .then(data => {
            if (data.error) { showToast("Fehler: " + data.error, "error"); return; }
            showToast("Video geloescht", "success");

            // Zeile aus Tabelle entfernen (ohne Modal neu zu laden)
            const row = document.querySelector(`tr[data-video-id="${videoId}"]`);
            if (row) {
                row.remove();
            } else {
                // Fallback: Ansicht komplett aktualisieren
                if (context === "series" && currentSeriesId) {
                    openSeriesDetail(currentSeriesId);
                } else if (context === "movie" && currentMovieId) {
                    openMovieDetail(currentMovieId);
                } else {
                    reloadAllSections();
                }
            }
            loadStats();
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

// === Batch-Thumbnail-Generierung ===

async function generateThumbnails() {
    // Status pruefen
    const status = await fetch("/api/library/thumbnail-status").then(r => r.json());
    if (status.missing === 0) {
        showToast("Alle " + status.total + " Videos haben bereits Thumbnails", "info");
        return;
    }

    if (!await showConfirm(
        status.missing + " von " + status.total + " Videos haben noch kein Thumbnail. Jetzt generieren?",
        {title: "Thumbnails generieren", detail: "Die Generierung laeuft im Hintergrund per ffmpeg.", okText: "Starten", icon: "info"}
    )) return;

    fetch("/api/library/generate-thumbnails", {method: "POST"})
        .then(r => r.json())
        .then(data => {
            if (data.status === "running") {
                showToast("Thumbnail-Generierung laeuft bereits", "info");
            } else {
                showToast("Thumbnail-Generierung gestartet", "success");
            }
            // Globalen Fortschrittsbalken anzeigen und Polling starten
            _gpShow("thumbnails", "Thumbnails", "Starte...", 0);
            pollThumbnailStatus();
        })
        .catch(e => showToast("Fehler: " + e, "error"));
}

function pollThumbnailStatus() {
    const interval = setInterval(() => {
        fetch("/api/library/thumbnail-status")
            .then(r => r.json())
            .then(data => {
                const pct = data.total > 0 ? Math.round((data.generated / data.total) * 100) : 0;
                _gpShow("thumbnails", "Thumbnails", data.generated + " / " + data.total, pct);
                if (!data.running) {
                    clearInterval(interval);
                    _gpHideDelayed("thumbnails");
                    showToast(data.generated + " / " + data.total + " Thumbnails vorhanden", "success");
                }
            })
            .catch(() => clearInterval(interval));
    }, 2000);
}
