Files
NovaSonic/index.html
T
HoneyandClaude Sonnet 4.6 f7a5247f17 feat: Spotify Importer — link → yt-dlp download + optional Navidrome playlist
- New 'Spotify Importer' page under Entdecken (sidebar + mobile nav)
- Backend: POST /api/spotify/resolve — resolves track/album/playlist URLs via Spotify API
- Backend: new 'playlist' download type accepting pre-resolved track list (no iTunes re-fetch)
- Frontend: paste Spotify link → preview with cover, type badge, track list
  - Track: single download or full album via iTunes fallback
  - Album: download all tracks with Spotify metadata
  - Playlist: download all tracks + optional 'Als Navidrome-Playlist speichern' checkbox
- Navidrome playlist creation: searches library by title/artist after download, creates playlist
- Spotify Client ID + Secret stored in app settings (setup screen + settings modal)
- iTunes metadata tags kept for all downloaded files (existing pipeline unchanged)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 00:56:11 +02:00

774 lines
51 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="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>
<!-- ─── 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-tabs">
<button class="setup-tab active" onclick="switchSetupTab('navidrome')">Navidrome</button>
<button class="setup-tab" onclick="switchSetupTab('discover')">Discover APIs</button>
</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>
<div class="setup-pane" id="pane-discover">
<div class="field">
<label>Last.fm API Key <span style="color:var(--text3);font-weight:400;text-transform:none">(optional)</span></label>
<input id="lastfm-key" type="text" placeholder="Kostenlos auf last.fm/api">
<div class="hint">Für Empfehlungen & Similar Artists. Kostenlos registrieren unter <strong>last.fm/api/account/create</strong></div>
</div>
<div class="field">
<label>NovaSonic Backend URL <span style="color:var(--text3);font-weight:400;text-transform:none">(optional)</span></label>
<input id="backend-url" type="url" placeholder="http://192.168.1.x:3700">
<div class="hint">Für Download-to-Server Funktion. Backend separat starten (novasonic-server.js)</div>
</div>
<div class="field">
<label>Spotify Client ID <span style="color:var(--text3);font-weight:400;text-transform:none">(optional)</span></label>
<input id="spotify-client-id" type="text" placeholder="Für Spotify Importer">
<div class="hint">Kostenlos unter developer.spotify.com/dashboard</div>
</div>
<div class="field">
<label>Spotify Client Secret <span style="color:var(--text3);font-weight:400;text-transform:none">(optional)</span></label>
<input id="spotify-client-secret" 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 class="header-btns">
<button class="hbtn" onclick="openSettings()" title="Einstellungen">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M14.24 9.76a4 4 0 0 1 0 4.48M4.93 4.93a10 10 0 0 0 0 14.14M9.76 9.76a4 4 0 0 0 0 4.48"/></svg>
</button>
<button class="hbtn danger" onclick="disconnect()" title="Trennen">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18.36 6.64a9 9 0 1 1-12.73 0"/><line x1="12" y1="2" x2="12" y2="12"/></svg>
</button>
</div>
</div>
<div class="search-wrap">
<div class="search-box" onclick="document.getElementById('search-input').focus()">
<svg width="13" height="13" 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="search-input" type="text" placeholder="Lokale Suche…" oninput="onLocalSearch(this.value)">
</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('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 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>
<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">
<div class="nav-section-title">Player</div>
<div class="nav-item" onclick="toggleQueue()">
<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>Warteschlange
</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" 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 class="section-header" style="margin-top:8px"><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-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-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-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>
</div>
</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>
<!-- 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-primary" style="padding:0 18px;font-size:14px;white-space:nowrap;flex-shrink:0" 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">Spotify Client ID + Secret in den Einstellungen → Discover APIs eintragen.<br>Kostenlos unter <strong style="color:var(--text2)">developer.spotify.com/dashboard</strong></div>
</div>
<div id="spotify-preview" style="display:none;margin-top:20px">
<div id="spotify-preview-inner"></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" onclick="playerBarGoAlbum()" style="cursor:pointer" title="Zum Album">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" id="player-artist">
<span id="player-artist-name" onclick="playerBarGoArtist()" style="cursor:pointer" title="Zum Künstler">—</span><span id="player-album-sep" style="color:var(--text3)"> · </span><span id="player-album-name" onclick="playerBarGoAlbum()" style="cursor:pointer" title="Zum Album"></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" onclick="seekTo(event)"><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" id="volume-slider" min="0" max="100" value="80" oninput="setVolume(this.value)">
</div>
</div>
</div>
</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),#c07010);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="field">
<label>Spotify Client ID</label>
<input id="s-spotify-id" type="text" placeholder="Für Spotify Importer">
<div class="hint">Kostenlos unter developer.spotify.com/dashboard</div>
</div>
<div class="field">
<label>Spotify Client Secret</label>
<input id="s-spotify-secret" type="password" placeholder="(unverändert lassen)">
</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>
<!-- Sticky footer -->
<div style="display:flex;gap:8px;padding:12px 24px 16px;border-top:1px solid var(--border);flex-shrink:0;background:var(--surface);border-radius:0 0 18px 18px">
<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),#c07010);color:#000" onclick="saveSettings()">Speichern</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"></audio>
<audio id="preview-audio"></audio>
<!-- 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),#c07010);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-cancel" onclick="closeEditPlModal()">Abbrechen</button>
<button class="edit-pl-btn-save" onclick="saveEditPlaylist()">Speichern</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"></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" id="np-artist">
<span id="np-artist-name" onclick="npGoArtist()" style="cursor:pointer">—</span><span id="np-album-sep" style="opacity:.5"> · </span><span id="np-album-name" onclick="npGoAlbum()" style="cursor:pointer;opacity:.8">—</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" id="np-vol-slider" min="0" max="100" value="80" oninput="setVolume(this.value);document.getElementById('volume-slider').value=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('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>
</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>
</body>
</html>