1005 lines
64 KiB
HTML
1005 lines
64 KiB
HTML
<!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>
|
||
|
||
<!-- ─── 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>
|
||
<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('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 class="section-header" style="margin-top:8px"><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>
|
||
</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="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" 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">—</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 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 id="abs-sleep-custom-row" class="abs-sleep-custom-row" onclick="if(!this.classList.contains('active')){this.classList.add('active');var inp=document.getElementById('abs-sleep-custom-input');inp.value='';inp.focus();}">
|
||
<span class="abs-sleep-custom-label">Eigene Zeit…</span>
|
||
<input id="abs-sleep-custom-input" type="number" min="1" max="999" placeholder="Min" onclick="event.stopPropagation()" onkeydown="if(event.key==='Enter'){var v=parseInt(this.value);if(v>0)absSleepIn(v);}">
|
||
<button onclick="event.stopPropagation();var v=parseInt(document.getElementById('abs-sleep-custom-input').value);if(v>0)absSleepIn(v);">OK</button>
|
||
</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" id="np-artist">
|
||
<span id="np-artist-name">—</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 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> |