Files
NovaSonic/index.html
T
HoneyandClaude Sonnet 4.6 d3a1bf6a82 feat: home hero+genres, fix Android queue desync via song ID verification
Home view:
- Add hero card (random pick from recent+frequent combined pool)
- Add genre browser chips (sorted by song count, tap = shuffle-play)

Android sync fix:
- Add currentId to getState() snapshot so JS can verify song identity
- onReady: reset stale localStorage queue when native song ID mismatches
- onTrackChanged: reject stale old-queue callbacks by ID verification
- onPlaybackStateChanged safety net: ID-guard prevents wrong-queue index corruption
- connectToService: call updateStateSnapshot() before onReady so getState() is never empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 09:06:47 +02:00

1019 lines
65 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#07070f">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="NovaSonic">
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="icon.svg">
<title>NovaSonic</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<script>window.NS_SERVER_URL='';window.NS_BACKEND_URL='';</script>
<script src="config.js" onerror="0"></script>
<script type="module" src="/src/main.js"></script>
</head>
<body>
<script>(function(){try{if(localStorage.getItem('ns_cfg'))document.documentElement.classList.add('ns-authed');}catch(e){}})()</script>
<!-- ─── SETUP ─── -->
<div id="setup-screen">
<div class="setup-card">
<div class="setup-logo">
<div class="logo-icon">♫</div>
<h1>Nova<em>Sonic</em></h1>
</div>
<div class="setup-pane active" id="pane-navidrome">
<div class="field"><label>Server URL</label><input id="srv-url" type="url" placeholder="https://music.example.com"></div>
<div class="field"><label>Benutzername</label><input id="srv-user" type="text" placeholder="admin"></div>
<div class="field"><label>Passwort</label><input id="srv-pass" type="password" placeholder="••••••••"></div>
</div>
<button class="btn-primary" id="btn-connect" onclick="doConnect()">Verbinden</button>
<div class="setup-error" id="setup-error"></div>
</div>
</div>
<!-- ─── APP ─── -->
<div id="app">
<div class="app-body">
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="sidebar-logo">
<div class="icon">♫</div>
<span>Nova<em>Sonic</em></span>
</div>
</div>
<nav>
<div class="nav-section">
<div class="nav-section-title">Bibliothek</div>
<div class="nav-item active" onclick="navigate('home')" data-page="home">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>Home
</div>
<div class="nav-item" onclick="navigate('artists')" data-page="artists">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>Künstler
</div>
<div class="nav-item" onclick="navigate('albums')" data-page="albums">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>Alben
</div>
<div class="nav-item" onclick="navigate('playlists')" data-page="playlists">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>Playlists
</div>
<div class="nav-item" onclick="navigate('starred')" data-page="starred">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>Favoriten
</div>
</div>
<div class="nav-section">
<div class="nav-section-title">Entdecken</div>
<div class="nav-item" onclick="navigate('discover')" data-page="discover">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/></svg>Global Suche
</div>
<div class="nav-item" onclick="navigate('new-releases')" data-page="new-releases">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>Neue Releases
</div>
<div class="nav-item" onclick="navigate('recommendations')" data-page="recommendations">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>Für dich
</div>
<div class="nav-item" onclick="navigate('random')" data-page="random">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/></svg>Zufällig
</div>
<div class="nav-item" onclick="navigate('spotify-importer')" data-page="spotify-importer">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M8 11.973c2.5-1.473 5.5-.973 7.5.527M9.5 14.473c1.5-.973 3.5-.973 5 .027M7 9c3-1.5 6.5-1 9 1"/></svg>Spotify Importer
</div>
</div>
<div class="nav-section" id="nav-abs-section" style="display:none">
<div class="nav-section-title">Hörbücher</div>
<div class="nav-item" onclick="navigate('audiobooks')" data-page="audiobooks">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>Bibliothek
</div>
</div>
</nav>
</aside>
<div class="mobile-overlay" id="mobile-overlay" onclick="closeSidebar()"></div>
<!-- MAIN -->
<main class="main" id="main-content">
<!-- Mobile header -->
<div id="mobile-header" style="position:sticky;top:0;background:var(--bg);z-index:10;border-bottom:1px solid var(--border)">
<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 14px">
<div class="sidebar-logo">
<div class="icon" style="width:26px;height:26px;font-size:12px;border-radius:7px">♫</div>
<span style="font-size:15px;font-weight:700">Nova<em style="color:var(--accent);font-style:normal">Sonic</em></span>
</div>
<div style="display:flex;align-items:center;gap:4px">
<button class="hbtn notif-bell-btn" onclick="toggleNotifPanel()" title="Benachrichtigungen" style="padding:8px;color:var(--text2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
<span class="notif-badge" style="display:none">0</span>
</button>
<button class="hbtn" id="mobile-search-btn" onclick="toggleMobileSearch()" title="Suche" style="padding:8px;color:var(--text2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
</button>
<button class="hbtn" onclick="openSettings()" title="Einstellungen" style="padding:8px;color:var(--text2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</button>
</div>
</div>
<div id="mobile-search-bar" style="display:none;padding:0 14px 10px">
<div class="search-box" style="background:var(--surface);border-color:var(--border2)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input id="mobile-search-input" type="text" placeholder="Künstler, Album, Titel…" oninput="onMobileSearch(this.value)" style="font-size:15px">
<button onclick="closeMobileSearch()" style="background:none;border:none;color:var(--text3);cursor:pointer;font-size:16px;padding:0 2px;line-height:1">✕</button>
</div>
</div>
</div>
<!-- HOME -->
<div class="page active" id="page-home">
<div id="home-greeting" class="home-greeting"></div>
<div id="home-hero" class="home-hero"></div>
<div class="section-header" style="margin-top:20px"><span class="section-title">Genres</span></div>
<div id="home-genres" class="genre-chips"></div>
<div class="section-header" style="margin-top:20px"><span class="section-title">Zuletzt gespielt</span></div>
<div class="scroll-row" id="home-recent"></div>
<div class="section-header"><span class="section-title">Zufällig</span><span class="section-more" onclick="navigate('random')">Mehr</span></div>
<div class="scroll-row" id="home-random"></div>
<div class="section-header"><span class="section-title">Neueste Alben</span><span class="section-more" onclick="navigate('albums')">Mehr</span></div>
<div class="scroll-row" id="home-newest"></div>
<div class="section-header"><span class="section-title">Häufig gespielt</span></div>
<div class="scroll-row" id="home-frequent"></div>
</div>
<!-- ARTISTS -->
<div class="page" id="page-artists">
<div class="page-header"><div class="page-title">Künstler</div></div>
<div id="artists-grid" class="grid grid-4"></div>
</div>
<!-- ARTIST DETAIL -->
<div class="page" id="page-artist-detail">
<button class="back-btn" id="artist-back-btn"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>Alle Künstler</button>
<div class="artist-hero" id="artist-detail-hero"><div class="artist-hero-overlay"><div class="artist-hero-name" id="artist-detail-name"></div></div></div>
<div id="artist-action-bar" class="artist-action-bar"></div>
<div id="artist-bio-section"></div>
<div id="artist-similar-section"></div>
<div id="artist-topsongs-section"></div>
<div id="artist-albums-section"></div>
</div>
<!-- ARTIST ALL SONGS -->
<div class="page" id="page-artist-all-songs">
<button class="back-btn" id="artist-songs-back-btn"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>Zurück</button>
<div class="page-header" style="margin-top:8px"><div class="page-title" id="artist-songs-title">Alle Songs</div></div>
<div id="artist-songs-list"></div>
</div>
<!-- ITUNES ALBUM DETAIL -->
<div class="page" id="page-itunes-album-detail">
<button class="back-btn" id="itunes-album-back-btn"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>Zurück</button>
<div class="album-header">
<div class="album-cover" id="itunes-album-cover-wrap"><div class="no-art">💿</div></div>
<div class="album-meta">
<div class="album-type">iTunes</div>
<div class="album-name" id="itunes-album-detail-name">—</div>
<div class="album-artist-name" id="itunes-album-detail-artist"></div>
<div class="album-stats" id="itunes-album-detail-stats"></div>
<div class="album-actions">
<button class="btn-outline" id="itunes-album-dl-btn"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Album laden</button>
</div>
</div>
</div>
<div id="itunes-album-songs"></div>
</div>
<!-- ALBUMS -->
<div class="page" id="page-albums">
<div class="page-header"><div class="page-title">Alben</div></div>
<div id="albums-sort-chips" class="sort-chips">
<button class="sort-chip active" data-sort="alphabeticalByName" onclick="loadAlbums('alphabeticalByName')">A–Z</button>
<button class="sort-chip" data-sort="alphabeticalByArtist" onclick="loadAlbums('alphabeticalByArtist')">Künstler</button>
<button class="sort-chip" data-sort="newest" onclick="loadAlbums('newest')">Neueste</button>
<button class="sort-chip" data-sort="recent" onclick="loadAlbums('recent')">Zuletzt gespielt</button>
<button class="sort-chip" data-sort="frequent" onclick="loadAlbums('frequent')">Meistgespielt</button>
</div>
<div id="albums-grid" class="grid grid-4"></div>
</div>
<!-- ALBUM DETAIL -->
<div class="page" id="page-album-detail">
<button class="back-btn" id="album-back-btn"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>Zurück</button>
<div class="album-header">
<div class="album-cover" id="album-cover-wrap"><div class="no-art">💿</div></div>
<div class="album-meta">
<div class="album-type">Album</div>
<div class="album-name" id="album-detail-name">—</div>
<div class="album-artist-name" id="album-detail-artist"></div>
<div class="album-stats" id="album-detail-stats"></div>
<div class="album-actions">
<button class="btn-play-all" id="btn-play-album"><svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>Abspielen</button>
<button class="btn-outline" id="btn-dl-album"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Download</button>
</div>
</div>
</div>
<div class="song-list" id="album-songs"></div>
</div>
<!-- STARRED -->
<div class="page" id="page-starred">
<div class="page-header"><div class="page-title">Favoriten ⭐</div></div>
<div id="starred-sort-chips" class="sort-chips">
<button class="sort-chip active" data-sort="newest" onclick="loadStarred('newest')">Neueste</button>
<button class="sort-chip" data-sort="oldest" onclick="loadStarred('oldest')">Älteste</button>
<button class="sort-chip" data-sort="az" onclick="loadStarred('az')">A–Z</button>
<button class="sort-chip" data-sort="artist" onclick="loadStarred('artist')">Künstler</button>
<button class="sort-chip" data-sort="plays" onclick="loadStarred('plays')">Meistgespielt</button>
</div>
<div class="list-search-wrap">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input id="starred-search" class="list-search" type="text" placeholder="In Favoriten suchen…" oninput="filterStarred(this.value)">
</div>
<div id="starred-songs" class="song-list"></div>
</div>
<!-- PLAYLISTS -->
<div class="page" id="page-playlists">
<div class="page-header">
<div class="page-title">Playlists 🎶</div>
<button class="btn-outline" style="font-size:12px;padding:8px 14px" onclick="createPlaylistPrompt()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Neue Playlist
</button>
</div>
<div id="playlists-grid" class="grid grid-4"></div>
</div>
<!-- PLAYLIST DETAIL -->
<div class="page" id="page-playlist-detail">
<button class="back-btn" id="playlist-back-btn" onclick="navigate('playlists')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>Alle Playlists
</button>
<div class="album-header">
<div class="album-cover" id="playlist-cover"><div class="no-art">🎶</div></div>
<div class="album-meta">
<div class="album-type">Playlist</div>
<div class="album-name" id="playlist-detail-name">—</div>
<div class="album-stats" id="playlist-detail-stats"></div>
<div class="playlist-detail-desc" id="playlist-detail-desc"></div>
<div class="album-actions">
<button class="btn-play-all" id="btn-play-playlist"><svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>Abspielen</button>
<button class="btn-outline" id="btn-shuffle-playlist"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/></svg>Shuffle</button>
<button class="btn-outline" id="btn-edit-playlist"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>Bearbeiten</button>
<button class="btn-outline" id="btn-reorder-playlist"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>Neu anordnen</button>
</div>
</div>
</div>
<div class="list-search-wrap">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input id="playlist-search" class="list-search" type="text" placeholder="In Playlist suchen…" oninput="filterPlaylist(this.value)">
</div>
<div class="pl-reorder-bar" id="pl-reorder-bar" style="display:none">
<span>Reihenfolge ändern</span>
<div style="display:flex;gap:8px">
<button class="edit-pl-btn-cancel" onclick="plReorderCancel()">Abbrechen</button>
<button class="edit-pl-btn-save" onclick="plReorderSave()">Speichern</button>
</div>
</div>
<div class="song-list" id="playlist-songs"></div>
</div>
<!-- RANDOM -->
<div class="page" id="page-random">
<div class="page-header">
<div class="page-title">Zufällig <em>♪</em></div>
<button class="btn-outline" onclick="loadRandom()" style="font-size:12px;padding:8px 14px"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/></svg>Neu mischen</button>
</div>
<div id="random-songs" class="song-list"></div>
</div>
<!-- LOCAL SEARCH -->
<div class="page" id="page-search">
<div class="page-header"><div class="page-title">Suche</div></div>
<div id="search-results"></div>
</div>
<!-- DISCOVER (Global) -->
<div class="page" id="page-discover">
<div class="page-header">
<div class="page-title"><span class="blue">Global</span> Suche</div>
<span class="badge badge-itunes" style="font-size:11px;padding:4px 10px">iTunes Store + Local</span>
</div>
<div class="search-wrap" style="padding:0 0 20px">
<div class="search-box" style="background:var(--surface);border-color:var(--border2)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input id="global-search-input" type="text" placeholder="Künstler, Titel, Album suchen…" oninput="onGlobalSearch(this.value)" style="font-size:15px">
</div>
</div>
<div id="global-search-results">
<div class="empty-state"><div class="icon">🔍</div><p>Gib einen Suchbegriff ein um loszulegen</p></div>
</div>
</div>
<!-- NEW RELEASES -->
<div class="page" id="page-new-releases">
<div class="page-header">
<div class="page-title">Neue <em>Releases</em></div>
<span class="badge badge-itunes" style="font-size:11px;padding:4px 10px">iTunes Charts</span>
</div>
<div class="section-header" style="margin-top:0"><span class="section-title">Top Alben weltweit</span></div>
<div id="nr-albums" class="grid grid-4"></div>
<div class="section-header"><span class="section-title">Top Songs</span></div>
<div id="nr-songs" class="song-list"></div>
</div>
<!-- RECOMMENDATIONS -->
<div class="page" id="page-recommendations">
<div class="page-header">
<div class="page-title">Für <em>dich</em></div>
</div>
<div id="reco-content">
<div class="empty-state"><div class="icon">🎵</div><p>Spiele zuerst Musik in deiner Bibliothek,<br>dann zeigen wir dir ähnliche Künstler und Tracks</p></div>
</div>
</div>
<!-- AUDIOBOOKS -->
<div class="page" id="page-audiobooks">
<div class="page-header">
<div class="page-title">Hörbücher <em>📚</em></div>
</div>
<div class="search-wrap" style="padding:0 0 16px">
<div class="search-box" style="background:var(--surface);border-color:var(--border2)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input id="abs-search-input" type="text" placeholder="Titel oder Autor suchen…" oninput="absOnSearch(this.value)">
</div>
</div>
<div id="abs-books-grid" class="abs-books-grid"></div>
</div>
<!-- AUDIOBOOK DETAIL -->
<div class="page" id="page-abs-book-detail">
<button class="back-btn" onclick="navigate('audiobooks')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>Hörbücher
</button>
<div id="abs-book-detail-content"></div>
</div>
<!-- SPOTIFY IMPORTER -->
<div class="page" id="page-spotify-importer">
<div class="page-header">
<div class="page-title">Spotify <em>Importer</em></div>
<span style="background:rgba(29,185,84,.15);color:#1DB954;border:1px solid rgba(29,185,84,.3);font-size:11px;padding:4px 10px;border-radius:20px;font-weight:600;flex-shrink:0">Spotify</span>
</div>
<div class="search-wrap" style="padding:0 0 20px">
<div style="display:flex;gap:8px">
<div class="search-box" style="background:var(--surface);border-color:var(--border2);flex:1">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M8 11.973c2.5-1.473 5.5-.973 7.5.527M9.5 14.473c1.5-.973 3.5-.973 5 .027M7 9c3-1.5 6.5-1 9 1"/></svg>
<input id="spotify-link-input" type="url" placeholder="Spotify Link einfügen (Track, Album oder Playlist)…" style="font-size:15px" oninput="clearSpotifyPreview()">
</div>
<button class="btn-outline" style="padding:0 20px;font-size:14px;font-weight:700;white-space:nowrap;flex-shrink:0;background:linear-gradient(135deg,var(--accent),var(--accent-dark));color:#000;border-color:transparent" onclick="resolveSpotifyLink()">Importieren</button>
</div>
</div>
<div id="spotify-importer-hint" style="background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:16px;font-size:13px;color:var(--text2);line-height:1.8">
<div style="font-weight:700;color:var(--text);margin-bottom:8px">So funktioniert's</div>
<div>1. Spotify Link für Track, Album oder Playlist einfügen</div>
<div>2. Metadaten werden direkt von Spotify geladen</div>
<div>3. Songs via yt-dlp herunterladen → direkt in Navidrome Library</div>
<div style="margin-top:10px;color:var(--text3);font-size:12px">Kein Spotify-Account erforderlich — funktioniert direkt über das Backend.</div>
</div>
<div id="spotify-preview" style="display:none;margin-top:20px">
<div id="spotify-preview-inner"></div>
<div id="spotify-enrich-status" style="display:none"></div>
<div id="spotify-tracks-section" style="display:none">
<div class="section-header" style="margin-top:8px">
<span class="section-title" id="spotify-tracks-title">Tracks</span>
</div>
<div id="spotify-tracks-list" class="song-list"></div>
</div>
</div>
</div>
</main>
</div>
<!-- QUEUE PANEL -->
<div class="queue-panel" id="queue-panel">
<div class="queue-title">Warteschlange</div>
<div class="queue-list" id="queue-list"></div>
</div>
<!-- PLAYER -->
<div class="player">
<div class="player-drag-handle"></div>
<div class="player-track">
<div class="player-art" id="player-art" style="cursor:pointer" title="Jetzt läuft öffnen"><div class="no-art">♪</div></div>
<div class="player-info">
<div style="display:flex;align-items:center;gap:6px">
<div class="player-title" id="player-title">Kein Titel</div>
<button id="btn-star-current" onclick="starCurrent()" title="Favorit" style="background:none;border:none;cursor:pointer;padding:2px;flex-shrink:0;display:flex;align-items:center">
<svg id="star-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--text3)" stroke-width="2" style="transition:fill .2s,stroke .2s"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
</button>
</div>
<div class="player-artist">
<span id="player-album-name" onclick="playerBarGoAlbum()" style="cursor:pointer" title="Zum Album"></span>
</div>
<div class="player-artist" id="player-artist">
<span id="player-artist-name">—</span>
</div>
<div class="player-status-row">
<span class="status-badge badge-shuffle" id="badge-shuffle">🔀 Shuffle</span>
<span class="status-badge badge-repeat" id="badge-repeat">🔁 Repeat</span>
</div>
</div>
</div>
<div class="player-controls">
<div class="ctrl-buttons">
<button class="ctrl-btn" id="btn-shuffle" onclick="toggleShuffle()" title="Shuffle">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/></svg>
</button>
<button class="ctrl-btn" onclick="prevSong()">
<svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor"><polygon points="19 20 9 12 19 4 19 20"/><line x1="5" y1="19" x2="5" y2="5" stroke="currentColor" stroke-width="2"/></svg>
</button>
<button class="play-pause-btn" id="btn-play-pause" onclick="togglePlay()">
<svg id="play-icon" width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<button class="ctrl-btn" onclick="nextSong()">
<svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 4 15 12 5 20 5 4"/><line x1="19" y1="5" x2="19" y2="19" stroke="currentColor" stroke-width="2"/></svg>
</button>
<button class="ctrl-btn" id="btn-repeat" onclick="toggleRepeat()" title="Wiederholen">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
</button>
</div>
<div class="progress-bar-wrap">
<span class="progress-time" id="time-cur">0:00</span>
<div class="progress-bar" id="progress-bar"><div class="progress-fill" id="progress-fill"></div></div>
<span class="progress-time right" id="time-total">0:00</span>
</div>
</div>
<div class="player-right">
<button class="icon-btn" onclick="downloadCurrent()" title="Herunterladen">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
</button>
<button class="icon-btn" onclick="toggleQueue()" title="Warteschlange">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
</button>
<button class="icon-btn" id="btn-radio-mode" onclick="toggleRadioMode()" title="Radio-Modus: endloser Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="2"/><path d="M4.93 4.93a10 10 0 0 0 0 14.14"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M7.76 7.76a6 6 0 0 0 0 8.49"/><path d="M16.24 7.76a6 6 0 0 1 0 8.49"/></svg>
</button>
<div class="volume-wrap">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
<input type="range" class="volume-slider volume-slider-sync" id="volume-slider" min="0" max="100" value="50" oninput="setVolume(this.value)">
</div>
</div>
</div>
</div>
<!-- ABS MINI BAR (overlays the music player bar when an audiobook is active) -->
<div id="abs-mini-bar" class="abs-mini-bar">
<div class="abs-mini-prog-strip"><div id="abs-mini-prog-fill" class="abs-mini-prog-fill"></div></div>
<div id="abs-mini-cover" class="abs-mini-cover" onclick="absOpenPlayer()">📚</div>
<div class="abs-mini-info" onclick="absOpenPlayer()">
<div id="abs-mini-title" class="abs-mini-title">—</div>
<div id="abs-mini-chapter" class="abs-mini-chapter"></div>
</div>
<button class="abs-mini-play" id="abs-mini-play" onclick="absTogglePlay()">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<button class="abs-mini-open" onclick="absOpenPlayer()" title="Öffnen">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="18 15 12 9 6 15"/></svg>
</button>
</div>
<!-- PREVIEW BAR (iTunes 30s) -->
<div class="preview-bar" id="preview-bar">
<div class="player-art" id="preview-art" style="width:42px;height:42px;border-radius:6px"><div class="no-art" style="font-size:16px">♪</div></div>
<div class="preview-info">
<div class="preview-title" id="preview-title">—<span class="preview-badge">30s Preview</span></div>
<div class="preview-sub" id="preview-sub">iTunes Store</div>
</div>
<div class="preview-prog"><div class="preview-fill" id="preview-fill"></div></div>
<button class="preview-close" onclick="stopPreview()">✕</button>
</div>
<!-- DOWNLOAD MODAL -->
<div class="modal-overlay" id="download-modal">
<div class="modal">
<h2>Auf Server herunterladen</h2>
<p class="modal-sub">Der NovaSonic Backend lädt die Musik via yt-dlp herunter und speichert sie direkt in deinem Navidrome-Musikordner.</p>
<div class="modal-track-info" id="dl-modal-info"></div>
<div class="modal-opts" id="dl-modal-opts">
<div class="modal-opt selected" data-type="track" onclick="selectDlType(this,'track')">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/></svg>
<div><div class="opt-label">Einzelner Track</div><div class="opt-sub">Nur diesen Titel herunterladen</div></div>
</div>
<div class="modal-opt" data-type="album" onclick="selectDlType(this,'album')">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>
<div><div class="opt-label">Ganzes Album</div><div class="opt-sub" id="dl-album-sub">Album herunterladen</div></div>
</div>
<div class="modal-opt" data-type="artist" onclick="selectDlType(this,'artist')">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
<div><div class="opt-label">Ganzer Künstler</div><div class="opt-sub">Alle Top-Alben des Künstlers</div></div>
</div>
</div>
<div class="modal-disclaimer">
ℹ️ Bitte lade nur Musik herunter, die du besitzt oder für die du eine Lizenz hast. Dieses Tool ist für Backup-Zwecke und eigene Inhalte gedacht.
</div>
<div class="modal-btns">
<button class="modal-btns btn-cancel" style="flex:1;padding:12px;border-radius:9px;font-family:var(--font);font-size:14px;font-weight:600;cursor:pointer;border:none;background:var(--surface2);color:var(--text2)" onclick="closeDlModal()">Abbrechen</button>
<button class="modal-btns btn-download" style="flex:1;padding:12px;border-radius:9px;font-family:var(--font);font-size:14px;font-weight:600;cursor:pointer;border:none;background:linear-gradient(135deg,var(--blue),#2d6ad4);color:#fff" id="btn-do-download" onclick="executeDownload()">Herunterladen</button>
</div>
</div>
</div>
<!-- SETTINGS MODAL -->
<div class="modal-overlay" id="settings-modal">
<div class="modal" style="padding:0;display:flex;flex-direction:column;max-height:85vh;overflow:hidden;width:min(480px,calc(100vw - 32px))">
<!-- Sticky header -->
<div style="display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);flex-shrink:0">
<div style="display:flex;align-items:center;gap:12px">
<div style="width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--accent),var(--accent-dark));display:flex;align-items:center;justify-content:center;font-size:17px;box-shadow:0 4px 12px var(--accent-glow)">⚙</div>
<div>
<div style="font-size:17px;font-weight:800;letter-spacing:-.3px">Einstellungen</div>
<div style="font-size:11px;color:var(--text2);margin-top:1px">NovaSonic</div>
</div>
</div>
<button onclick="closeSettings()" style="background:var(--surface2);border:none;color:var(--text2);width:32px;height:32px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:15px;flex-shrink:0;transition:background .15s" onmouseover="this.style.background='var(--surface)'" onmouseout="this.style.background='var(--surface2)'">✕</button>
</div>
<!-- Scrollable body -->
<div style="flex:1;overflow-y:auto;padding:18px 24px 8px">
<div class="settings-section">
<h3 style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
<span style="width:24px;height:24px;border-radius:6px;background:var(--blue-dim);display:inline-flex;align-items:center;justify-content:center;font-size:12px">🌐</span>
Navidrome Server
</h3>
<div class="field"><label>Server URL</label><input id="s-url" type="url" placeholder="https://music.example.com"></div>
<div class="field"><label>Benutzername</label><input id="s-user" type="text" placeholder="admin"></div>
<div class="field"><label>Passwort</label><input id="s-pass" type="password" placeholder="(unverändert lassen)"></div>
</div>
<div class="settings-section">
<h3 style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
<span style="width:24px;height:24px;border-radius:6px;background:var(--accent-dim);display:inline-flex;align-items:center;justify-content:center;font-size:12px">🔑</span>
Discover APIs
</h3>
<div class="field">
<label>Last.fm API Key</label>
<input id="s-lastfm" type="text" placeholder="Für Künstler-Infos & Empfehlungen">
<div class="hint">Kostenlos unter last.fm/api/account/create</div>
</div>
<div class="field">
<label>NovaSonic Backend URL</label>
<input id="s-backend" type="url" placeholder="http://192.168.x.x:3700">
<div class="hint">Für Download-to-Server via yt-dlp</div>
</div>
<div class="settings-row" style="margin-top:8px;border-radius:9px">
<div>
<div style="font-size:13px;font-weight:500">Backend Status</div>
<div class="hint" id="backend-status-text">Nicht verbunden</div>
</div>
<div style="display:flex;align-items:center;gap:8px">
<div class="status-dot" id="backend-status-dot"></div>
<button class="btn-outline" style="padding:6px 12px;font-size:12px" onclick="checkBackend()">Testen</button>
</div>
</div>
</div>
<div class="settings-section">
<h3 style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
<span style="width:24px;height:24px;border-radius:6px;background:var(--green-dim);display:inline-flex;align-items:center;justify-content:center;font-size:12px">📚</span>
Audiobookshelf
</h3>
<div class="field">
<label>Server URL</label>
<input id="abs-s-url" type="url" placeholder="https://books.example.com">
</div>
<div class="field">
<label>API Key</label>
<input id="abs-s-apikey" type="password" placeholder="Aus ABS: Profil → API Token">
<div class="hint">Einstellungen → Profil → API Token in Audiobookshelf</div>
</div>
<div id="abs-lib-picker" style="display:none" class="field">
<label>Bibliothek</label>
<select id="abs-lib-select" onchange="absSetLibrary(this.value)" style="width:100%;padding:10px 12px;background:var(--bg2);border:1px solid var(--border2);border-radius:var(--radius-sm);color:var(--text);font-family:var(--font);font-size:14px;outline:none"></select>
</div>
<div style="display:flex;gap:8px;margin-top:8px">
<button class="btn-outline" style="flex:1;padding:9px;font-size:13px;color:var(--danger);border-color:rgba(224,85,85,0.3)" onclick="absDisconnect()">Trennen</button>
<button class="btn-outline" id="abs-s-connect-btn" style="flex:1;padding:9px;font-size:13px;font-weight:700" onclick="absConnectSettings()">Verbinden</button>
</div>
<span id="abs-s-status" style="font-size:12px;color:var(--green);display:block;margin-top:6px;min-height:16px"></span>
</div>
<div class="settings-section">
<h3 style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
<span style="width:24px;height:24px;border-radius:6px;background:var(--surface2);display:inline-flex;align-items:center;justify-content:center;font-size:12px">🎨</span>
Theme
</h3>
<div id="theme-picker" class="theme-picker"></div>
</div>
</div>
<!-- Sticky footer -->
<div style="display:flex;flex-direction:column;gap:8px;padding:12px 24px 16px;border-top:1px solid var(--border);flex-shrink:0;background:var(--surface);border-radius:0 0 18px 18px">
<div style="display:flex;gap:8px">
<button style="flex:1;padding:12px;border-radius:9px;font-family:var(--font);font-size:14px;font-weight:600;cursor:pointer;border:none;background:var(--surface2);color:var(--text2)" onclick="closeSettings()">Abbrechen</button>
<button style="flex:1;padding:12px;border-radius:9px;font-family:var(--font);font-size:14px;font-weight:600;cursor:pointer;border:none;background:linear-gradient(135deg,var(--accent),var(--accent-dark));color:#000" onclick="saveSettings()">Speichern</button>
</div>
<button style="width:100%;padding:10px;border-radius:9px;font-family:var(--font);font-size:13px;font-weight:600;cursor:pointer;background:none;border:1px solid rgba(224,85,85,0.3);color:var(--danger);transition:background .15s" onmouseover="this.style.background='rgba(224,85,85,0.1)'" onmouseout="this.style.background='none'" onclick="confirmDisconnect()">Abmelden</button>
</div>
</div>
</div>
<!-- JOB PANEL -->
<div class="jobs-panel" id="jobs-panel"></div>
<div class="toast-container" id="toast-container"></div>
<audio id="audio-element" preload="auto" playsinline crossorigin="anonymous"></audio>
<audio id="preview-audio"></audio>
<audio id="abs-audio"></audio>
<!-- AUDIOBOOKSHELF PLAYER OVERLAY -->
<div id="abs-player" class="abs-player-overlay">
<!-- Header -->
<div class="abs-player-header">
<button class="abs-hbtn" onclick="absClosePlayer()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<span class="abs-player-label">Hörbuch</span>
<div style="width:36px"></div>
</div>
<!-- Cover -->
<div class="abs-cover-area">
<div class="abs-cover-wrap">
<img id="abs-cover" src="" alt="" class="abs-cover-img" style="display:none">
<div id="abs-cover-placeholder" class="abs-cover-ph">📚</div>
</div>
</div>
<!-- Info -->
<div class="abs-player-info">
<div id="abs-title" class="abs-player-title">—</div>
<div id="abs-author" class="abs-player-author"></div>
<div id="abs-chapter-name" class="abs-player-chapter"></div>
</div>
<!-- Progress bar -->
<div class="abs-progress-area">
<div class="abs-prog-bar" id="abs-prog-bar"
onmousedown="absProgDown(event)" onmousemove="absProgMove(event)" onmouseup="absProgUp(event)"
ontouchstart="absProgDown(event)" ontouchmove="absProgMove(event)" ontouchend="absProgUp(event)">
<div class="abs-prog-track">
<div class="abs-prog-fill" id="abs-prog-fill"></div>
<div class="abs-prog-thumb" id="abs-prog-thumb"></div>
</div>
</div>
<div class="abs-prog-times">
<span id="abs-time-cur">0:00</span>
<span id="abs-time-rem">-0:00</span>
</div>
</div>
<!-- Main controls -->
<div class="abs-controls">
<button class="abs-ctrl-btn" onclick="absPrevChapter()" title="Voriges Kapitel">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><polygon points="19 20 9 12 19 4 19 20"/><line x1="5" y1="19" x2="5" y2="5" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
</button>
<button class="abs-ctrl-btn abs-skip-btn" onclick="absSkipSecs(-10)" title="-10s">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
<path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10"/>
<polyline points="15 9 12 6 9 9"/>
<path d="M12 6v4"/>
</svg>
<span class="abs-skip-label">10</span>
</button>
<button class="abs-play-pause" id="abs-playpause-btn" onclick="absTogglePlay()">
<svg width="28" height="28" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<button class="abs-ctrl-btn abs-skip-btn" onclick="absSkipSecs(30)" title="+30s">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
<path d="M12 22C17.523 22 22 17.523 22 12S17.523 2 12 2 2 6.477 2 12"/>
<polyline points="9 9 12 6 15 9"/>
<path d="M12 6v4"/>
</svg>
<span class="abs-skip-label">30</span>
</button>
<button class="abs-ctrl-btn" onclick="absNextChapter()" title="Nächstes Kapitel">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 4 15 12 5 20 5 4"/><line x1="19" y1="5" x2="19" y2="19" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
</button>
</div>
<!-- Extra controls -->
<div class="abs-extra-controls">
<!-- Speed -->
<div class="abs-picker-wrap">
<button class="abs-extra-btn" id="abs-speed-btn" onclick="absToggleSpeedPicker()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<span id="abs-speed-val">1×</span>
</button>
<div id="abs-speed-picker" class="abs-picker">
<button data-speed="0.75" onclick="absSetSpeed(0.75)">0.75×</button>
<button data-speed="1" onclick="absSetSpeed(1)">1×</button>
<button data-speed="1.25" onclick="absSetSpeed(1.25)">1.25×</button>
<button data-speed="1.5" onclick="absSetSpeed(1.5)">1.5×</button>
<button data-speed="1.75" onclick="absSetSpeed(1.75)">1.75×</button>
<button data-speed="2" onclick="absSetSpeed(2)">2×</button>
</div>
</div>
<!-- Sleep timer -->
<div class="abs-picker-wrap">
<button class="abs-extra-btn" id="abs-sleep-btn" onclick="absToggleSleepPicker()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
<div id="abs-sleep-picker" class="abs-picker abs-picker-right">
<button onclick="absSleepIn(0)">Aus</button>
<button onclick="absSleepIn(5)">5 Min</button>
<button onclick="absSleepIn(10)">10 Min</button>
<button onclick="absSleepIn(15)">15 Min</button>
<button onclick="absSleepIn(30)">30 Min</button>
<button onclick="absSleepIn(45)">45 Min</button>
<button onclick="absSleepIn(60)">60 Min</button>
<div class="abs-sleep-custom-row">
<span class="abs-sleep-custom-label">Eigene Zeit</span>
<div class="abs-sleep-custom-controls">
<input id="abs-sleep-custom-input" type="number" min="1" max="999" placeholder="Min" onkeydown="if(event.key==='Enter'){var v=parseInt(this.value);if(v>0)absSleepIn(v);}">
<button class="abs-sleep-custom-ok" onclick="var v=parseInt(document.getElementById('abs-sleep-custom-input').value);if(v>0)absSleepIn(v);">OK</button>
</div>
</div>
</div>
</div>
<!-- Chapter list toggle -->
<button class="abs-extra-btn" onclick="absToggleChapters()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
Kapitel
</button>
<!-- Chapter track mode -->
<button class="abs-extra-btn" id="abs-chtrack-btn" onclick="absToggleChapterTrack()" title="Kapitel-Track: Fortschritt nur im aktuellen Kapitel anzeigen">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12"/><line x1="12" y1="12" x2="15" y2="15"/><line x1="3" y1="12" x2="6" y2="12" stroke-width="2.5"/></svg>
Kap.Track
</button>
</div>
<!-- Volume -->
<div class="abs-volume-row">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3);flex-shrink:0"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/></svg>
<input type="range" class="abs-vol-slider volume-slider-sync" min="0" max="100" value="50" oninput="setVolume(this.value)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3);flex-shrink:0"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
</div>
<!-- Chapter list -->
<div id="abs-chapter-list" class="abs-chapter-list"></div>
</div>
<!-- CONTEXT MENU -->
<div class="ctx-menu" id="ctx-menu" style="display:none"></div>
<!-- ADD TO PLAYLIST MODAL -->
<div class="pl-modal" id="pl-modal">
<div class="inner">
<h3 id="pl-modal-title">Zu Playlist hinzufügen</h3>
<div class="pl-list" id="pl-list"></div>
<div class="pl-new-row">
<input class="pl-input" id="pl-new-name" placeholder="Neue Playlist erstellen…" type="text">
<div class="pl-btns">
<button style="background:var(--surface2);color:var(--text2)" onclick="closePlModal()">Abbrechen</button>
<button style="background:linear-gradient(135deg,var(--accent),var(--accent-dark));color:#000" onclick="createAndAddPlaylist()">Erstellen & Hinzufügen</button>
</div>
</div>
</div>
</div>
<!-- EDIT PLAYLIST MODAL -->
<div class="edit-pl-overlay" id="edit-pl-overlay" onclick="if(event.target===this)closeEditPlModal()">
<div class="edit-pl-box">
<div class="edit-pl-header">
<span>Playlist bearbeiten</span>
<button class="edit-pl-close" onclick="closeEditPlModal()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<div class="edit-pl-body">
<div class="edit-pl-cover-wrap">
<div class="edit-pl-cover" id="edit-pl-cover"><div class="no-art">🎶</div></div>
<div class="edit-pl-cover-actions">
<button class="edit-pl-cover-btn" onclick="document.getElementById('edit-pl-file').click()" title="Bild hochladen">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
</button>
<button class="edit-pl-cover-btn" onclick="editPlRemoveCover()" title="Bild entfernen">
<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 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>
</button>
</div>
<input type="file" id="edit-pl-file" accept="image/*" style="display:none" onchange="editPlCoverSelected(this)">
</div>
<div class="edit-pl-fields">
<label class="edit-pl-label">Name <span style="color:var(--accent)">*</span></label>
<input class="edit-pl-input" id="edit-pl-name" type="text" placeholder="Playlist-Name">
<label class="edit-pl-label">Beschreibung</label>
<textarea class="edit-pl-input edit-pl-textarea" id="edit-pl-desc" placeholder="Optionale Beschreibung…" rows="3"></textarea>
<div class="edit-pl-toggle-row">
<span>Öffentlich</span>
<div class="toggle" id="edit-pl-public-toggle" onclick="this.classList.toggle('on')"></div>
</div>
</div>
</div>
<div class="edit-pl-footer">
<button class="edit-pl-btn-delete" onclick="deletePlaylist()">Löschen</button>
<div style="display:flex;gap:10px">
<button class="edit-pl-btn-cancel" onclick="closeEditPlModal()">Abbrechen</button>
<button class="edit-pl-btn-save" onclick="saveEditPlaylist()">Speichern</button>
</div>
</div>
</div>
</div>
<!-- CONFIRM DIALOG -->
<div class="edit-pl-overlay" id="confirm-overlay" onclick="if(event.target===this)_confirmCancel()">
<div class="confirm-box">
<div class="confirm-title" id="confirm-title"></div>
<div class="confirm-msg" id="confirm-msg"></div>
<div class="confirm-footer">
<button class="edit-pl-btn-cancel" onclick="_confirmCancel()">Abbrechen</button>
<button class="edit-pl-btn-delete" id="confirm-ok-btn" onclick="_confirmOk()">Bestätigen</button>
</div>
</div>
</div>
<!-- SONG INFO MODAL -->
<div class="info-modal" id="info-modal">
<div class="inner">
<div class="info-header">
<div class="info-cover" id="info-cover"><div class="no-art" style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:32px">🎵</div></div>
<div><div class="info-top-title" id="info-top-title">—</div><div class="info-top-sub" id="info-top-sub">—</div></div>
</div>
<div class="info-rows" id="info-rows"></div>
<button class="info-close" onclick="closeInfoModal()">Schließen</button>
</div>
</div>
<!-- NOW PLAYING OVERLAY -->
<div class="np-overlay" id="np-overlay">
<div class="np-bg" id="np-bg-a"></div>
<div class="np-bg" id="np-bg-b" style="opacity:0"></div>
<div class="np-inner">
<div class="np-topbar">
<button class="np-close" onclick="closeNowPlaying()" title="Schließen">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<div class="np-tabs">
<button class="np-tab active" id="np-tab-player" onclick="switchNpTab('player')">Jetzt läuft</button>
<button class="np-tab" id="np-tab-lyrics" onclick="switchNpTab('lyrics')">Lyrics</button>
<button class="np-tab" id="np-tab-queue" onclick="switchNpTab('queue')">Queue</button>
</div>
<button class="np-menu-btn" onclick="openNpMenu()" title="Optionen">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="5" r="1.5" fill="currentColor"/><circle cx="12" cy="12" r="1.5" fill="currentColor"/><circle cx="12" cy="19" r="1.5" fill="currentColor"/></svg>
</button>
</div>
<div id="np-player-view">
<div class="np-cover" id="np-cover">
<div class="no-art">♪</div>
</div>
<div class="np-info">
<div class="np-text">
<div class="np-title" id="np-title">Kein Titel</div>
<div class="np-artist">
<span id="np-album-name" onclick="npGoAlbum()" style="cursor:pointer;opacity:.8">—</span>
</div>
<div class="np-artist" id="np-artist">
<span id="np-artist-name">—</span>
</div>
</div>
<button class="np-star" id="np-star-btn" onclick="npToggleStar()">
<svg id="np-star-icon" width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,.5)" stroke-width="2" style="transition:fill .2s,stroke .2s"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
</button>
</div>
<div class="np-progress">
<div class="np-bar" id="np-bar" onclick="npSeek(event)">
<div class="np-bar-fill" id="np-fill"></div>
</div>
<div class="np-times">
<span id="np-cur">0:00</span>
<span id="np-tot">0:00</span>
</div>
</div>
<div class="np-controls">
<button class="np-ctrl" id="np-shuffle-btn" onclick="toggleShuffle()" title="Shuffle">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/></svg>
</button>
<button class="np-ctrl" onclick="prevSong()">
<svg width="30" height="30" viewBox="0 0 24 24" fill="currentColor"><polygon points="19 20 9 12 19 4 19 20"/><line x1="5" y1="19" x2="5" y2="5" stroke="currentColor" stroke-width="2"/></svg>
</button>
<button class="np-play" id="np-play-btn" onclick="togglePlay()">
<svg id="np-play-icon" width="26" height="26" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<button class="np-ctrl" onclick="nextSong()">
<svg width="30" height="30" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 4 15 12 5 20 5 4"/><line x1="19" y1="5" x2="19" y2="19" stroke="currentColor" stroke-width="2"/></svg>
</button>
<button class="np-ctrl" id="np-repeat-btn" onclick="toggleRepeat()" title="Wiederholen">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
</button>
</div>
<div class="np-extras">
<button class="np-icon-btn" onclick="downloadCurrent()" title="Download">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
</button>
<div class="np-vol">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:rgba(255,255,255,.45)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/></svg>
<input type="range" class="np-vol-slider volume-slider-sync" id="np-vol-slider" min="0" max="100" value="50" oninput="setVolume(this.value)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:rgba(255,255,255,.45)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
</div>
<button class="np-icon-btn" id="np-radio-btn" onclick="toggleRadioMode()" title="Radio-Modus: endloser Shuffle">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="2"/><path d="M4.93 4.93a10 10 0 0 0 0 14.14"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M7.76 7.76a6 6 0 0 0 0 8.49"/><path d="M16.24 7.76a6 6 0 0 1 0 8.49"/></svg>
</button>
</div>
</div><!-- /np-player-view -->
<div id="np-lyrics-view">
<div id="np-lyrics-content"></div>
</div>
<div id="np-queue-view">
<div id="np-queue-list"></div>
</div>
</div>
</div>
<!-- NP ACTION SHEET -->
<div class="np-sheet-overlay" id="np-sheet-overlay" onclick="closeNpMenu()"></div>
<div class="np-sheet" id="np-sheet">
<div class="np-sheet-handle"></div>
<div class="np-sheet-song" id="np-sheet-song"></div>
<div id="np-sheet-items"></div>
</div>
<nav class="bottom-nav" id="bottom-nav">
<div class="bottom-nav-inner">
<div class="bnav-tabs">
<button class="bnav-tab active" id="bnav-tab-library" onclick="switchNavMode('library')">Bibliothek</button>
<button class="bnav-tab" id="bnav-tab-discover" onclick="switchNavMode('discover')">Entdecken</button>
</div>
<div class="bnav-pages" id="bnav-library">
<button class="bnav-item active" onclick="navigate('home')" data-page="home">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</button>
<button class="bnav-item" onclick="navigate('artists')" data-page="artists">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
Künstler
</button>
<button class="bnav-item" onclick="navigate('albums')" data-page="albums">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>
Alben
</button>
<button class="bnav-item" onclick="navigate('playlists')" data-page="playlists">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
Playlists
</button>
<button class="bnav-item" onclick="navigate('starred')" data-page="starred">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
Favoriten
</button>
<button class="bnav-item" id="bnav-audiobooks" onclick="navigate('audiobooks')" data-page="audiobooks" style="display:none">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
Bücher
</button>
</div>
<div class="bnav-pages" id="bnav-discover" style="display:none">
<button class="bnav-item" onclick="navigate('discover')" data-page="discover">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Suche
</button>
<button class="bnav-item" onclick="navigate('new-releases')" data-page="new-releases">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>
Releases
</button>
<button class="bnav-item" onclick="navigate('recommendations')" data-page="recommendations">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
Für dich
</button>
<button class="bnav-item" onclick="navigate('random')" data-page="random">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/></svg>
Zufällig
</button>
<button class="bnav-item" onclick="navigate('spotify-importer')" data-page="spotify-importer">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M8 11.973c2.5-1.473 5.5-.973 7.5.527M9.5 14.473c1.5-.973 3.5-.973 5 .027M7 9c3-1.5 6.5-1 9 1"/></svg>
Importer
</button>
</div>
</div>
</nav>
<!-- NOTIFICATION PANEL -->
<div id="notif-overlay" class="notif-overlay" onclick="toggleNotifPanel()"></div>
<div id="notif-panel" class="notif-panel">
<div class="notif-panel-hdr">
<span class="notif-panel-title">Benachrichtigungen</span>
<button class="notif-clear-btn" onclick="clearNotifs()">Alle löschen</button>
</div>
<div id="notif-list" class="notif-list"></div>
</div>
</body>
</html>