521 lines
34 KiB
HTML
521 lines
34 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:;">
|
|
<title>Twitch VOD Manager</title>
|
|
<link rel="stylesheet" href="./styles.css">
|
|
</head>
|
|
<body class="theme-twitch">
|
|
<div class="update-banner" id="updateBanner">
|
|
<span id="updateText">Neue Version verfügbar!</span>
|
|
<div id="updateProgress" style="display: none; flex: 1; margin: 0 15px;">
|
|
<div style="background: rgba(0,0,0,0.3); border-radius: 4px; height: 8px; overflow: hidden;">
|
|
<div id="updateProgressBar" style="background: white; height: 100%; width: 0%; transition: width 0.3s;"></div>
|
|
</div>
|
|
</div>
|
|
<button id="updateButton" onclick="downloadUpdate()">Jetzt herunterladen</button>
|
|
</div>
|
|
|
|
<!-- Clip Dialog Modal -->
|
|
<div class="modal-overlay" id="clipModal">
|
|
<div class="modal" style="background: #2b2b2b; max-width: 500px;">
|
|
<button class="modal-close" onclick="closeClipDialog()">x</button>
|
|
<h2 style="color: #E5A00D; text-align: center; margin-bottom: 20px;" id="clipDialogTitle">Clip zuschneiden</h2>
|
|
|
|
<!-- Start Zeit mit Slider -->
|
|
<div style="margin-bottom: 15px;">
|
|
<label style="display: block; margin-bottom: 5px;">Start:</label>
|
|
<input type="range" id="clipStartSlider" min="0" max="100" value="0"
|
|
style="width: 100%; height: 6px; -webkit-appearance: none; background: #1a1a1a; border-radius: 3px; cursor: pointer;"
|
|
oninput="updateFromSlider('start')">
|
|
<div style="display: flex; align-items: center; gap: 10px; margin-top: 8px;">
|
|
<label style="color: #888;">Startzeit (HH:MM:SS):</label>
|
|
<input type="text" id="clipStartTime" value="00:00:00"
|
|
style="width: 100px; background: #333; border: 1px solid #444; border-radius: 4px; padding: 6px 10px; color: white; font-family: monospace; text-align: center;"
|
|
onchange="updateFromInput('start')">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- End Zeit mit Slider -->
|
|
<div style="margin-bottom: 15px;">
|
|
<label style="display: block; margin-bottom: 5px;">Ende:</label>
|
|
<input type="range" id="clipEndSlider" min="0" max="100" value="60"
|
|
style="width: 100%; height: 6px; -webkit-appearance: none; background: #1a1a1a; border-radius: 3px; cursor: pointer;"
|
|
oninput="updateFromSlider('end')">
|
|
<div style="display: flex; align-items: center; gap: 10px; margin-top: 8px;">
|
|
<label style="color: #888;">Endzeit (HH:MM:SS):</label>
|
|
<input type="text" id="clipEndTime" value="00:01:00"
|
|
style="width: 100px; background: #333; border: 1px solid #444; border-radius: 4px; padding: 6px 10px; color: white; font-family: monospace; text-align: center;"
|
|
onchange="updateFromInput('end')">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dauer Anzeige -->
|
|
<div style="text-align: center; margin-bottom: 20px;">
|
|
<span style="color: #888;">Dauer: </span>
|
|
<span id="clipDurationDisplay" style="color: #00c853;">00:01:00</span>
|
|
</div>
|
|
|
|
<!-- Teil Nummer -->
|
|
<div style="margin-bottom: 15px;">
|
|
<label style="display: block; margin-bottom: 8px;">Start Part-Nummer (optional, fur Fortsetzung):</label>
|
|
<input type="text" id="clipStartPart" placeholder="z.B. 42"
|
|
style="width: 100px; background: #333; border: 1px solid #444; border-radius: 4px; padding: 8px 12px; color: white;"
|
|
oninput="updateFilenameExamples()">
|
|
<div style="color: #888; font-size: 12px; margin-top: 5px;">Leer lassen = Teil 1</div>
|
|
</div>
|
|
|
|
<!-- Dateinamen Format -->
|
|
<div style="margin-bottom: 20px;">
|
|
<label style="display: block; margin-bottom: 10px;">Dateinamen-Format:</label>
|
|
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 8px;">
|
|
<input type="radio" name="filenameFormat" value="simple" checked onchange="updateFilenameExamples()"
|
|
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
|
<span id="formatSimple" style="color: #aaa;">01.02.2026_1.mp4 (Standard)</span>
|
|
</label>
|
|
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 8px;">
|
|
<input type="radio" name="filenameFormat" value="timestamp" onchange="updateFilenameExamples()"
|
|
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
|
<span id="formatTimestamp" style="color: #aaa;">01.02.2026_CLIP_00-00-00_1.mp4 (mit Zeitstempel)</span>
|
|
</label>
|
|
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 10px;">
|
|
<input type="radio" name="filenameFormat" value="template" onchange="updateFilenameExamples()"
|
|
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
|
<span id="formatTemplate" style="color: #aaa;">{date}_{part}.mp4 (benutzerdefiniert)</span>
|
|
</label>
|
|
|
|
<div id="clipFilenameTemplateWrap" style="display:none; margin-top: 10px;">
|
|
<input type="text" id="clipFilenameTemplate" value="{date}_{part}.mp4"
|
|
placeholder="{date}_{part}.mp4"
|
|
style="width: 100%; background: #333; border: 1px solid #444; border-radius: 4px; padding: 8px 12px; color: white; font-family: monospace;"
|
|
oninput="updateFilenameExamples()">
|
|
<div id="clipTemplateHelp" style="color: #888; font-size: 12px; margin-top: 6px;">Platzhalter: {title} {id} {channel} {date} {part} {trim_start} {trim_end} {trim_length} {date_custom="yyyy-MM-dd"}</div>
|
|
<div id="clipTemplateLint" style="color: #8bc34a; font-size: 12px; margin-top: 4px;">Template-Check: OK</div>
|
|
<button class="btn-secondary" id="clipTemplateGuideBtn" style="margin-top: 8px;" onclick="openTemplateGuide('clip')">Template Guide</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Button -->
|
|
<div style="text-align: center;">
|
|
<button class="btn-primary" style="background: #00c853; padding: 12px 30px; border: none; border-radius: 4px; color: white; font-weight: 600; cursor: pointer;" onclick="confirmClipDialog()">Zur Queue hinzufugen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Template Guide Modal -->
|
|
<div class="modal-overlay" id="templateGuideModal">
|
|
<div class="modal template-guide-modal">
|
|
<button class="modal-close" onclick="closeTemplateGuide()">x</button>
|
|
<h2 id="templateGuideTitle">Template Guide</h2>
|
|
<p id="templateGuideIntro" class="template-guide-intro">Nutze Variablen fur Dateinamen und prufe das Ergebnis als Live-Vorschau.</p>
|
|
|
|
<div class="template-guide-actions">
|
|
<button class="btn-secondary" id="templateGuideUseVod" onclick="setTemplateGuidePreset('vod')">VOD Template</button>
|
|
<button class="btn-secondary" id="templateGuideUseParts" onclick="setTemplateGuidePreset('parts')">VOD Part Template</button>
|
|
<button class="btn-secondary" id="templateGuideUseClip" onclick="setTemplateGuidePreset('clip')">Clip Template</button>
|
|
</div>
|
|
|
|
<label id="templateGuideTemplateLabel" class="template-guide-label">Template</label>
|
|
<input type="text" id="templateGuideInput" class="template-guide-input" oninput="updateTemplateGuidePreview()" placeholder="{title}.mp4">
|
|
|
|
<div class="template-guide-preview-box">
|
|
<div class="template-guide-preview-label" id="templateGuideOutputLabel">Live Vorschau</div>
|
|
<div id="templateGuideOutput" class="template-guide-output">-</div>
|
|
<div id="templateGuideContext" class="template-guide-context"></div>
|
|
</div>
|
|
|
|
<h3 id="templateGuideVarsTitle" class="template-guide-vars-title">Verfugbare Variablen</h3>
|
|
<div class="template-guide-table-wrap">
|
|
<table class="template-guide-table">
|
|
<thead>
|
|
<tr>
|
|
<th id="templateGuideVarCol">Variable</th>
|
|
<th id="templateGuideDescCol">Beschreibung</th>
|
|
<th id="templateGuideExampleCol">Beispiel</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="templateGuideBody"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="template-guide-footer">
|
|
<button class="btn-secondary" id="templateGuideCloseBtn" onclick="closeTemplateGuide()">Schliessen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="app">
|
|
<aside class="sidebar">
|
|
<div class="logo">
|
|
<svg viewBox="0 0 24 24"><path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714Z"/></svg>
|
|
<span id="logoText">Twitch VOD Manager</span>
|
|
</div>
|
|
|
|
<nav class="nav">
|
|
<div class="nav-item active" data-tab="vods" onclick="showTab('vods')">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM9 8l7 4-7 4V8z"/></svg>
|
|
<span id="navVodsText">Twitch VODs</span>
|
|
</div>
|
|
<div class="nav-item" data-tab="clips" onclick="showTab('clips')">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>
|
|
<span id="navClipsText">Twitch Clips</span>
|
|
</div>
|
|
<div class="nav-item" data-tab="cutter" onclick="showTab('cutter')">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3h-3z"/></svg>
|
|
<span id="navCutterText">Video schneiden</span>
|
|
</div>
|
|
<div class="nav-item" data-tab="merge" onclick="showTab('merge')">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z"/></svg>
|
|
<span id="navMergeText">Videos zusammenfugen</span>
|
|
</div>
|
|
<div class="nav-item" data-tab="settings" onclick="showTab('settings')">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
|
|
<span id="navSettingsText">Einstellungen</span>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="section-title">Streamer</div>
|
|
<div class="streamers" id="streamerList"></div>
|
|
|
|
<div class="queue-section">
|
|
<div class="queue-header">
|
|
<span class="queue-title" id="queueTitleText">Warteschlange</span>
|
|
<span class="health-badge unknown" id="healthBadge">System: Unbekannt</span>
|
|
<span class="queue-count" id="queueCount">0</span>
|
|
</div>
|
|
<div class="queue-list" id="queueList"></div>
|
|
<div class="queue-actions">
|
|
<button class="btn btn-start" id="btnStart" onclick="toggleDownload()">Start</button>
|
|
<button class="btn btn-retry" id="btnRetryFailed" onclick="retryFailedDownloads()" title="Nur fehlgeschlagene Downloads erneut starten">Wiederholen</button>
|
|
<button class="btn btn-clear" id="btnClear" onclick="clearCompleted()">Leeren</button>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="main">
|
|
<header class="header">
|
|
<h1 id="pageTitle">VODs</h1>
|
|
<div class="header-actions">
|
|
<div class="header-search">
|
|
<input type="text" id="newStreamer" placeholder="Streamer hinzufugen..." onkeypress="if(event.key==='Enter')addStreamer()">
|
|
<button onclick="addStreamer()">+</button>
|
|
</div>
|
|
<button class="btn-icon" onclick="refreshVODs()">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
|
<span id="refreshText">Aktualisieren</span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="content">
|
|
<!-- VODs Tab -->
|
|
<div class="tab-content active" id="vodsTab">
|
|
<div class="vod-grid" id="vodGrid">
|
|
<div class="empty-state">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 14l-5-4 5-4v8zm2-8l5 4-5 4V9z"/></svg>
|
|
<h3>Keine VODs</h3>
|
|
<p>Wahle einen Streamer aus der Liste oder fuge einen neuen hinzu.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Clips Tab -->
|
|
<div class="tab-content" id="clipsTab">
|
|
<div class="clip-input">
|
|
<h2 id="clipsHeading">Twitch Clip-Download</h2>
|
|
<input type="text" id="clipUrl" placeholder="https://clips.twitch.tv/... oder https://www.twitch.tv/.../clip/...">
|
|
<button class="btn-primary" onclick="downloadClip()" id="btnClip">Clip herunterladen</button>
|
|
<div class="clip-status" id="clipStatus"></div>
|
|
</div>
|
|
|
|
<div class="settings-card" style="max-width: 600px; margin: 20px auto;">
|
|
<h3 id="clipsInfoTitle">Info</h3>
|
|
<p style="color: var(--text-secondary); line-height: 1.6; white-space: pre-line;" id="clipsInfoText">
|
|
Unterstutzte Formate:
|
|
- https://clips.twitch.tv/ClipName
|
|
- https://www.twitch.tv/streamer/clip/ClipName
|
|
|
|
Clips werden im Download-Ordner unter "Clips/StreamerName/" gespeichert.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Video Cutter Tab -->
|
|
<div class="tab-content" id="cutterTab">
|
|
<div class="cutter-container">
|
|
<div class="settings-card">
|
|
<h3 id="cutterSelectTitle">Video auswahlen</h3>
|
|
<div class="form-row">
|
|
<input type="text" id="cutterFilePath" readonly placeholder="Keine Datei ausgewahlt...">
|
|
<button class="btn-secondary" id="cutterBrowseBtn" onclick="selectCutterVideo()">Durchsuchen</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="video-preview" id="cutterPreview">
|
|
<div class="placeholder">
|
|
<svg width="64" height="64" viewBox="0 0 24 24" fill="currentColor" style="opacity:0.3"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM9 8l7 4-7 4V8z"/></svg>
|
|
<p style="margin-top:10px">Video auswahlen um Vorschau zu sehen</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cutter-info" id="cutterInfo" style="display:none">
|
|
<div class="cutter-info-item">
|
|
<span class="cutter-info-label">Dauer</span>
|
|
<span class="cutter-info-value" id="infoDuration">--:--:--</span>
|
|
</div>
|
|
<div class="cutter-info-item">
|
|
<span class="cutter-info-label">Auflosung</span>
|
|
<span class="cutter-info-value" id="infoResolution">----x----</span>
|
|
</div>
|
|
<div class="cutter-info-item">
|
|
<span class="cutter-info-label">FPS</span>
|
|
<span class="cutter-info-value" id="infoFps">--</span>
|
|
</div>
|
|
<div class="cutter-info-item">
|
|
<span class="cutter-info-label">Auswahl</span>
|
|
<span class="cutter-info-value" id="infoSelection">--:--:--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="timeline-container" id="timelineContainer" style="display:none">
|
|
<div class="timeline" id="timeline" onclick="seekTimeline(event)">
|
|
<div class="timeline-selection" id="timelineSelection"></div>
|
|
<div class="timeline-current" id="timelineCurrent"></div>
|
|
</div>
|
|
|
|
<div class="time-inputs">
|
|
<div class="time-input-group">
|
|
<label>Start:</label>
|
|
<input type="text" id="startTime" value="00:00:00" onchange="updateTimeFromInput()">
|
|
</div>
|
|
<div class="time-input-group">
|
|
<label>Ende:</label>
|
|
<input type="text" id="endTime" value="00:00:00" onchange="updateTimeFromInput()">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="progress-container" id="cutProgress">
|
|
<div class="progress-bar">
|
|
<div class="progress-bar-fill" id="cutProgressBar"></div>
|
|
</div>
|
|
<div class="progress-text" id="cutProgressText">0%</div>
|
|
</div>
|
|
|
|
<div class="cutter-actions">
|
|
<button class="btn-primary" id="btnCut" onclick="startCutting()" disabled>Schneiden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Merge Tab -->
|
|
<div class="tab-content" id="mergeTab">
|
|
<div class="merge-container">
|
|
<div class="settings-card">
|
|
<h3 id="mergeTitle">Videos zusammenfugen</h3>
|
|
<p style="color: var(--text-secondary); margin-bottom: 15px;" id="mergeDesc">
|
|
Wahle mehrere Videos aus um sie zu einem Video zusammenzufugen.
|
|
Die Reihenfolge kann per Drag & Drop geandert werden.
|
|
</p>
|
|
<button class="btn-secondary" id="mergeAddBtn" onclick="addMergeFiles()">+ Videos hinzufugen</button>
|
|
</div>
|
|
|
|
<div class="file-list" id="mergeFileList">
|
|
<div class="empty-state" style="padding: 40px 20px;">
|
|
<svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor" style="opacity:0.3"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
<p style="margin-top:10px">Keine Videos ausgewahlt</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="progress-container" id="mergeProgress">
|
|
<div class="progress-bar">
|
|
<div class="progress-bar-fill" id="mergeProgressBar"></div>
|
|
</div>
|
|
<div class="progress-text" id="mergeProgressText">0%</div>
|
|
</div>
|
|
|
|
<div class="merge-actions">
|
|
<button class="btn-primary" id="btnMerge" onclick="startMerging()" disabled>Zusammenfugen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Tab -->
|
|
<div class="tab-content" id="settingsTab">
|
|
<div class="settings-card">
|
|
<h3 id="designTitle">Design</h3>
|
|
<div class="form-group">
|
|
<label id="themeLabel">Theme</label>
|
|
<select id="themeSelect" onchange="changeTheme(this.value)">
|
|
<option value="twitch">Twitch</option>
|
|
<option value="discord">Discord</option>
|
|
<option value="youtube">YouTube</option>
|
|
<option value="apple">Apple</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label id="languageLabel">Sprache</label>
|
|
<div class="language-picker" id="languagePicker">
|
|
<button type="button" class="lang-option" id="langOptionDe" onclick="selectLanguageOption('de')" aria-pressed="false">
|
|
<span class="flag-icon flag-de" aria-hidden="true"></span>
|
|
<span id="languageDeText">Deutsch</span>
|
|
</button>
|
|
<button type="button" class="lang-option" id="langOptionEn" onclick="selectLanguageOption('en')" aria-pressed="false">
|
|
<span class="flag-icon flag-en" aria-hidden="true"></span>
|
|
<span id="languageEnText">English</span>
|
|
</button>
|
|
</div>
|
|
<select id="languageSelect" onchange="changeLanguage(this.value)" style="display:none">
|
|
<option value="de">de</option>
|
|
<option value="en">en</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3 id="apiTitle">Twitch API</h3>
|
|
<div class="form-group">
|
|
<label id="clientIdLabel">Client ID</label>
|
|
<input type="text" id="clientId" placeholder="Twitch Client ID">
|
|
</div>
|
|
<div class="form-group">
|
|
<label id="clientSecretLabel">Client Secret</label>
|
|
<input type="password" id="clientSecret" placeholder="Twitch Client Secret">
|
|
</div>
|
|
<button class="btn-primary" id="saveSettingsBtn" onclick="saveSettings()">Speichern & Verbinden</button>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3 id="downloadSettingsTitle">Download-Einstellungen</h3>
|
|
<div class="form-group">
|
|
<label id="storageLabel">Speicherort</label>
|
|
<div class="form-row">
|
|
<input type="text" id="downloadPath" readonly>
|
|
<button class="btn-secondary" onclick="selectFolder()">Ordner</button>
|
|
<button class="btn-secondary" id="openFolderBtn" onclick="openFolder()">Offnen</button>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label id="modeLabel">Download-Modus</label>
|
|
<select id="downloadMode">
|
|
<option value="full" id="modeFullText">Ganzes VOD</option>
|
|
<option value="parts" id="modePartsText">In Teile splitten</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label id="partMinutesLabel">Teil-Lange (Minuten)</label>
|
|
<input type="number" id="partMinutes" value="120" min="10" max="480">
|
|
</div>
|
|
<div class="form-group">
|
|
<label id="performanceModeLabel">Performance-Profil</label>
|
|
<select id="performanceMode">
|
|
<option value="stability" id="performanceModeStability">Max Stabilitat</option>
|
|
<option value="balanced" id="performanceModeBalanced">Ausgewogen</option>
|
|
<option value="speed" id="performanceModeSpeed">Max Geschwindigkeit</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label style="display:flex; align-items:center; gap:8px;">
|
|
<input type="checkbox" id="smartSchedulerToggle" checked>
|
|
<span id="smartSchedulerLabel">Smart Queue Scheduler aktivieren</span>
|
|
</label>
|
|
<label style="display:flex; align-items:center; gap:8px; margin-top: 8px;">
|
|
<input type="checkbox" id="duplicatePreventionToggle" checked>
|
|
<span id="duplicatePreventionLabel">Duplikate in Queue verhindern</span>
|
|
</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<label id="metadataCacheMinutesLabel">Metadata-Cache (Minuten)</label>
|
|
<input type="number" id="metadataCacheMinutes" value="10" min="1" max="120">
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="form-row" style="align-items:center; margin-bottom: 4px;">
|
|
<label id="filenameTemplatesTitle" style="margin: 0;">Dateinamen-Templates</label>
|
|
<button class="btn-secondary" id="settingsTemplateGuideBtn" type="button" onclick="openTemplateGuide('vod')">Template Guide</button>
|
|
</div>
|
|
<div class="form-row" style="gap: 8px; margin: 8px 0 6px;">
|
|
<button class="btn-secondary" id="templatePresetDefault" type="button" onclick="applyTemplatePreset('default')">Preset: Default</button>
|
|
<button class="btn-secondary" id="templatePresetArchive" type="button" onclick="applyTemplatePreset('archive')">Preset: Archive</button>
|
|
<button class="btn-secondary" id="templatePresetClipper" type="button" onclick="applyTemplatePreset('clipper')">Preset: Clipper</button>
|
|
</div>
|
|
<div style="display: grid; gap: 8px; margin-top: 8px;">
|
|
<label id="vodTemplateLabel" style="font-size: 13px; color: var(--text-secondary);">VOD Template</label>
|
|
<input type="text" id="vodFilenameTemplate" placeholder="{title}.mp4" style="font-family: monospace;" oninput="validateFilenameTemplates()">
|
|
|
|
<label id="partsTemplateLabel" style="font-size: 13px; color: var(--text-secondary); margin-top: 4px;">VOD Part Template</label>
|
|
<input type="text" id="partsFilenameTemplate" placeholder="{date}_Part{part_padded}.mp4" style="font-family: monospace;" oninput="validateFilenameTemplates()">
|
|
|
|
<label id="defaultClipTemplateLabel" style="font-size: 13px; color: var(--text-secondary); margin-top: 4px;">Clip Template</label>
|
|
<input type="text" id="defaultClipFilenameTemplate" placeholder="{date}_{part}.mp4" style="font-family: monospace;" oninput="validateFilenameTemplates()">
|
|
</div>
|
|
<div id="filenameTemplateHint" style="color: #888; font-size: 12px; margin-top: 8px;">Platzhalter: {title} {id} {channel} {date} {part} {part_padded} {trim_start} {trim_end} {trim_length} {date_custom="yyyy-MM-dd"}</div>
|
|
<div id="filenameTemplateLint" style="font-size: 12px; margin-top: 6px; color: #8bc34a;">Template-Check: OK</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3 id="updateTitle">Updates</h3>
|
|
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.1.13</p>
|
|
<button class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3 id="preflightTitle">System-Check</h3>
|
|
<div class="form-row" style="margin-bottom: 10px;">
|
|
<button class="btn-secondary" id="btnPreflightRun" onclick="runPreflight(false)">Check ausfuhren</button>
|
|
<button class="btn-secondary" id="btnPreflightFix" onclick="runPreflight(true)">Auto-Fix Tools</button>
|
|
</div>
|
|
<pre id="preflightResult" class="log-panel">Noch kein Check ausgefuhrt.</pre>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3 id="debugLogTitle">Live Debug-Log</h3>
|
|
<div class="form-row" style="margin-bottom: 10px; align-items: center;">
|
|
<button class="btn-secondary" id="btnRefreshLog" onclick="refreshDebugLog()">Aktualisieren</button>
|
|
<label style="display:flex; align-items:center; gap:6px; font-size:13px; color: var(--text-secondary);">
|
|
<input type="checkbox" id="debugAutoRefresh" onchange="toggleDebugAutoRefresh(this.checked)">
|
|
<span id="autoRefreshText">Auto-Refresh</span>
|
|
</label>
|
|
</div>
|
|
<pre id="debugLogOutput" class="log-panel">Lade...</pre>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3 id="runtimeMetricsTitle">Runtime Metrics</h3>
|
|
<div class="form-row" style="margin-bottom: 10px; align-items: center;">
|
|
<button class="btn-secondary" id="btnRefreshMetrics" onclick="refreshRuntimeMetrics()">Aktualisieren</button>
|
|
<button class="btn-secondary" id="btnExportMetrics" onclick="exportRuntimeMetrics()">Export JSON</button>
|
|
<label style="display:flex; align-items:center; gap:6px; font-size:13px; color: var(--text-secondary);">
|
|
<input type="checkbox" id="runtimeMetricsAutoRefresh" onchange="toggleRuntimeMetricsAutoRefresh(this.checked)">
|
|
<span id="runtimeMetricsAutoRefreshText">Auto-Refresh</span>
|
|
</label>
|
|
</div>
|
|
<pre id="runtimeMetricsOutput" class="log-panel">Lade...</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="status-bar">
|
|
<div class="status-indicator">
|
|
<div class="status-dot" id="statusDot"></div>
|
|
<span id="statusText">Nicht verbunden</span>
|
|
</div>
|
|
<span id="versionText">v4.1.13</span>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script src="../dist/renderer-locale-de.js"></script>
|
|
<script src="../dist/renderer-locale-en.js"></script>
|
|
<script src="../dist/renderer-texts.js"></script>
|
|
<script src="../dist/renderer-shared.js"></script>
|
|
<script src="../dist/renderer-settings.js"></script>
|
|
<script src="../dist/renderer-streamers.js"></script>
|
|
<script src="../dist/renderer-queue.js"></script>
|
|
<script src="../dist/renderer-updates.js"></script>
|
|
<script src="../dist/renderer.js"></script>
|
|
</body>
|
|
</html>
|