4325 lines
256 KiB
HTML
4325 lines
256 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Monochrome Music</title>
|
||
<meta name="theme-color" content="#000000" />
|
||
<meta name="mobile-web-app-capable" content="yes" />
|
||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||
<base href="/" />
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||
<meta name="apple-mobile-web-app-title" content="Monochrome" />
|
||
<meta name="description" content="A minimalist music streaming application" />
|
||
<link rel="apple-touch-icon" href="/assets/logo.svg" />
|
||
<link rel="manifest" href="/manifest.json" />
|
||
<link rel="icon" href="/assets/logo.svg" type="image/svg+xml" />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
<link rel="stylesheet" href="/styles.css" />
|
||
</head>
|
||
|
||
<body>
|
||
<audio id="audio-player" crossorigin="anonymous"></audio>
|
||
<div id="context-menu">
|
||
<ul>
|
||
<li data-action="shuffle-play-card" data-type-filter="album,playlist,mix,user-playlist">
|
||
Shuffle play
|
||
</li>
|
||
<li data-action="start-mix" data-type-filter="album,track">Start mix</li>
|
||
<li data-action="play-next">Play next</li>
|
||
<li data-action="add-to-queue">Add to queue</li>
|
||
<li
|
||
data-action="toggle-like"
|
||
data-label-track="Like"
|
||
data-label-album="Save album to library"
|
||
data-label-playlist="Save playlist to library"
|
||
>
|
||
Like
|
||
</li>
|
||
<li data-action="add-to-playlist" data-type-filter="track">Add to playlist</li>
|
||
<li data-action="go-to-artist" data-type-filter="track,album">Go to artist</li>
|
||
<li data-action="go-to-album" data-type-filter="track">Go to album</li>
|
||
<li data-action="copy-link">Copy link</li>
|
||
<li data-action="open-in-new-tab">Open in new tab</li>
|
||
<li data-action="track-info" data-type-filter="track">Track info</li>
|
||
<li data-action="open-original-url" data-type-filter="track">Open original URL</li>
|
||
<li data-action="download">Download</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="sort-menu" style="display: none">
|
||
<ul>
|
||
<li data-sort="custom">Playlist Order</li>
|
||
<li data-sort="added-newest" class="requires-added-date">Date Added (Newest)</li>
|
||
<li data-sort="added-oldest" class="requires-added-date">Date Added (Oldest)</li>
|
||
<li data-sort="title">Title (A-Z)</li>
|
||
<li data-sort="artist">Artist (A-Z)</li>
|
||
<li data-sort="album">Album (A-Z)</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="side-panel" class="side-panel">
|
||
<div class="panel-header">
|
||
<h3 id="side-panel-title">Panel</h3>
|
||
<div class="panel-controls" id="side-panel-controls">
|
||
<!-- Controls injected dynamically -->
|
||
</div>
|
||
</div>
|
||
<div id="side-panel-content" class="panel-content">
|
||
<!-- Content injected dynamically -->
|
||
</div>
|
||
</div>
|
||
|
||
<div id="fullscreen-cover-overlay" style="display: none">
|
||
<div class="fullscreen-cover-content">
|
||
<canvas id="visualizer-canvas"></canvas>
|
||
<button id="close-fullscreen-cover-btn" title="Close">×</button>
|
||
<button id="toggle-fullscreen-lyrics-btn" class="fullscreen-lyrics-toggle" title="Toggle Lyrics">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" />
|
||
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
||
<line x1="12" y1="19" x2="12" y2="22" />
|
||
<line x1="8" y1="22" x2="16" y2="22" />
|
||
</svg>
|
||
</button>
|
||
<div class="fullscreen-main-view">
|
||
<img
|
||
id="fullscreen-cover-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
alt="Album Cover"
|
||
/>
|
||
<div class="fullscreen-track-info">
|
||
<h2 id="fullscreen-track-title"></h2>
|
||
<h3 id="fullscreen-track-artist"></h3>
|
||
<div class="fullscreen-actions">
|
||
<button id="fs-like-btn" class="btn-icon like-btn" title="Like">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="heart-icon"
|
||
>
|
||
<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"
|
||
></path>
|
||
</svg>
|
||
</button>
|
||
<button id="fs-add-playlist-btn" class="btn-icon" title="Add to Playlist">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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>
|
||
</button>
|
||
<button id="fs-download-btn" class="btn-icon" title="Download">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||
<polyline points="7 10 12 15 17 10"></polyline>
|
||
<line x1="12" y1="15" x2="12" y2="3"></line>
|
||
</svg>
|
||
</button>
|
||
<button id="fs-cast-btn" class="btn-icon" title="Cast">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"></path>
|
||
<path d="M2 12a9 9 0 0 1 9 9"></path>
|
||
<path d="M2 17a5 5 0 0 1 5 5"></path>
|
||
<path d="M2 22h.01"></path>
|
||
</svg>
|
||
</button>
|
||
<button id="fs-queue-btn" class="btn-icon" title="Queue">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M3 5h.01" />
|
||
<path d="M3 12h.01" />
|
||
<path d="M3 19h.01" />
|
||
<path d="M8 5h13" />
|
||
<path d="M8 12h13" />
|
||
<path d="M8 19h13" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div id="fullscreen-next-track" style="display: none">
|
||
<span class="label">Up Next: </span>
|
||
<span class="value"></span>
|
||
</div>
|
||
</div>
|
||
<div class="fullscreen-controls">
|
||
<div class="fullscreen-progress-container">
|
||
<span id="fs-current-time">0:00</span>
|
||
<div id="fs-progress-bar" class="progress-bar">
|
||
<div id="fs-progress-fill" class="progress-fill"></div>
|
||
</div>
|
||
<span id="fs-total-duration">0:00</span>
|
||
</div>
|
||
<div class="fullscreen-buttons">
|
||
<button id="fs-shuffle-btn" title="Shuffle">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-shuffle-icon lucide-shuffle"
|
||
>
|
||
<path d="m18 14 4 4-4 4" />
|
||
<path d="m18 2 4 4-4 4" />
|
||
<path d="M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" />
|
||
<path d="M2 6h1.972a4 4 0 0 1 3.6 2.2" />
|
||
<path d="M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" />
|
||
</svg>
|
||
</button>
|
||
<button id="fs-prev-btn" title="Previous">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-arrow-left-to-line-icon lucide-arrow-left-to-line"
|
||
>
|
||
<path d="M3 19V5" />
|
||
<path d="m13 6-6 6 6 6" />
|
||
<path d="M7 12h14" />
|
||
</svg>
|
||
</button>
|
||
<button id="fs-play-pause-btn" class="play-pause-btn" title="Play">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="32"
|
||
height="32"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
||
</svg>
|
||
</button>
|
||
<button id="fs-next-btn" title="Next">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-arrow-right-to-line-icon lucide-arrow-right-to-line"
|
||
>
|
||
<path d="M17 12H3" />
|
||
<path d="m11 18 6-6-6-6" />
|
||
<path d="M21 5v14" />
|
||
</svg>
|
||
</button>
|
||
<button id="fs-repeat-btn" title="Repeat">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-repeat-icon lucide-repeat"
|
||
>
|
||
<path d="m17 2 4 4-4 4" />
|
||
<path d="M3 11v-1a4 4 0 0 1 4-4h14" />
|
||
<path d="m7 22-4-4 4-4" />
|
||
<path d="M21 13v1a4 4 0 0 1-4 4H3" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="fullscreen-volume-container">
|
||
<button id="fs-volume-btn" class="fs-volume-btn" title="Mute">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
|
||
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>
|
||
</svg>
|
||
</button>
|
||
<div id="fs-volume-bar" class="fs-volume-bar">
|
||
<div id="fs-volume-fill" class="fs-volume-fill"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="playlist-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content">
|
||
<h3 id="playlist-modal-title">Create Playlist</h3>
|
||
<input
|
||
type="text"
|
||
id="playlist-name-input"
|
||
class="template-input"
|
||
placeholder="Playlist name"
|
||
style="margin: 1rem 0"
|
||
/>
|
||
<input
|
||
type="url"
|
||
id="playlist-cover-input"
|
||
class="template-input"
|
||
placeholder="Cover image URL (optional)"
|
||
style="margin: 0.5rem 0"
|
||
/>
|
||
<p style="font-size: 0.8rem; margin: 0">
|
||
Upload your cover to
|
||
<a href="https://catbox.moe" style="text-decoration: underline">catbox.moe</a> then paste the link
|
||
here. (or any other service that allows directly linking to a file, but we recommend catbox due to
|
||
their long-term storage of your files)
|
||
</p>
|
||
<textarea
|
||
id="playlist-description-input"
|
||
class="template-input"
|
||
placeholder="Description (optional)"
|
||
style="margin: 0.5rem 0; min-height: 80px; resize: vertical"
|
||
></textarea>
|
||
<br />
|
||
<div
|
||
id="csv-import-section"
|
||
style="
|
||
display: none;
|
||
margin: 1rem 0;
|
||
padding: 1rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
background: var(--background-secondary);
|
||
"
|
||
>
|
||
<p style="margin-bottom: 0.5rem; font-size: 0.9rem">Import from CSV</p>
|
||
<p style="font-size: 0.8rem; margin: 0">
|
||
Spotify and Apple Music are supported. (Apple Music is prone to errors.) Please use
|
||
<a href="https://exportify.app/" style="text-decoration: underline">Exportify (Spotify)</a> or
|
||
<a
|
||
href="https://www.tunemymusic.com/transfer/spotify-to-apple-music"
|
||
style="text-decoration: underline"
|
||
>TuneMyMusic (Apple Music)</a
|
||
>
|
||
to export your playlist into a .csv. Make sure its headers are in English.
|
||
</p>
|
||
<br />
|
||
<input
|
||
type="file"
|
||
id="csv-file-input"
|
||
class="btn-secondary"
|
||
accept=".csv"
|
||
style="width: 100%; margin-bottom: 0.5rem"
|
||
/>
|
||
<p style="font-size: 0.8rem; margin: 0">
|
||
<b>Warning:</b> This feature isn't perfect and is prone to errors! Please check your playlist
|
||
after to remove any unwanted songs that were added by the system.
|
||
</p>
|
||
</div>
|
||
|
||
<div
|
||
class="setting-item"
|
||
style="margin-bottom: 1rem; padding: 0; border: none; background: transparent"
|
||
>
|
||
<div class="info">
|
||
<span class="label">Public Playlist</span>
|
||
<span class="description">Visible to anyone with the link.</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="playlist-public-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="modal-actions">
|
||
<button id="playlist-share-btn" class="btn-secondary" style="display: none">Share</button>
|
||
<button id="playlist-modal-cancel" class="btn-secondary">Cancel</button>
|
||
<button id="playlist-modal-save" class="btn-primary">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="folder-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content">
|
||
<h3 id="folder-modal-title">Create Folder</h3>
|
||
<input
|
||
type="text"
|
||
id="folder-name-input"
|
||
class="template-input"
|
||
placeholder="Folder name"
|
||
style="margin: 1rem 0"
|
||
/>
|
||
<input
|
||
type="url"
|
||
id="folder-cover-input"
|
||
class="template-input"
|
||
placeholder="Icon URL (optional)"
|
||
style="margin: 0.5rem 0"
|
||
/>
|
||
<div class="modal-actions">
|
||
<button id="folder-modal-cancel" class="btn-secondary">Cancel</button>
|
||
<button id="folder-modal-save" class="btn-primary">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="playlist-select-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content">
|
||
<h3>Add to Playlist</h3>
|
||
<div id="playlist-select-list" class="modal-list">
|
||
<!-- Options will be injected here -->
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button id="playlist-select-cancel" class="btn-secondary">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="shortcuts-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content medium">
|
||
<div class="shortcuts-header">
|
||
<h3>Keyboard Shortcuts</h3>
|
||
<button class="close-shortcuts">×</button>
|
||
</div>
|
||
<div class="shortcuts-content">
|
||
<div class="shortcut-item"><kbd>Space</kbd><span>Play / Pause</span></div>
|
||
<div class="shortcut-item"><kbd>→</kbd><span>Seek forward 10s</span></div>
|
||
<div class="shortcut-item"><kbd>←</kbd><span>Seek backward 10s</span></div>
|
||
<div class="shortcut-item"><kbd>Shift</kbd> + <kbd>→</kbd><span>Next track</span></div>
|
||
<div class="shortcut-item"><kbd>Shift</kbd> + <kbd>←</kbd><span>Previous track</span></div>
|
||
<div class="shortcut-item"><kbd>↑</kbd><span>Volume up</span></div>
|
||
<div class="shortcut-item"><kbd>↓</kbd><span>Volume down</span></div>
|
||
<div class="shortcut-item"><kbd>M</kbd><span>Mute / Unmute</span></div>
|
||
<div class="shortcut-item"><kbd>S</kbd><span>Toggle shuffle</span></div>
|
||
<div class="shortcut-item"><kbd>R</kbd><span>Toggle repeat</span></div>
|
||
<div class="shortcut-item"><kbd>Q</kbd><span>Open queue</span></div>
|
||
<div class="shortcut-item"><kbd>L</kbd><span>Toggle lyrics</span></div>
|
||
<div class="shortcut-item"><kbd>/</kbd><span>Focus search</span></div>
|
||
<div class="shortcut-item"><kbd>Esc</kbd><span>Close modals</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="missing-tracks-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content wide">
|
||
<div class="missing-tracks-header">
|
||
<h3>Note</h3>
|
||
<button class="close-missing-tracks">×</button>
|
||
</div>
|
||
<div class="missing-tracks-content">
|
||
<p>
|
||
Unfortunately, some songs weren't able to be added. This could be an issue with our import
|
||
system - try searching for the song and adding it. It could also be due to Monochrome not having
|
||
the song :(
|
||
</p>
|
||
<div class="missing-tracks-list">
|
||
<h4>Missing Tracks:</h4>
|
||
<ul id="missing-tracks-list-ul"></ul>
|
||
</div>
|
||
</div>
|
||
<div class="missing-tracks-actions">
|
||
<button class="btn-secondary" id="close-missing-tracks-btn">OK</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="sleep-timer-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content" style="max-width: 300px">
|
||
<h3 style="text-align: center; margin-bottom: 1.5rem">Sleep Timer</h3>
|
||
<div class="timer-options">
|
||
<button class="timer-option btn-secondary" data-minutes="5">5 minutes</button>
|
||
<button class="timer-option btn-secondary" data-minutes="15">15 minutes</button>
|
||
<button class="timer-option btn-secondary" data-minutes="30">30 minutes</button>
|
||
<button class="timer-option btn-secondary" data-minutes="60">1 hour</button>
|
||
<button class="timer-option btn-secondary" data-minutes="120">2 hours</button>
|
||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem">
|
||
<input
|
||
type="number"
|
||
id="custom-minutes"
|
||
class="template-input"
|
||
placeholder="Custom"
|
||
min="1"
|
||
max="480"
|
||
/>
|
||
<button class="timer-option btn-primary" id="custom-timer-btn" style="padding: 0.5rem 1rem">
|
||
Set
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-actions" style="justify-content: center">
|
||
<button id="cancel-sleep-timer" class="btn-secondary">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="discography-download-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content" style="max-width: 500px">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem">
|
||
<h3 style="margin: 0">Download Discography</h3>
|
||
<button
|
||
class="close-modal-btn"
|
||
style="
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
color: var(--muted-foreground);
|
||
"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
<p style="margin-bottom: 1rem; color: var(--muted-foreground)">
|
||
Select which releases to download for <span id="discography-artist-name"></span>:
|
||
</p>
|
||
<div style="display: flex; flex-direction: column; gap: 1rem">
|
||
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer">
|
||
<input type="checkbox" id="download-albums" checked />
|
||
<span>Albums (<span id="albums-count">0</span>)</span>
|
||
</label>
|
||
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer">
|
||
<input type="checkbox" id="download-eps" />
|
||
<span>EPs (<span id="eps-count">0</span>)</span>
|
||
</label>
|
||
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer">
|
||
<input type="checkbox" id="download-singles" />
|
||
<span>Singles (<span id="singles-count">0</span>)</span>
|
||
</label>
|
||
</div>
|
||
<div class="modal-actions" style="margin-top: 1.5rem">
|
||
<button class="btn-secondary" id="cancel-discography-download">Cancel</button>
|
||
<button class="btn-primary" id="start-discography-download">Download</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="custom-db-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem">
|
||
<h3 style="margin: 0">Custom Database/Auth</h3>
|
||
<button
|
||
id="custom-db-reset"
|
||
class="btn-secondary danger"
|
||
style="padding: 0.4rem 0.8rem; font-size: 0.8rem"
|
||
>
|
||
Reset to Defaults
|
||
</button>
|
||
</div>
|
||
<p style="font-size: 0.9rem; color: var(--muted-foreground); margin-bottom: 1rem">
|
||
Configure custom PocketBase and Firebase instances. Leave empty to use defaults.
|
||
<br />
|
||
A Guide To Set This Up Can Be Found
|
||
<a
|
||
href="https://github.com/SamidyFR/monochrome/blob/main/self-hosted-database.md"
|
||
style="text-decoration: underline"
|
||
>Here</a
|
||
>.
|
||
</p>
|
||
<div style="margin-bottom: 1rem">
|
||
<label style="display: block; margin-bottom: 0.5rem; font-size: 0.9rem">PocketBase URL</label>
|
||
<input
|
||
type="url"
|
||
id="custom-pb-url"
|
||
class="template-input"
|
||
placeholder="https://monodb.samidy.com"
|
||
/>
|
||
</div>
|
||
<div style="margin-bottom: 1rem">
|
||
<label style="display: block; margin-bottom: 0.5rem; font-size: 0.9rem"
|
||
>Firebase Configuration (JSON)</label
|
||
>
|
||
<textarea
|
||
id="custom-firebase-config"
|
||
class="template-input"
|
||
style="height: 150px; font-family: monospace; font-size: 0.8rem; resize: vertical"
|
||
placeholder="{'apiKey': '...', ...}"
|
||
></textarea>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button id="custom-db-cancel" class="btn-secondary">Cancel</button>
|
||
<button id="custom-db-save" class="btn-primary">Save & Reload</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tracker-modal" class="modal tracker-modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content wide" style="max-height: 85vh; display: flex; flex-direction: column">
|
||
<header class="detail-header" style="margin-bottom: 1rem; padding-bottom: 0">
|
||
<img
|
||
id="tracker-header-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
class="detail-header-image"
|
||
style="width: 140px; height: 140px; box-shadow: var(--shadow-md)"
|
||
alt=""
|
||
/>
|
||
<div class="detail-header-info">
|
||
<div class="type">Unreleased Project</div>
|
||
<h1 id="tracker-header-title" class="title" style="font-size: 2rem"></h1>
|
||
<div id="tracker-header-meta" class="meta"></div>
|
||
<div class="detail-header-actions">
|
||
<button class="btn-secondary" id="close-tracker-modal">Close</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div
|
||
id="tracker-filters"
|
||
style="padding: 0 2rem 1rem; display: flex; gap: 0.5rem; flex-wrap: wrap"
|
||
></div>
|
||
<div id="tracker-tracklist" class="track-list" style="overflow-y: auto; flex: 1">
|
||
<div class="track-list-header">
|
||
<span style="width: 40px; text-align: center">#</span>
|
||
<span>Title</span>
|
||
<span class="duration-header">Duration</span>
|
||
<span style="display: flex; justify-content: flex-end; opacity: 0.8">Menu</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="epilepsy-warning-modal" class="modal">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content">
|
||
<h3 style="color: #ef4444; display: flex; align-items: center; gap: 0.5rem">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
|
||
<line x1="12" y1="9" x2="12" y2="13" />
|
||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||
</svg>
|
||
Photosensitivity Warning
|
||
</h3>
|
||
<p style="margin: 1rem 0; line-height: 1.5">
|
||
The visualizer contains flashing lights and rapidly moving patterns that may trigger seizures for
|
||
people with photosensitive epilepsy.
|
||
</p>
|
||
<p style="margin-bottom: 1.5rem; font-size: 0.9rem; color: var(--muted-foreground)">
|
||
Viewer discretion is advised.
|
||
</p>
|
||
<div class="modal-actions" style="flex-direction: column; gap: 0.5rem">
|
||
<button id="epilepsy-accept-btn" class="btn-primary" style="width: 100%">
|
||
Proceed & Don't Show Again
|
||
</button>
|
||
<button id="epilepsy-cancel-btn" class="btn-secondary" style="width: 100%">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="sidebar-overlay"></div>
|
||
|
||
<div id="csv-import-progress" class="csv-import-progress" style="display: none">
|
||
<div class="progress-header">
|
||
<h4>Importing Tracks from CSV</h4>
|
||
<p class="progress-warning">
|
||
This can take a while depending on your playlist size. Please be patient.
|
||
</p>
|
||
</div>
|
||
<div class="progress-content">
|
||
<div class="current-track">Preparing import...</div>
|
||
<div
|
||
class="current-artist"
|
||
style="font-size: 0.9em; color: var(--text-secondary); margin-bottom: 0.5rem"
|
||
></div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="csv-progress-fill"></div>
|
||
</div>
|
||
<div class="progress-text">
|
||
<span id="csv-progress-current">0</span> / <span id="csv-progress-total">0</span> tracks processed
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="app-container">
|
||
<aside class="sidebar">
|
||
<div class="sidebar-content">
|
||
<div class="sidebar-logo">
|
||
<svg
|
||
class="app-logo"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="200"
|
||
height="200"
|
||
viewBox="14.75 14.75 70.5 70.5"
|
||
>
|
||
<g fill="currentColor">
|
||
<path
|
||
d="M38.25 14.75H85.25V61.75H61.75V38.25H38.25ZM14.75 38.25H38.25V61.75H61.75V85.25H14.75Z"
|
||
/>
|
||
</g>
|
||
</svg>
|
||
<span>Monochrome</span>
|
||
<button
|
||
id="sidebar-toggle"
|
||
class="btn-icon desktop-only"
|
||
style="margin-left: auto"
|
||
title="Collapse Sidebar"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m15 18-6-6 6-6" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<nav class="sidebar-nav main">
|
||
<ul>
|
||
<li class="nav-item" id="sidebar-nav-home">
|
||
<a href="/">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-house-icon lucide-house"
|
||
>
|
||
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
|
||
<path
|
||
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||
/>
|
||
</svg>
|
||
<span>Home</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-library">
|
||
<a href="/library">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m16 6 4 14" />
|
||
<path d="M12 6v14" />
|
||
<path d="M8 8v12" />
|
||
<path d="M4 4v16" />
|
||
</svg>
|
||
<span>Library</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-recent">
|
||
<a href="/recent">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
||
<path d="M3 3v5h5" />
|
||
<path d="M12 7v5l4 2" />
|
||
</svg>
|
||
<span>Recent</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-unreleased">
|
||
<a href="/unreleased">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<rect x="3" y="3" width="7" height="7" />
|
||
<rect x="14" y="3" width="7" height="7" />
|
||
<rect x="14" y="14" width="7" height="7" />
|
||
<rect x="3" y="14" width="7" height="7" />
|
||
</svg>
|
||
<span>Unreleased</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-donate">
|
||
<a href="https://ko-fi.com/monochromemusic" target="_blank" rel="noopener noreferrer">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M11 14h2a2 2 0 0 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 16" />
|
||
<path
|
||
d="m14.45 13.39 5.05-4.694C20.196 8 21 6.85 21 5.75a2.75 2.75 0 0 0-4.797-1.837.276.276 0 0 1-.406 0A2.75 2.75 0 0 0 11 5.75c0 1.2.802 2.248 1.5 2.946L16 11.95"
|
||
/>
|
||
<path d="m2 15 6 6" />
|
||
<path
|
||
d="m7 20 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a1 1 0 0 0-2.75-2.91"
|
||
/>
|
||
</svg>
|
||
<span>Donate</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-settings">
|
||
<a href="/settings">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path
|
||
d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"
|
||
/>
|
||
<circle cx="12" cy="12" r="3" />
|
||
</svg>
|
||
<span>Settings</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-account">
|
||
<a href="/account">
|
||
<svg
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
id="Layer_1"
|
||
data-name="Layer 1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
fill="currentColor"
|
||
>
|
||
<g stroke-width="0"></g>
|
||
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
||
<g>
|
||
<defs>
|
||
<style>
|
||
.cls-1 {
|
||
fill: none;
|
||
stroke: currentColor;
|
||
stroke-miterlimit: 10;
|
||
stroke-width: 1.9200000000000004;
|
||
}
|
||
</style>
|
||
</defs>
|
||
<circle class="cls-1" cx="12" cy="7.25" r="5.73"></circle>
|
||
<path
|
||
class="cls-1"
|
||
d="M1.5,23.48l.37-2.05A10.3,10.3,0,0,1,12,13h0a10.3,10.3,0,0,1,10.13,8.45l.37,2.05"
|
||
></path>
|
||
</g>
|
||
</svg>
|
||
<span>Account</span>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
<nav class="sidebar-nav">
|
||
<ul>
|
||
<li class="nav-item" id="sidebar-nav-about">
|
||
<a href="/about">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-info-icon lucide-info"
|
||
>
|
||
<circle cx="12" cy="12" r="10" />
|
||
<path d="M12 16v-4" />
|
||
<path d="M12 8h.01" />
|
||
</svg>
|
||
<span>About</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-download">
|
||
<a href="/download">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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" x2="12" y1="15" y2="3" />
|
||
</svg>
|
||
<span>Download</span>
|
||
</a>
|
||
</li>
|
||
<li class="nav-item" id="sidebar-nav-discord">
|
||
<a href="https://monochrome.samidy.com/discord" target="_blank">
|
||
<svg
|
||
width="64px"
|
||
height="64px"
|
||
viewBox="0 -28.5 256 256"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||
preserveAspectRatio="xMidYMid"
|
||
fill="#000000"
|
||
>
|
||
<g stroke-width="0"></g>
|
||
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
||
<g>
|
||
<g>
|
||
<path
|
||
d="M216.856339,16.5966031 C200.285002,8.84328665 182.566144,3.2084988 164.041564,0 C161.766523,4.11318106 159.108624,9.64549908 157.276099,14.0464379 C137.583995,11.0849896 118.072967,11.0849896 98.7430163,14.0464379 C96.9108417,9.64549908 94.1925838,4.11318106 91.8971895,0 C73.3526068,3.2084988 55.6133949,8.86399117 39.0420583,16.6376612 C5.61752293,67.146514 -3.4433191,116.400813 1.08711069,164.955721 C23.2560196,181.510915 44.7403634,191.567697 65.8621325,198.148576 C71.0772151,190.971126 75.7283628,183.341335 79.7352139,175.300261 C72.104019,172.400575 64.7949724,168.822202 57.8887866,164.667963 C59.7209612,163.310589 61.5131304,161.891452 63.2445898,160.431257 C105.36741,180.133187 151.134928,180.133187 192.754523,160.431257 C194.506336,161.891452 196.298154,163.310589 198.110326,164.667963 C191.183787,168.842556 183.854737,172.420929 176.223542,175.320965 C180.230393,183.341335 184.861538,190.991831 190.096624,198.16893 C211.238746,191.588051 232.743023,181.531619 254.911949,164.955721 C260.227747,108.668201 245.831087,59.8662432 216.856339,16.5966031 Z M85.4738752,135.09489 C72.8290281,135.09489 62.4592217,123.290155 62.4592217,108.914901 C62.4592217,94.5396472 72.607595,82.7145587 85.4738752,82.7145587 C98.3405064,82.7145587 108.709962,94.5189427 108.488529,108.914901 C108.508531,123.290155 98.3405064,135.09489 85.4738752,135.09489 Z M170.525237,135.09489 C157.88039,135.09489 147.510584,123.290155 147.510584,108.914901 C147.510584,94.5396472 157.658606,82.7145587 170.525237,82.7145587 C183.391518,82.7145587 193.761324,94.5189427 193.539891,108.914901 C193.539891,123.290155 183.391518,135.09489 170.525237,135.09489 Z"
|
||
fill="#8E8E96"
|
||
fill-rule="nonzero"
|
||
></path>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
<span>Discord</span>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="main-content">
|
||
<div id="page-background"></div>
|
||
<header class="main-header">
|
||
<div class="navigation-controls desktop-only">
|
||
<button id="nav-back" class="nav-btn" title="Go Back">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m15 18-6-6 6-6" />
|
||
</svg>
|
||
</button>
|
||
<button id="nav-forward" class="nav-btn" title="Go Forward">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m9 18 6-6-6-6" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<button class="hamburger-menu" id="hamburger-btn" title="Open navigation">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||
</svg>
|
||
</button>
|
||
<form class="search-bar" id="search-form">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="search-icon"
|
||
>
|
||
<circle cx="11" cy="11" r="8"></circle>
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||
</svg>
|
||
<input
|
||
type="search"
|
||
id="search-input"
|
||
placeholder="Search for tracks, artists, albums..."
|
||
autocomplete="off"
|
||
autocorrect="off"
|
||
autocapitalize="off"
|
||
spellcheck="false"
|
||
/>
|
||
<button
|
||
type="button"
|
||
class="search-clear-btn btn-icon"
|
||
title="Clear search"
|
||
style="display: none"
|
||
>
|
||
×
|
||
</button>
|
||
<div id="search-history" class="search-history" style="display: none"></div>
|
||
</form>
|
||
</header>
|
||
|
||
<div id="page-home" class="page">
|
||
<div id="home-welcome" style="display: none; text-align: center; padding: 4rem 2rem">
|
||
<h2 style="margin-bottom: 1rem">Welcome to Monochrome</h2>
|
||
<p style="color: var(--muted-foreground)">
|
||
You haven't listened to anything yet. Search for your favorite songs to get started!
|
||
</p>
|
||
</div>
|
||
|
||
<section class="content-section" id="home-editors-picks-section-empty" style="margin-top: 0">
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Editor's Picks</h2>
|
||
</div>
|
||
<div class="card-grid" id="home-editors-picks-empty"></div>
|
||
</section>
|
||
|
||
<div id="home-content" style="display: none">
|
||
<section class="content-section">
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Recommended Songs</h2>
|
||
<button
|
||
class="btn-secondary"
|
||
id="refresh-songs-btn"
|
||
title="Refresh"
|
||
style="padding: 4px 8px"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" />
|
||
<path d="M21 3v5h-5" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="track-list" id="home-recommended-songs"></div>
|
||
</section>
|
||
<section class="content-section">
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Recommended Albums</h2>
|
||
<button
|
||
class="btn-secondary"
|
||
id="refresh-albums-btn"
|
||
title="Refresh"
|
||
style="padding: 4px 8px"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" />
|
||
<path d="M21 3v5h-5" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="card-grid" id="home-recommended-albums"></div>
|
||
</section>
|
||
<section class="content-section">
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Recommended Artists</h2>
|
||
<button
|
||
class="btn-secondary"
|
||
id="refresh-artists-btn"
|
||
title="Refresh"
|
||
style="padding: 4px 8px"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" />
|
||
<path d="M21 3v5h-5" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="card-grid" id="home-recommended-artists"></div>
|
||
</section>
|
||
<section class="content-section">
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Jump Back In</h2>
|
||
<button
|
||
class="btn-secondary"
|
||
id="clear-recent-btn"
|
||
title="Clear History"
|
||
style="padding: 4px 8px"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M3 6h18" />
|
||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="card-grid" id="home-recent-mixed"></div>
|
||
</section>
|
||
<section class="content-section" id="home-editors-picks-section">
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Editor's Picks</h2>
|
||
</div>
|
||
<div class="card-grid" id="home-editors-picks"></div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="page-search" class="page">
|
||
<h2 class="section-title" id="search-results-title">Search Results</h2>
|
||
<div class="search-tabs">
|
||
<button class="search-tab active" data-tab="tracks">Tracks</button>
|
||
<button class="search-tab" data-tab="albums">Albums</button>
|
||
<button class="search-tab" data-tab="artists">Artists</button>
|
||
<button class="search-tab" data-tab="playlists">Playlists</button>
|
||
</div>
|
||
<div class="search-tab-content active" id="search-tab-tracks">
|
||
<div class="track-list" id="search-tracks-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="search-tab-albums">
|
||
<div class="card-grid" id="search-albums-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="search-tab-artists">
|
||
<div class="card-grid" id="search-artists-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="search-tab-playlists">
|
||
<div class="card-grid" id="search-playlists-container"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="page-library" class="page">
|
||
<section class="content-section">
|
||
<div class="library-header">
|
||
<h2>My Playlists</h2>
|
||
<button id="create-playlist-btn" class="btn-primary" style="margin: 10px 0">
|
||
Create Playlist
|
||
</button>
|
||
<button id="create-folder-btn" class="btn-secondary" style="margin: 10px 0 10px 10px">
|
||
Create Folder
|
||
</button>
|
||
</div>
|
||
<div class="card-grid" id="my-folders-container"></div>
|
||
<div class="card-grid" id="my-playlists-container"></div>
|
||
</section>
|
||
|
||
<section class="content-section">
|
||
<h2 class="section-title">Favorites</h2>
|
||
<div class="search-tabs">
|
||
<button class="search-tab active" data-tab="tracks">Liked Tracks</button>
|
||
<button class="search-tab" data-tab="albums">Albums</button>
|
||
<button class="search-tab" data-tab="artists">Artists</button>
|
||
<button class="search-tab" data-tab="playlists">Playlists and Mixes</button>
|
||
<button class="search-tab" data-tab="local">Local Files</button>
|
||
</div>
|
||
<div class="search-tab-content active" id="library-tab-tracks">
|
||
<div style="display: flex; justify-content: flex-start; gap: 0.5rem; margin-bottom: 0.5rem">
|
||
<button
|
||
id="shuffle-liked-tracks-btn"
|
||
class="btn-secondary"
|
||
style="
|
||
display: none;
|
||
width: 32px;
|
||
height: 32px;
|
||
padding: 0;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
"
|
||
title="Shuffle Liked Tracks"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m18 14 4 4-4 4" />
|
||
<path d="m18 2 4 4-4 4" />
|
||
<path d="M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" />
|
||
<path d="M2 6h1.972a4 4 0 0 1 3.6 2.2" />
|
||
<path d="M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
id="download-liked-tracks-btn"
|
||
class="btn-secondary"
|
||
style="
|
||
display: none;
|
||
width: 32px;
|
||
height: 32px;
|
||
padding: 0;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
"
|
||
title="Download Liked Tracks"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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>
|
||
<div class="track-list" id="library-tracks-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="library-tab-albums">
|
||
<div class="card-grid" id="library-albums-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="library-tab-artists">
|
||
<div class="card-grid" id="library-artists-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="library-tab-playlists">
|
||
<div class="card-grid" id="library-playlists-container"></div>
|
||
</div>
|
||
<div class="search-tab-content" id="library-tab-local">
|
||
<div class="track-list" id="library-local-container">
|
||
<div id="local-files-intro" style="padding: 20px; text-align: center">
|
||
<button id="select-local-folder-btn" class="btn-secondary">
|
||
<svg
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
style="margin-right: 8px"
|
||
>
|
||
<path
|
||
d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
|
||
></path>
|
||
</svg>
|
||
<span id="select-local-folder-text">Select Music Folder</span>
|
||
</button>
|
||
<p
|
||
id="local-browser-warning"
|
||
style="display: none; color: #ef4444; margin-top: 10px; font-size: 0.9rem"
|
||
>
|
||
Please use Google Chrome or Microsoft Edge to play local files.
|
||
</p>
|
||
<p style="margin-top: 10px; font-size: 0.9rem; color: var(--muted-foreground)">
|
||
Select a folder on your device to play local files. <br />
|
||
Note: Metadata reading is basic (FLAC/MP3 tags).
|
||
</p>
|
||
</div>
|
||
<div
|
||
id="local-files-header"
|
||
style="
|
||
display: none;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 10px 20px;
|
||
"
|
||
>
|
||
<h3>Local Files</h3>
|
||
<button
|
||
id="change-local-folder-btn"
|
||
class="btn-secondary"
|
||
style="font-size: 0.8rem; padding: 4px 8px"
|
||
>
|
||
Change Folder
|
||
</button>
|
||
</div>
|
||
<div id="local-files-list"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<div id="page-recent" class="page">
|
||
<div
|
||
style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem"
|
||
>
|
||
<h2 class="section-title" style="margin-bottom: 0">Recently played</h2>
|
||
<button class="btn-secondary" id="clear-history-btn" title="Clear History">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M3 6h18" />
|
||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
||
<line x1="10" y1="11" x2="10" y2="17" />
|
||
<line x1="14" y1="11" x2="14" y2="17" />
|
||
</svg>
|
||
<span>Clear</span>
|
||
</button>
|
||
</div>
|
||
<div class="track-list" id="recent-tracks-container"></div>
|
||
</div>
|
||
|
||
<div id="page-unreleased" class="page">
|
||
<div id="unreleased-content" style="padding: 1rem 0"></div>
|
||
</div>
|
||
|
||
<div id="page-tracker-artist" class="page">
|
||
<header class="detail-header">
|
||
<img
|
||
id="tracker-artist-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
alt=""
|
||
class="detail-header-image artist"
|
||
/>
|
||
<div class="detail-header-info">
|
||
<div class="type">Unreleased Artist</div>
|
||
<h1 class="title" id="tracker-artist-detail-name"></h1>
|
||
<div class="meta" id="tracker-artist-detail-meta"></div>
|
||
<div class="detail-header-actions">
|
||
<button id="play-tracker-artist-btn" class="btn-primary">
|
||
<span>Shuffle Play</span>
|
||
</button>
|
||
<button id="download-tracker-artist-btn" class="btn-primary">
|
||
<span>Download All</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div id="tracker-artist-projects-container"></div>
|
||
</div>
|
||
|
||
<div id="page-album" class="page">
|
||
<header class="detail-header">
|
||
<img
|
||
id="album-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
alt=""
|
||
class="detail-header-image"
|
||
/>
|
||
<div class="detail-header-info">
|
||
<h1 class="title" id="album-detail-title"></h1>
|
||
<div class="meta" id="album-detail-meta"></div>
|
||
<div class="meta" id="album-detail-producer"></div>
|
||
<div class="detail-header-actions">
|
||
<button id="play-album-btn" class="btn-primary">
|
||
<span>Play Album</span>
|
||
</button>
|
||
<button id="shuffle-album-btn" class="btn-primary">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="18"
|
||
height="18"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m18 14 4 4-4 4" />
|
||
<path d="m18 2 4 4-4 4" />
|
||
<path d="M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" />
|
||
<path d="M2 6h1.972a4 4 0 0 1 3.6 2.2" />
|
||
<path d="M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" />
|
||
</svg>
|
||
<span>Shuffle</span>
|
||
</button>
|
||
<button id="album-mix-btn" class="btn-primary" style="display: none">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
>
|
||
<path
|
||
d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"
|
||
/>
|
||
</svg>
|
||
<span>Mix</span>
|
||
</button>
|
||
<button id="download-album-btn" class="btn-primary">
|
||
<span>Download</span>
|
||
</button>
|
||
<button id="add-album-to-playlist-btn" class="btn-secondary" title="Add to Playlist">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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>
|
||
</button>
|
||
<button
|
||
id="like-album-btn"
|
||
class="btn-secondary like-btn"
|
||
data-action="toggle-like"
|
||
data-type="album"
|
||
title="Save to Favorites"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="heart-icon"
|
||
>
|
||
<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"
|
||
></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div class="album-content-layout">
|
||
<div class="track-list" id="album-detail-tracklist"></div>
|
||
|
||
<section
|
||
class="content-section"
|
||
id="album-section-more-albums"
|
||
style="display: none; margin-top: 3rem"
|
||
>
|
||
<h2 class="section-title" id="album-title-more-albums">from Artist</h2>
|
||
<div class="card-grid" id="album-detail-more-albums"></div>
|
||
</section>
|
||
|
||
<section class="content-section" id="album-section-eps" style="display: none; margin-top: 3rem">
|
||
<h2 class="section-title" id="album-title-eps">EPs and Singles</h2>
|
||
<div class="card-grid" id="album-detail-eps"></div>
|
||
</section>
|
||
|
||
<section
|
||
class="content-section"
|
||
id="album-section-similar-artists"
|
||
style="display: none; margin-top: 3rem"
|
||
>
|
||
<h2 class="section-title">Similar Artists</h2>
|
||
<div class="card-grid" id="album-detail-similar-artists"></div>
|
||
</section>
|
||
|
||
<section
|
||
class="content-section"
|
||
id="album-section-similar-albums"
|
||
style="display: none; margin-top: 3rem"
|
||
>
|
||
<h2 class="section-title">Similar Albums</h2>
|
||
<div class="card-grid" id="album-detail-similar-albums"></div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
|
||
<section id="page-playlist" class="page">
|
||
<header class="detail-header">
|
||
<div class="detail-header-cover-container">
|
||
<img
|
||
id="playlist-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
class="detail-header-image"
|
||
alt="Playlist Cover"
|
||
/>
|
||
<div id="playlist-detail-collage" class="detail-header-collage" style="display: none"></div>
|
||
</div>
|
||
<div class="detail-header-info">
|
||
<h1 class="title" id="playlist-detail-title"></h1>
|
||
<div class="meta" id="playlist-detail-meta"></div>
|
||
<div class="meta" id="playlist-detail-description" class="detail-description"></div>
|
||
<div class="detail-header-actions">
|
||
<button id="play-playlist-btn" class="btn-primary">
|
||
<span>Play</span>
|
||
</button>
|
||
<button id="download-playlist-btn" class="btn-primary">
|
||
<span>Download</span>
|
||
</button>
|
||
<button
|
||
id="like-playlist-btn"
|
||
class="btn-secondary like-btn"
|
||
data-action="toggle-like"
|
||
data-type="playlist"
|
||
title="Save to Favorites"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="heart-icon"
|
||
>
|
||
<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"
|
||
></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<form class="track-list-search-container" onsubmit="return false;">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="search-icon"
|
||
>
|
||
<circle cx="11" cy="11" r="8"></circle>
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||
</svg>
|
||
<input
|
||
type="search"
|
||
id="track-list-search-input"
|
||
placeholder="Search tracks..."
|
||
class="track-list-search-input"
|
||
autocomplete="off"
|
||
autocorrect="off"
|
||
autocapitalize="off"
|
||
spellcheck="false"
|
||
/>
|
||
<button
|
||
type="button"
|
||
class="search-clear-btn btn-icon"
|
||
title="Clear search"
|
||
style="display: none"
|
||
>
|
||
×
|
||
</button>
|
||
</form>
|
||
<div id="playlist-detail-tracklist" class="track-list"></div>
|
||
|
||
<section
|
||
class="content-section"
|
||
id="playlist-section-recommended"
|
||
style="display: none; margin-top: 3rem"
|
||
>
|
||
<h2 class="section-title">Recommended Songs</h2>
|
||
<p style="color: grey; margin-bottom: 15px">Suggested Songs From Your Playlist</p>
|
||
<div class="track-list" id="playlist-detail-recommended"></div>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="page-folder" class="page">
|
||
<header class="detail-header">
|
||
<img
|
||
id="folder-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
class="detail-header-image"
|
||
alt="Folder Cover"
|
||
/>
|
||
<div class="detail-header-info">
|
||
<h1 class="title" id="folder-detail-title"></h1>
|
||
<div class="meta" id="folder-detail-meta"></div>
|
||
<div class="detail-header-actions">
|
||
<button id="delete-folder-btn" class="btn-secondary danger">Delete Folder</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div class="card-grid" id="folder-detail-container"></div>
|
||
</section>
|
||
|
||
<section id="page-mix" class="page">
|
||
<header class="detail-header">
|
||
<img
|
||
id="mix-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
class="detail-header-image"
|
||
alt="Mix Cover"
|
||
/>
|
||
<div class="detail-header-info">
|
||
<h1 class="title" id="mix-detail-title"></h1>
|
||
<div class="meta" id="mix-detail-meta"></div>
|
||
<div class="meta" id="mix-detail-description" class="detail-description"></div>
|
||
<div class="detail-header-actions">
|
||
<button id="play-mix-btn" class="btn-primary">
|
||
<span>Play</span>
|
||
</button>
|
||
<button id="download-mix-btn" class="btn-primary">
|
||
<span>Download</span>
|
||
</button>
|
||
<button
|
||
id="like-mix-btn"
|
||
class="btn-secondary like-btn"
|
||
data-action="toggle-like"
|
||
data-type="mix"
|
||
title="Save to Favorites"
|
||
style="display: none"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="heart-icon"
|
||
>
|
||
<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"
|
||
></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div id="mix-detail-tracklist" class="track-list"></div>
|
||
</section>
|
||
|
||
<div id="page-artist" class="page">
|
||
<header class="detail-header">
|
||
<img
|
||
id="artist-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
alt="Artist"
|
||
class="detail-header-image artist"
|
||
/>
|
||
<div class="detail-header-info">
|
||
<h1 class="title" id="artist-detail-name"></h1>
|
||
<div class="meta" id="artist-detail-meta"></div>
|
||
<div class="detail-header-actions">
|
||
<button id="play-artist-radio-btn" class="btn-primary">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
>
|
||
<path
|
||
d="M4.5 9v6h3l5 5V4l-5 5h-3zm16 3a6 6 0 0 0-3.26-5.3l-1.48 1.48C17.31 9.21 18 10.53 18 12c0 1.47-.69 2.79-1.74 3.82l1.48 1.48A6 6 0 0 0 20.5 12z"
|
||
/>
|
||
</svg>
|
||
<span>Artist Radio</span>
|
||
</button>
|
||
<button id="shuffle-artist-btn" class="btn-primary">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="18"
|
||
height="18"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="m18 14 4 4-4 4" />
|
||
<path d="m18 2 4 4-4 4" />
|
||
<path d="M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" />
|
||
<path d="M2 6h1.972a4 4 0 0 1 3.6 2.2" />
|
||
<path d="M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" />
|
||
</svg>
|
||
<span>Shuffle</span>
|
||
</button>
|
||
<button id="artist-mix-btn" class="btn-primary" style="display: none">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
>
|
||
<path
|
||
d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"
|
||
/>
|
||
</svg>
|
||
<span>Mix</span>
|
||
</button>
|
||
<button id="download-discography-btn" class="btn-primary">
|
||
<span>Download Discography</span>
|
||
</button>
|
||
<button
|
||
id="like-artist-btn"
|
||
class="btn-secondary like-btn"
|
||
data-action="toggle-like"
|
||
data-type="artist"
|
||
title="Save to Favorites"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="heart-icon"
|
||
>
|
||
<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"
|
||
></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<section class="content-section">
|
||
<h2 class="section-title">Popular Tracks</h2>
|
||
<div class="track-list" id="artist-detail-tracks"></div>
|
||
</section>
|
||
<section class="content-section">
|
||
<h2 class="section-title">Albums</h2>
|
||
<div class="card-grid" id="artist-detail-albums"></div>
|
||
</section>
|
||
<section class="content-section" id="artist-section-eps" style="display: none">
|
||
<h2 class="section-title">EPs and Singles</h2>
|
||
<div class="card-grid" id="artist-detail-eps"></div>
|
||
</section>
|
||
<section
|
||
class="content-section"
|
||
id="artist-section-load-unreleased"
|
||
style="display: none; margin: 1.5rem 0"
|
||
>
|
||
<button id="load-unreleased-btn" class="btn-primary">Load Unreleased Projects</button>
|
||
</section>
|
||
<section class="content-section" id="artist-section-unreleased" style="display: none">
|
||
<h2 class="section-title">Unreleased Music</h2>
|
||
<div class="card-grid" id="artist-detail-unreleased"></div>
|
||
</section>
|
||
<section class="content-section" id="artist-section-similar" style="display: none">
|
||
<h2 class="section-title">Similar Artists</h2>
|
||
<div class="card-grid" id="artist-detail-similar"></div>
|
||
</section>
|
||
</div>
|
||
|
||
<div id="page-track" class="page">
|
||
<header class="detail-header">
|
||
<img
|
||
id="track-detail-image"
|
||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||
class="detail-header-image"
|
||
alt="Track Cover"
|
||
/>
|
||
<div class="detail-header-info">
|
||
<div class="type">Song</div>
|
||
<h1 class="title" id="track-detail-title"></h1>
|
||
<div class="meta">
|
||
<span id="track-detail-artist"></span> • <span id="track-detail-album"></span> •
|
||
<span id="track-detail-year"></span>
|
||
</div>
|
||
<div class="detail-header-actions">
|
||
<button id="play-track-btn" class="btn-primary">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
>
|
||
<path d="M8 5v14l11-7z" />
|
||
</svg>
|
||
<span>Play</span>
|
||
</button>
|
||
<button id="track-lyrics-btn" class="btn-secondary">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" />
|
||
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
||
<line x1="12" y1="19" x2="12" y2="22" />
|
||
<line x1="8" y1="22" x2="16" y2="22" />
|
||
</svg>
|
||
<span>Lyrics</span>
|
||
</button>
|
||
<button id="share-track-btn" class="btn-secondary">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
|
||
<polyline points="16 6 12 2 8 6" />
|
||
<line x1="12" y1="2" x2="12" y2="15" />
|
||
</svg>
|
||
<span>Share</span>
|
||
</button>
|
||
<button
|
||
id="like-track-btn"
|
||
class="btn-secondary like-btn"
|
||
data-action="toggle-like"
|
||
data-type="track"
|
||
title="Save to Favorites"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="heart-icon"
|
||
>
|
||
<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"
|
||
></path>
|
||
</svg>
|
||
</button>
|
||
<button id="download-track-btn" class="btn-secondary" title="Download">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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>
|
||
</div>
|
||
</header>
|
||
<section class="content-section" id="track-album-section">
|
||
<h2 class="section-title">More from Album</h2>
|
||
<div class="track-list" id="track-detail-album-tracks"></div>
|
||
</section>
|
||
<section class="content-section" id="track-similar-section" style="display: none">
|
||
<h2 class="section-title">Similar Tracks</h2>
|
||
<div class="track-list" id="track-detail-similar-tracks"></div>
|
||
</section>
|
||
</div>
|
||
|
||
<div id="page-settings" class="page">
|
||
<h2 class="section-title">Settings</h2>
|
||
<form
|
||
class="track-list-search-container settings-search-container"
|
||
onsubmit="return false;"
|
||
style="margin: 1rem 0"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="search-icon"
|
||
>
|
||
<circle cx="11" cy="11" r="8"></circle>
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||
</svg>
|
||
<input
|
||
type="search"
|
||
id="settings-search-input"
|
||
placeholder="Search settings..."
|
||
class="track-list-search-input"
|
||
autocomplete="off"
|
||
autocorrect="off"
|
||
autocapitalize="off"
|
||
spellcheck="false"
|
||
/>
|
||
<button
|
||
type="button"
|
||
class="search-clear-btn btn-icon"
|
||
title="Clear search"
|
||
style="display: none"
|
||
>
|
||
×
|
||
</button>
|
||
</form>
|
||
<div class="settings-tabs">
|
||
<button class="settings-tab active" data-tab="appearance">Appearance</button>
|
||
<button class="settings-tab" data-tab="interface">Interface</button>
|
||
<button class="settings-tab" data-tab="scrobbling">Scrobbling</button>
|
||
<button class="settings-tab" data-tab="audio">Audio</button>
|
||
<button class="settings-tab" data-tab="downloads">Downloads</button>
|
||
<button class="settings-tab" data-tab="system">System</button>
|
||
</div>
|
||
<div class="settings-tab-content active" id="settings-tab-appearance">
|
||
<div class="settings-list">
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Theme</span>
|
||
<span class="description">Choose your preferred color scheme</span>
|
||
</div>
|
||
</div>
|
||
<div class="theme-picker" id="theme-picker">
|
||
<div class="theme-option" data-theme="system">System</div>
|
||
<div class="theme-option" data-theme="monochrome">Black</div>
|
||
<div class="theme-option" data-theme="white">White</div>
|
||
<div class="theme-option" data-theme="dark">Dark</div>
|
||
<div class="theme-option" data-theme="ocean">Ocean</div>
|
||
<div class="theme-option" data-theme="purple">Purple</div>
|
||
<div class="theme-option" data-theme="forest">Forest</div>
|
||
<div class="theme-option" data-theme="mocha">Mocha</div>
|
||
<div class="theme-option" data-theme="machiatto">Machiatto</div>
|
||
<div class="theme-option" data-theme="frappe">Frappé</div>
|
||
<div class="theme-option" data-theme="latte">Latte</div>
|
||
<div class="theme-option" data-theme="custom">Custom</div>
|
||
</div>
|
||
<div class="custom-theme-editor" id="custom-theme-editor">
|
||
<h4>Custom Theme</h4>
|
||
<div class="theme-color-grid" id="theme-color-grid"></div>
|
||
<div class="theme-actions">
|
||
<button class="btn-secondary" id="apply-custom-theme">Apply Theme</button>
|
||
<button class="btn-secondary" id="reset-custom-theme">Reset</button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item font-settings-container">
|
||
<div class="info">
|
||
<span class="label">Font</span>
|
||
<span class="description"
|
||
>Choose from presets, Google Fonts, URLs, or upload your own</span
|
||
>
|
||
</div>
|
||
<div class="font-input-group">
|
||
<select id="font-type-select" class="font-type-select">
|
||
<option value="preset">Preset</option>
|
||
<option value="google">Google Fonts</option>
|
||
<option value="url">URL</option>
|
||
<option value="upload">Upload</option>
|
||
</select>
|
||
|
||
<div id="font-preset-section" class="font-section">
|
||
<select id="font-preset-select">
|
||
<option value="Inter">Inter (Default)</option>
|
||
<option value="IBM Plex Mono">IBM Plex Mono</option>
|
||
<option value="Roboto">Roboto</option>
|
||
<option value="Open Sans">Open Sans</option>
|
||
<option value="Lato">Lato</option>
|
||
<option value="Montserrat">Montserrat</option>
|
||
<option value="Poppins">Poppins</option>
|
||
<option value="System UI">System UI</option>
|
||
<option value="monospace">Monospace</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div id="font-google-section" class="font-section" style="display: none">
|
||
<input
|
||
type="text"
|
||
id="font-google-input"
|
||
placeholder="Enter Google Fonts URL or font name (e.g., IBM Plex Mono)"
|
||
class="font-input"
|
||
/>
|
||
<button id="font-google-apply" class="btn-secondary">Apply</button>
|
||
</div>
|
||
|
||
<div id="font-url-section" class="font-section" style="display: none">
|
||
<input
|
||
type="text"
|
||
id="font-url-input"
|
||
placeholder="Enter font file URL (.woff, .woff2, .ttf, .otf)"
|
||
class="font-input"
|
||
/>
|
||
<input
|
||
type="text"
|
||
id="font-url-name"
|
||
placeholder="Font name (optional)"
|
||
class="font-input font-name-input"
|
||
/>
|
||
<button id="font-url-apply" class="btn-secondary">Apply</button>
|
||
</div>
|
||
|
||
<div id="font-upload-section" class="font-section" style="display: none">
|
||
<input
|
||
type="file"
|
||
id="font-upload-input"
|
||
accept=".woff,.woff2,.ttf,.otf"
|
||
class="font-file-input"
|
||
/>
|
||
<div id="uploaded-fonts-list" class="uploaded-fonts-list"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Waveform Seekbar</span>
|
||
<span class="description"
|
||
>Show a visual waveform of the track in the progress bar
|
||
(Experimental)</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="waveform-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Smooth Scrolling</span>
|
||
<span class="description"
|
||
>Provides a smoother scrolling experience with Lenis (Experimental)</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="smooth-scrolling-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Album Cover Background</span>
|
||
<span class="description"
|
||
>Use the album cover as a blurred background on album pages and as primary
|
||
color</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="album-background-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Dynamic Colors</span>
|
||
<span class="description"
|
||
>Automatically change the app accent color based on the currently playing
|
||
track's album art</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="dynamic-color-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Full-screen Visualizer</span>
|
||
<span class="description">Enable particle visualizer in full-screen mode</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="visualizer-enabled-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item" id="visualizer-preset-setting">
|
||
<div class="info">
|
||
<span class="label">Visualizer Style</span>
|
||
<span class="description">Select the visualization style</span>
|
||
</div>
|
||
<select id="visualizer-preset-select">
|
||
<option value="lcd">LCD Pixels</option>
|
||
<option value="particles">Particles</option>
|
||
<option value="unknown-pleasures">Unknown Pleasures</option>
|
||
<option value="butterchurn">Butterchurn (Milkdrop)</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-item" id="visualizer-mode-setting">
|
||
<div class="info">
|
||
<span class="label">Visualizer Mode</span>
|
||
<span class="description"
|
||
>Choose how the visualizer is displayed in full-screen</span
|
||
>
|
||
</div>
|
||
<select id="visualizer-mode-select">
|
||
<option value="solid">Solid Background</option>
|
||
<option value="blended">Blended on Cover Art</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-item" id="visualizer-smart-intensity-setting">
|
||
<div class="info">
|
||
<span class="label">Smart Intensity Switching</span>
|
||
<span class="description"
|
||
>Automatically adjust visualizer intensity based on song energy</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="smart-intensity-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item" id="visualizer-sensitivity-setting">
|
||
<div class="info">
|
||
<span class="label">Visualizer Sensitivity</span>
|
||
<span class="description"
|
||
>Adjust the intensity of the visualizer effects.
|
||
<strong
|
||
>Warning: High sensitivity may cause flashing lights and rapid motion,
|
||
which can trigger seizures in people with photosensitive
|
||
epilepsy.</strong
|
||
></span
|
||
>
|
||
</div>
|
||
<div style="display: flex; align-items: center; gap: 10px">
|
||
<input
|
||
type="range"
|
||
id="visualizer-sensitivity-slider"
|
||
min="0.1"
|
||
max="2.0"
|
||
step="0.1"
|
||
value="0.6"
|
||
style="width: 100px"
|
||
/>
|
||
<span
|
||
id="visualizer-sensitivity-value"
|
||
style="font-size: 0.9rem; min-width: 3em; text-align: right"
|
||
>60%</span
|
||
>
|
||
</div>
|
||
</div>
|
||
<!-- Butterchurn Settings -->
|
||
<div class="setting-item" id="butterchurn-cycle-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Cycle Presets</span>
|
||
<span class="description">Automatically change visualizer presets</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="butterchurn-cycle-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div
|
||
class="setting-item"
|
||
id="butterchurn-specific-preset-setting"
|
||
style="display: none"
|
||
>
|
||
<div class="info">
|
||
<span class="label">Current Preset</span>
|
||
<span class="description">Select a specific Butterchurn preset</span>
|
||
</div>
|
||
<select id="butterchurn-specific-preset-select" style="width: 200px">
|
||
<option value="">Loading...</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-item" id="butterchurn-duration-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Cycle Duration</span>
|
||
<span class="description">Seconds between preset changes</span>
|
||
</div>
|
||
<input
|
||
type="number"
|
||
id="butterchurn-duration-input"
|
||
min="5"
|
||
max="300"
|
||
value="30"
|
||
class="template-input"
|
||
style="width: 80px"
|
||
/>
|
||
</div>
|
||
<div class="setting-item" id="butterchurn-randomize-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Randomize Presets</span>
|
||
<span class="description"
|
||
>Select next preset randomly instead of sequentially</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="butterchurn-randomize-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-tab-content" id="settings-tab-interface">
|
||
<div class="settings-list">
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Recommended Songs</span>
|
||
<span class="description">Display recommended songs on the home page</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="show-recommended-songs-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Recommended Albums</span>
|
||
<span class="description">Display recommended albums on the home page</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="show-recommended-albums-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Recommended Artists</span>
|
||
<span class="description">Display recommended artists on the home page</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="show-recommended-artists-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Jump Back In</span>
|
||
<span class="description"
|
||
>Display recent albums, playlists, and mixes on the home page</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="show-jump-back-in-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Editor's Picks</span>
|
||
<span class="description"
|
||
>Display curated album selections on the home page</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="show-editors-picks-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Shuffle Editor's Picks</span>
|
||
<span class="description"
|
||
>Randomize the order of editor's picks on each load</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="shuffle-editors-picks-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Home in Sidebar</span>
|
||
<span class="description">Display the Home link in the sidebar navigation</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-home-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Library in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Library link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-library-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Recent in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Recent link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-recent-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Unreleased in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Unreleased link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-unreleased-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Donate in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Donate link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-donate-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Settings in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Settings link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-settings-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Account in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Account link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-account-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show About in Sidebar</span>
|
||
<span class="description"
|
||
>Display the About link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-about-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Download in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Download link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-download-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Discord in Sidebar</span>
|
||
<span class="description"
|
||
>Display the Discord link in the sidebar navigation</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="sidebar-show-discord-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-tab-content" id="settings-tab-scrobbling">
|
||
<div class="settings-list">
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Scrobble Threshold</span>
|
||
<span class="description"
|
||
>Percentage of track to play before scrobbling (1-100%)</span
|
||
>
|
||
</div>
|
||
<div style="display: flex; align-items: center; gap: 10px">
|
||
<input
|
||
type="range"
|
||
id="scrobble-percentage-slider"
|
||
min="1"
|
||
max="100"
|
||
step="1"
|
||
value="75"
|
||
style="width: 100px"
|
||
/>
|
||
<input
|
||
type="number"
|
||
id="scrobble-percentage-input"
|
||
min="1"
|
||
max="100"
|
||
value="75"
|
||
style="
|
||
width: 50px;
|
||
font-size: 0.9rem;
|
||
text-align: center;
|
||
padding: 4px;
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 4px;
|
||
background: var(--input-bg);
|
||
color: var(--text-color);
|
||
"
|
||
/>
|
||
<span style="font-size: 0.9rem">%</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Last.fm Scrobbling</span>
|
||
<span class="description" id="lastfm-status"
|
||
>Connect your Last.fm account to scrobble tracks</span
|
||
>
|
||
</div>
|
||
<div id="lastfm-controls">
|
||
<button id="lastfm-connect-btn" class="btn-secondary">Connect Last.fm</button>
|
||
</div>
|
||
<div id="lastfm-credential-auth" style="display: none; margin-top: 12px">
|
||
<div id="lastfm-credential-form" style="display: none">
|
||
<p style="margin: 0 0 8px 0; font-size: 0.85rem; color: var(--muted)">
|
||
Enter your Last.fm credentials:
|
||
</p>
|
||
<input
|
||
type="text"
|
||
id="lastfm-username"
|
||
placeholder="Username"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
"
|
||
/>
|
||
<input
|
||
type="password"
|
||
id="lastfm-password"
|
||
placeholder="Password"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
"
|
||
/>
|
||
<div style="display: flex; gap: 8px">
|
||
<button
|
||
id="lastfm-login-credentials"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
||
>
|
||
Login
|
||
</button>
|
||
<button
|
||
id="lastfm-use-oauth"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
||
>
|
||
Use OAuth Instead
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="lastfm-credential-auth-secondary" style="display: none; margin-top: 12px">
|
||
<div style="display: flex; flex-direction: column; gap: 8px">
|
||
<div id="lastfm-credential-toggle-container-secondary">
|
||
<button
|
||
id="lastfm-show-credential-auth-secondary"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem; width: 100%"
|
||
>
|
||
Login with Username/Password
|
||
</button>
|
||
</div>
|
||
<div id="lastfm-credential-form-secondary" style="display: none">
|
||
<input
|
||
type="text"
|
||
id="lastfm-username-secondary"
|
||
placeholder="Last.fm Username"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
"
|
||
/>
|
||
<input
|
||
type="password"
|
||
id="lastfm-password-secondary"
|
||
placeholder="Last.fm Password"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
"
|
||
/>
|
||
<div style="display: flex; gap: 8px">
|
||
<button
|
||
id="lastfm-login-credentials-secondary"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
||
>
|
||
Login
|
||
</button>
|
||
<button
|
||
id="lastfm-use-oauth-secondary"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
||
>
|
||
Use OAuth
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="lastfm-credential-auth-alt" style="margin-top: 12px; display: none">
|
||
<div style="display: flex; flex-direction: column; gap: 8px">
|
||
<input
|
||
type="text"
|
||
id="lastfm-username-alt"
|
||
placeholder="Last.fm Username"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
"
|
||
/>
|
||
<input
|
||
type="password"
|
||
id="lastfm-password-alt"
|
||
placeholder="Last.fm Password"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
"
|
||
/>
|
||
<div style="display: flex; gap: 8px; margin-top: 4px">
|
||
<button
|
||
id="lastfm-login-credentials-alt"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem"
|
||
>
|
||
Login with Credentials
|
||
</button>
|
||
<button
|
||
id="lastfm-use-oauth-alt"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem"
|
||
>
|
||
Use OAuth Instead
|
||
</button>
|
||
</div>
|
||
<div style="display: flex; gap: 8px; margin-top: 4px">
|
||
<button
|
||
id="lastfm-show-credential-auth-alt"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem"
|
||
>
|
||
Login with Username/Password
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="setting-item" id="lastfm-toggle-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Enable Scrobbling</span>
|
||
<span class="description">Automatically scrobble played tracks</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="lastfm-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="setting-item" id="lastfm-love-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Love on Like</span>
|
||
<span class="description"
|
||
>Automatically 'love' tracks on Last.fm when you like them</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="lastfm-love-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="setting-item" id="lastfm-custom-creds-toggle-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Use Custom API Credentials</span>
|
||
<span class="description">Use your own Last.fm API key and secret</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="lastfm-custom-creds-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="setting-item" id="lastfm-custom-creds-setting" style="display: none">
|
||
<div class="info" style="flex: 1; min-width: 0">
|
||
<span class="label">Custom API Credentials</span>
|
||
<div style="display: flex; flex-direction: column; gap: 8px; margin-top: 8px">
|
||
<input
|
||
type="text"
|
||
id="lastfm-custom-api-key"
|
||
placeholder="API Key"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
"
|
||
/>
|
||
<input
|
||
type="password"
|
||
id="lastfm-custom-api-secret"
|
||
placeholder="API Secret"
|
||
style="
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
"
|
||
/>
|
||
<div style="display: flex; gap: 8px; margin-top: 4px">
|
||
<button
|
||
id="lastfm-save-custom-creds"
|
||
class="btn-secondary"
|
||
style="padding: 6px 12px; font-size: 0.85rem"
|
||
>
|
||
Save Credentials
|
||
</button>
|
||
<button
|
||
id="lastfm-clear-custom-creds"
|
||
class="btn-secondary danger"
|
||
style="padding: 6px 12px; font-size: 0.85rem; display: none"
|
||
>
|
||
Clear
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Libre.fm Scrobbling</span>
|
||
<span class="description" id="librefm-status"
|
||
>Connect your Libre.fm account to scrobble tracks</span
|
||
>
|
||
</div>
|
||
<div id="librefm-controls">
|
||
<button id="librefm-connect-btn" class="btn-secondary">Connect Libre.fm</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="setting-item" id="librefm-toggle-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Enable Scrobbling</span>
|
||
<span class="description">Automatically scrobble played tracks</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="librefm-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="setting-item" id="librefm-love-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Love on Like</span>
|
||
<span class="description"
|
||
>Automatically 'love' tracks on Libre.fm when you like them</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="librefm-love-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">ListenBrainz Scrobbling</span>
|
||
<span class="description"
|
||
>Submit listens to ListenBrainz (requires User Token)</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="listenbrainz-enabled-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item" id="listenbrainz-token-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">User Token</span>
|
||
<span class="description">Found on your ListenBrainz profile page</span>
|
||
</div>
|
||
<input
|
||
type="password"
|
||
id="listenbrainz-token-input"
|
||
placeholder="Enter Token"
|
||
class="template-input"
|
||
style="width: 250px"
|
||
/>
|
||
</div>
|
||
<div class="setting-item" id="listenbrainz-custom-url-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Custom API URL (Optional)</span>
|
||
<span class="description">Leave empty to use official ListenBrainz server</span>
|
||
</div>
|
||
<input
|
||
type="url"
|
||
id="listenbrainz-custom-url-input"
|
||
placeholder="https://api.listenbrainz.org/1"
|
||
class="template-input"
|
||
style="width: 250px"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Maloja Scrobbling</span>
|
||
<span class="description">Submit listens to a self-hosted Maloja server</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="maloja-enabled-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item" id="maloja-token-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">API Key</span>
|
||
<span class="description">Found in your Maloja settings</span>
|
||
</div>
|
||
<input
|
||
type="password"
|
||
id="maloja-token-input"
|
||
placeholder="Enter API Key"
|
||
class="template-input"
|
||
style="width: 250px"
|
||
/>
|
||
</div>
|
||
<div class="setting-item" id="maloja-custom-url-setting" style="display: none">
|
||
<div class="info">
|
||
<span class="label">Maloja Server URL</span>
|
||
<span class="description">Your Maloja instance URL</span>
|
||
</div>
|
||
<input
|
||
type="url"
|
||
id="maloja-custom-url-input"
|
||
placeholder="https://maloja.example.com"
|
||
class="template-input"
|
||
style="width: 250px"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-tab-content" id="settings-tab-audio">
|
||
<div class="settings-list">
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Streaming Quality</span>
|
||
<span class="description">Quality for streaming playback</span>
|
||
</div>
|
||
<select id="streaming-quality-setting">
|
||
<option value="HI_RES_LOSSLESS">Hi-Res FLAC (24-bit)</option>
|
||
<option value="LOSSLESS">FLAC (Lossless)</option>
|
||
<option value="HIGH">AAC 320kbps</option>
|
||
<option value="LOW">AAC 96kbps</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Download Quality</span>
|
||
<span class="description">Quality for track downloads</span>
|
||
</div>
|
||
<select id="download-quality-setting">
|
||
<option value="HI_RES_LOSSLESS">Hi-Res FLAC (24-bit)</option>
|
||
<option value="LOSSLESS">FLAC (Lossless)</option>
|
||
<option value="HIGH">AAC 320kbps</option>
|
||
<option value="LOW">AAC 96kbps</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Cover Art Size</span>
|
||
<span class="description">Size for downloaded/embedded cover art</span>
|
||
</div>
|
||
<input
|
||
type="text"
|
||
id="cover-art-size-setting"
|
||
class="template-input"
|
||
style="width: 120px; text-align: right"
|
||
placeholder="1280x1280"
|
||
/>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Show Quality Badges</span>
|
||
<span class="description">Display "HD" badge for Hi-Res tracks</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="show-quality-badges-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Album release year</span>
|
||
<span class="description"
|
||
>Show original album year instead of track/remaster date</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="use-album-release-year-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Gapless Playback</span>
|
||
<span class="description">Play audio without interruption between tracks</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">ReplayGain Mode</span>
|
||
<span class="description">Normalize volume across tracks</span>
|
||
</div>
|
||
<select id="replay-gain-mode">
|
||
<option value="off">Off</option>
|
||
<option value="track">Track</option>
|
||
<option value="album">Album</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">ReplayGain Pre-Amp</span>
|
||
<span class="description">Adjust gain manually (dB)</span>
|
||
</div>
|
||
<input
|
||
type="number"
|
||
id="replay-gain-preamp"
|
||
value="3"
|
||
step="0.5"
|
||
style="width: 80px"
|
||
/>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Mono Audio</span>
|
||
<span class="description">Combine left and right channels into mono</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="mono-audio-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Exponential Volume</span>
|
||
<span class="description"
|
||
>Use logarithmic volume curve for finer low-volume control</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="exponential-volume-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Playback Speed Control -->
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Playback Speed</span>
|
||
<span class="description">Adjust playback speed (0.5x - 2.0x)</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; gap: 12px">
|
||
<input
|
||
type="range"
|
||
id="playback-speed-slider"
|
||
min="0.5"
|
||
max="2.0"
|
||
step="0.1"
|
||
value="1.0"
|
||
style="width: 150px"
|
||
/>
|
||
<span
|
||
id="playback-speed-value"
|
||
style="min-width: 50px; text-align: right; font-family: var(--font-family)"
|
||
>1.0x</span
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Pitch Shift Control -->
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Pitch Shift</span>
|
||
<span class="description"
|
||
>Shift pitch up or down (-12 to +12 semitones). Note: Disable "Preserve
|
||
Pitch" to hear the effect</span
|
||
>
|
||
</div>
|
||
<div style="display: flex; align-items: center; gap: 12px">
|
||
<input
|
||
type="range"
|
||
id="pitch-shift-slider"
|
||
min="-12"
|
||
max="12"
|
||
step="1"
|
||
value="0"
|
||
style="width: 150px"
|
||
/>
|
||
<span
|
||
id="pitch-shift-value"
|
||
style="min-width: 50px; text-align: right; font-family: var(--font-family)"
|
||
>0</span
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Preserve Pitch</span>
|
||
<span class="description"
|
||
>Keep original pitch when changing playback speed</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="preserve-pitch-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 16-Band Equalizer -->
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Equalizer</span>
|
||
<span class="description"
|
||
>16-band parametric equalizer for fine audio control</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="equalizer-enabled-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="equalizer-container" id="equalizer-container" style="display: none">
|
||
<div class="equalizer-header">
|
||
<div class="equalizer-preset-row">
|
||
<label for="equalizer-preset-select">Preset</label>
|
||
<select id="equalizer-preset-select">
|
||
<option value="flat">Flat</option>
|
||
<option value="bass_boost">Bass Boost</option>
|
||
<option value="bass_reducer">Bass Reducer</option>
|
||
<option value="treble_boost">Treble Boost</option>
|
||
<option value="treble_reducer">Treble Reducer</option>
|
||
<option value="vocal_boost">Vocal Boost</option>
|
||
<option value="loudness">Loudness</option>
|
||
<option value="rock">Rock</option>
|
||
<option value="pop">Pop</option>
|
||
<option value="classical">Classical</option>
|
||
<option value="jazz">Jazz</option>
|
||
<option value="electronic">Electronic</option>
|
||
<option value="hip_hop">Hip-Hop</option>
|
||
<option value="r_and_b">R&B</option>
|
||
<option value="acoustic">Acoustic</option>
|
||
<option value="podcast">Podcast / Speech</option>
|
||
</select>
|
||
<button
|
||
id="equalizer-reset-btn"
|
||
class="btn-secondary"
|
||
title="Reset to Flat"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
||
<path d="M3 3v5h5" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="equalizer-bands" id="equalizer-bands">
|
||
<!-- Bands will be dynamically generated by JavaScript -->
|
||
<div class="eq-band" data-band="0">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">25</span>
|
||
</div>
|
||
<div class="eq-band" data-band="1">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">40</span>
|
||
</div>
|
||
<div class="eq-band" data-band="2">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">63</span>
|
||
</div>
|
||
<div class="eq-band" data-band="3">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">100</span>
|
||
</div>
|
||
<div class="eq-band" data-band="4">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">160</span>
|
||
</div>
|
||
<div class="eq-band" data-band="5">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">250</span>
|
||
</div>
|
||
<div class="eq-band" data-band="6">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">400</span>
|
||
</div>
|
||
<div class="eq-band" data-band="7">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">630</span>
|
||
</div>
|
||
<div class="eq-band" data-band="8">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">1K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="9">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">1.6K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="10">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">2.5K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="11">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">4K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="12">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">6.3K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="13">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">10K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="14">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">16K</span>
|
||
</div>
|
||
<div class="eq-band" data-band="15">
|
||
<input
|
||
type="range"
|
||
class="eq-slider"
|
||
min="-30"
|
||
max="30"
|
||
step="0.5"
|
||
value="0"
|
||
orient="vertical"
|
||
/>
|
||
<span class="eq-value">0</span>
|
||
<span class="eq-freq">20K</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="equalizer-scale">
|
||
<span>+30 dB</span>
|
||
<span>0 dB</span>
|
||
<span>-30 dB</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Now Playing View Mode</span>
|
||
<span class="description">Choose what shows when you click the album art</span>
|
||
</div>
|
||
<select id="now-playing-mode">
|
||
<option value="album">Go to Album</option>
|
||
<option value="cover">Fullscreen Mode</option>
|
||
<option value="lyrics">Lyrics Panel</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Compact Artists</span>
|
||
<span class="description"
|
||
>Show artist cards in a compact, horizontal layout</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="compact-artist-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Compact Albums</span>
|
||
<span class="description"
|
||
>Show album cards in a compact, horizontal layout</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="compact-album-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-tab-content" id="settings-tab-downloads">
|
||
<div class="settings-list">
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Zipped Bulk Downloads</span>
|
||
<span class="description"
|
||
>Download multiple tracks as a single ZIP file (requires browser
|
||
support)</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="zipped-bulk-downloads-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Download Lyrics</span>
|
||
<span class="description"
|
||
>Include .lrc files when downloading tracks/albums</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="download-lyrics-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Romaji Lyrics</span>
|
||
<span class="description"
|
||
>Convert Japanese lyrics to Romaji (Latin characters)</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="romaji-lyrics-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Filename Template</span>
|
||
<span class="description"
|
||
>Customize download filenames. Available: {trackNumber}, {artist}, {title},
|
||
{album}</span
|
||
>
|
||
</div>
|
||
<input
|
||
type="text"
|
||
id="filename-template"
|
||
class="template-input"
|
||
placeholder="{trackNumber} - {artist} - {title}"
|
||
/>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">ZIP Folder Template</span>
|
||
<span class="description"
|
||
>Customize album folder names. Available: {albumTitle}, {albumArtist},
|
||
{year}</span
|
||
>
|
||
</div>
|
||
<input
|
||
type="text"
|
||
id="zip-folder-template"
|
||
class="template-input"
|
||
placeholder="{albumTitle} - {albumArtist} - monochrome.tf"
|
||
/>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Generate M3U</span>
|
||
<span class="description">Include M3U playlist files in downloads</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="generate-m3u-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Generate M3U8</span>
|
||
<span class="description"
|
||
>Include extended M3U8 playlist files in downloads</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="generate-m3u8-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Generate CUE</span>
|
||
<span class="description"
|
||
>Include CUE sheets for gapless playback in downloads</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="generate-cue-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Generate NFO</span>
|
||
<span class="description"
|
||
>Include NFO files for media center compatibility</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="generate-nfo-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Generate JSON</span>
|
||
<span class="description">Include JSON files with rich metadata</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="generate-json-toggle" />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Relative Paths</span>
|
||
<span class="description">Use relative paths in playlist files</span>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="relative-paths-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-tab-content" id="settings-tab-system">
|
||
<div class="settings-list">
|
||
<div class="settings-group">
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Keyboard Shortcuts</span>
|
||
<span class="description">View available keyboard shortcuts</span>
|
||
</div>
|
||
<button id="show-shortcuts-btn" class="btn-secondary">Show Shortcuts</button>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Cache</span>
|
||
<span class="description" id="cache-info"
|
||
>Stores API responses to reduce requests</span
|
||
>
|
||
</div>
|
||
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Auto-Update App</span>
|
||
<span class="description"
|
||
>Automatically reload when a new version is available</span
|
||
>
|
||
</div>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="pwa-auto-update-toggle" checked />
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Reset Local Data</span>
|
||
<span class="description"
|
||
>Clear all local storage and cached data (does not affect cloud sync)</span
|
||
>
|
||
</div>
|
||
<button id="reset-local-data-btn" class="btn-secondary danger">Reset</button>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">Backup & Restore</span>
|
||
<span class="description"
|
||
>Export or import your library and history as JSON</span
|
||
>
|
||
</div>
|
||
<div style="display: flex; gap: 0.5rem">
|
||
<button id="export-library-btn" class="btn-secondary">Export</button>
|
||
<button id="import-library-btn" class="btn-secondary">Import</button>
|
||
<input
|
||
type="file"
|
||
id="import-library-input"
|
||
style="display: none"
|
||
accept=".json"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="info">
|
||
<span class="label">ADVANCED: Custom Database/Auth</span>
|
||
<span class="description"
|
||
>Configure custom PocketBase and Firebase instances</span
|
||
>
|
||
</div>
|
||
<button id="custom-db-btn" class="btn-secondary">Configure</button>
|
||
</div>
|
||
<div id="api-instance-manager">
|
||
<div class="setting-item" style="padding-bottom: 1rem; border: none">
|
||
<div class="info">
|
||
<span class="label">API Instances</span>
|
||
<span class="description">Manage and prioritize API instances.</span>
|
||
</div>
|
||
<button id="refresh-speed-test-btn" class="btn-secondary">
|
||
Refresh Instance List
|
||
</button>
|
||
</div>
|
||
<ul id="api-instance-list"></ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="page-about" class="page">
|
||
<h2 class="section-title">About Monochrome</h2>
|
||
<div class="about-content">
|
||
<p class="about-description">
|
||
Monochrome is a lightweight, privacy-focused music streaming client designed for
|
||
high-fidelity audio playback. Built with modern web technologies, it provides a clean,
|
||
distraction-free listening experience.
|
||
<br />
|
||
</p>
|
||
<br />
|
||
<div class="about-features">
|
||
<h4>Features</h4>
|
||
<ul>
|
||
<li>High-quality lossless audio streaming</li>
|
||
<li>Lyrics support with karaoke mode</li>
|
||
<li>Recently Played tracking for easy history access</li>
|
||
<li>Comprehensive Personal Library for favorites</li>
|
||
<li>Intelligent API caching for improved performance</li>
|
||
<li>Offline-capable Progressive Web App (PWA)</li>
|
||
<li>Media Session API integration for system controls</li>
|
||
<li>Queue management with shuffle and repeat modes</li>
|
||
<li>Track downloads with automatic metadata embedding</li>
|
||
<li>Multiple API instance support with failover</li>
|
||
<li>Dark, minimalist interface optimized for focus</li>
|
||
<li>Customizable themes</li>
|
||
<li>Keyboard shortcuts for power users</li>
|
||
</ul>
|
||
</div>
|
||
<div class="about-tech">
|
||
<h4>Technology Stack</h4>
|
||
<p>Vanilla JavaScript • ES6 Modules • IndexedDB • Service Workers • Media Session API</p>
|
||
</div>
|
||
<br /><br /><br />
|
||
<div class="about-donate">
|
||
<h2 class="section-title" style="text-align: center">Donate to Monochrome</h2>
|
||
<p style="text-align: center" class="donate-description">
|
||
If Monochrome has been useful to you and you're able to, consider making a donation.
|
||
<br />
|
||
It helps pay for the server and domain, and you get to support us :)
|
||
</p>
|
||
<div
|
||
class="donate-button"
|
||
id="donate-button-container"
|
||
style="
|
||
display: flex;
|
||
gap: 20px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 50px;
|
||
flex-wrap: wrap;
|
||
"
|
||
>
|
||
<a href="https://ko-fi.com/monochromemusic">
|
||
<button id="donate-btn" class="btn-secondary">Donate to Monochrome</button>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<br /><br /><br />
|
||
<div class="about-links">
|
||
<a
|
||
href="https://github.com/SamidyFR/monochrome"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="github-link"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-github-icon lucide-github"
|
||
>
|
||
<path
|
||
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
|
||
/>
|
||
<path d="M9 18c-4.51 2-5-2-7-2" />
|
||
</svg>
|
||
View on GitHub
|
||
</a>
|
||
<br />
|
||
<a
|
||
href="https://github.com/eduardprigoana/monochrome"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="github-link"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-github-icon lucide-github"
|
||
>
|
||
<path
|
||
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
|
||
/>
|
||
<path d="M9 18c-4.51 2-5-2-7-2" />
|
||
</svg>
|
||
View on GitHub (Original Repo)
|
||
</a>
|
||
</div>
|
||
<div class="about-footer">
|
||
<p class="version">Version 2.2.0</p>
|
||
<p class="disclaimer">
|
||
This is an independent client and is not affiliated with or endorsed by TIDAL or any
|
||
music streaming service.
|
||
</p>
|
||
<p class="disclaimer">
|
||
This application does not host, store, or distribute any media files. <br />
|
||
All content displayed or accessed through this app is provided by third‑party services
|
||
and APIs that are publicly available on the internet. <br />
|
||
We do not control, operate, or maintain any external content sources and are not
|
||
responsible for the accuracy, availability, or legality of any content provided by third
|
||
parties. <br />
|
||
Users are solely responsible for how they use this application and for ensuring that
|
||
their use complies with applicable laws and regulations in their jurisdiction.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="page-account" class="page">
|
||
<h2 class="section-title" style="text-align: center">Sign Up / Sign In</h2>
|
||
<div class="account-content">
|
||
<p style="text-align: center" class="account-description">
|
||
Make an account to allow syncing your library between devices.
|
||
</p>
|
||
<div
|
||
class="account-buttons"
|
||
id="auth-buttons-container"
|
||
style="
|
||
display: flex;
|
||
gap: 20px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 50px;
|
||
flex-wrap: wrap;
|
||
"
|
||
>
|
||
<button id="firebase-connect-btn" class="btn-secondary">Connect with Google</button>
|
||
<button id="toggle-email-auth-btn" class="btn-secondary">Connect with Email</button>
|
||
<button id="firebase-clear-cloud-btn" class="btn-secondary danger">Clear Cloud Data</button>
|
||
</div>
|
||
|
||
<div
|
||
id="email-auth-container"
|
||
style="
|
||
display: none;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
width: 100%;
|
||
max-width: 320px;
|
||
margin: 20px auto;
|
||
padding: 20px;
|
||
background: var(--card);
|
||
border-radius: var(--radius);
|
||
border: 1px solid var(--border);
|
||
"
|
||
>
|
||
<h3 style="text-align: center; margin-bottom: 10px">Email Authentication</h3>
|
||
<input type="email" id="auth-email" class="template-input" placeholder="Email Address" />
|
||
<input type="password" id="auth-password" class="template-input" placeholder="Password" />
|
||
<div style="display: flex; gap: 10px; justify-content: center; margin-top: 10px">
|
||
<button id="email-signin-btn" class="btn-primary" style="flex: 1">Sign In</button>
|
||
<button id="email-signup-btn" class="btn-secondary" style="flex: 1">Sign Up</button>
|
||
</div>
|
||
<button
|
||
id="reset-password-btn"
|
||
class="btn-secondary"
|
||
style="width: 100%; margin-top: 10px; font-size: 0.9rem"
|
||
>
|
||
Forgot Password?
|
||
</button>
|
||
<button
|
||
id="cancel-email-auth-btn"
|
||
class="btn-secondary"
|
||
style="width: 100%; margin-top: 10px"
|
||
>
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
<p id="firebase-status" style="text-align: center; padding-top: 15px; color: #8b8b93">
|
||
Sync your library across devices
|
||
</p>
|
||
<script>
|
||
if (window.authManager && window.authManager.user) {
|
||
const statusText = document.getElementById('firebase-status');
|
||
if (statusText)
|
||
statusText.textContent = `Signed in as ${window.authManager.user.email}`;
|
||
}
|
||
</script>
|
||
<p style="padding-top: 50px; text-align: center; color: #8b8b93">
|
||
We only store music data and a randomized ID to find out which Google/Email account is
|
||
which.
|
||
<br />
|
||
All data is anonymous. We do not store anything like emails, usernames, or anything
|
||
sensitive. <br />
|
||
</p>
|
||
<p style="padding-top: 50px; text-align: center; color: #8b8b93">
|
||
However, if you want complete control over your data, we allow you to use your own Database
|
||
Configuration.
|
||
</p>
|
||
<div
|
||
style="
|
||
display: flex;
|
||
gap: 50px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding-top: 25px;
|
||
"
|
||
>
|
||
<a id="advanced-config-link" class="btn-secondary" href="/settings"
|
||
>Advanced: Custom Configuration</a
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="page-download" class="page">
|
||
<h2 class="section-title" style="text-align: center">Monochrome Official App</h2>
|
||
<div class="download-content">
|
||
<p style="text-align: center" class="account-description">
|
||
Install the Monochrome Desktop App for a refined & improved music listening experience.
|
||
</p>
|
||
<br /><br />
|
||
|
||
<div class="download-buttons-icons">
|
||
<div class="download-buttons-icons-windows" style="display: flex; justify-content: center">
|
||
<svg
|
||
width="96px"
|
||
height="96px"
|
||
viewBox="0 0 20 20"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||
fill="#000000"
|
||
>
|
||
<g stroke-width="0"></g>
|
||
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
||
<g>
|
||
<title>windows [#FFF174]</title>
|
||
<desc>Created with Sketch.</desc>
|
||
<defs></defs>
|
||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||
<g
|
||
id="Dribbble-Light-Preview"
|
||
transform="translate(-60.000000, -7439.000000)"
|
||
fill="var(--foreground)"
|
||
>
|
||
<g id="icons" transform="translate(56.000000, 160.000000)">
|
||
<path
|
||
d="M13.1458647,7289.43426 C13.1508772,7291.43316 13.1568922,7294.82929 13.1619048,7297.46884 C16.7759398,7297.95757 20.3899749,7298.4613 23.997995,7299 C23.997995,7295.84873 24.002005,7292.71146 23.997995,7289.71311 C20.3809524,7289.71311 16.7649123,7289.43426 13.1458647,7289.43426 M4,7289.43526 L4,7296.22153 C6.72581454,7296.58933 9.45162907,7296.94113 12.1724311,7297.34291 C12.1774436,7294.71736 12.1704261,7292.0908 12.1704261,7289.46524 C9.44661654,7289.47024 6.72380952,7289.42627 4,7289.43526 M4,7281.84344 L4,7288.61071 C6.72581454,7288.61771 9.45162907,7288.57673 12.1774436,7288.57973 C12.1754386,7285.96017 12.1754386,7283.34361 12.1724311,7280.72405 C9.44461153,7281.06486 6.71679198,7281.42567 4,7281.84344 M24,7288.47179 C20.3879699,7288.48578 16.7759398,7288.54075 13.1619048,7288.55175 C13.1598997,7285.88921 13.1598997,7283.22967 13.1619048,7280.56914 C16.7689223,7280.01844 20.3839599,7279.50072 23.997995,7279 C24,7282.15826 23.997995,7285.31353 24,7288.47179"
|
||
id="windows-[#FFF174]"
|
||
></path>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<br />
|
||
<div style="display: flex; justify-content: center">
|
||
<a
|
||
id="download-windows-btn"
|
||
class="btn-secondary"
|
||
href="https://downloads.samidy.com/Monochrome-Windows.exe"
|
||
>Download Windows Version</a
|
||
>
|
||
</div>
|
||
<br />
|
||
<div class="download-buttons-icons-linux" style="display: flex; justify-content: center">
|
||
<svg
|
||
width="96px"
|
||
height="96px"
|
||
viewBox="0 0 16 16"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
fill="none"
|
||
stroke=""
|
||
>
|
||
<g stroke-width="0"></g>
|
||
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
||
<g>
|
||
<path
|
||
fill="var(--foreground)"
|
||
d="M8.294 1c-.09 0-.184.005-.28.012-2.465.194-1.811 2.804-1.85 3.674-.043.637-.174 1.14-.612 1.762-.516.613-1.24 1.604-1.584 2.637-.162.485-.24.982-.167 1.452a.247.247 0 00-.064.079c-.152.156-.263.35-.387.49-.116.115-.283.155-.465.232-.183.08-.384.157-.504.397a.78.78 0 00-.077.351c0 .116.016.234.032.313.034.233.068.425.023.566-.145.396-.163.668-.062.865.102.195.313.275.549.351.472.117 1.114.079 1.618.35.54.272 1.088.39 1.526.274.307-.067.566-.27.705-.552.342-.001.717-.157 1.318-.194.408-.034.918.155 1.503.116.015.078.037.116.067.194l.001.002c.229.454.65.66 1.1.625.45-.035.928-.313 1.316-.762.368-.446.982-.632 1.387-.877.203-.116.367-.273.379-.497.013-.234-.117-.473-.417-.803v-.056l-.002-.002c-.099-.117-.145-.312-.197-.54-.05-.234-.106-.459-.287-.61h-.001c-.035-.032-.072-.04-.11-.08a.208.208 0 00-.11-.037c.25-.745.153-1.487-.102-2.154-.31-.823-.854-1.54-1.269-2.032-.464-.587-.919-1.142-.91-1.965.016-1.255.138-3.578-2.067-3.581zm.309 1.986h.007c.125 0 .231.036.34.116a.89.89 0 01.256.31c.062.152.093.268.097.423 0-.012.004-.023.004-.035v.061a.05.05 0 01-.003-.012l-.002-.014c-.001.142-.03.282-.087.412a.556.556 0 01-.125.195.415.415 0 00-.051-.024c-.06-.027-.115-.038-.166-.078a.765.765 0 00-.128-.038c.03-.035.085-.078.107-.116a.69.69 0 00.051-.234V3.94a.706.706 0 00-.035-.233c-.027-.079-.06-.117-.107-.195-.05-.038-.098-.077-.156-.077h-.01c-.054 0-.102.018-.152.077a.467.467 0 00-.12.195.688.688 0 00-.052.234v.01c0 .053.004.105.011.156-.112-.039-.255-.078-.354-.117a.954.954 0 01-.01-.117V3.86a1.034 1.034 0 01.087-.448.63.63 0 01.251-.31.575.575 0 01.347-.117zm-1.728.035h.02c.084 0 .158.028.234.078a.806.806 0 01.2.272c.053.116.082.233.09.389v.002a.856.856 0 01-.002.155v.047l-.048.014c-.089.032-.16.079-.23.117a.633.633 0 00.002-.156V3.93c-.007-.078-.023-.117-.047-.194a.358.358 0 00-.097-.156.145.145 0 00-.107-.037h-.012c-.042.003-.076.023-.109.077a.322.322 0 00-.07.157.55.55 0 00-.013.193v.008c.007.08.021.117.047.195a.363.363 0 00.096.157c.006.005.012.01.02.014-.04.033-.068.04-.102.079a.177.177 0 01-.077.04 1.53 1.53 0 01-.16-.235 1.034 1.034 0 01-.09-.389c-.01-.132.005-.264.046-.39a.834.834 0 01.165-.312c.075-.077.152-.116.244-.116zm.799.995c.194 0 .428.038.71.233.17.116.304.157.613.273h.002c.149.079.236.155.279.232v-.076a.333.333 0 01.009.274c-.072.18-.301.375-.62.491v.001c-.156.08-.292.195-.452.272-.161.078-.343.17-.59.155a.664.664 0 01-.262-.039 2.077 2.077 0 01-.188-.115c-.113-.079-.211-.194-.357-.271v-.003h-.003c-.233-.144-.359-.299-.4-.414-.04-.157-.003-.275.113-.35.13-.08.221-.159.282-.197.06-.043.083-.059.102-.076h.001v-.002c.099-.117.255-.274.49-.35.08-.021.171-.038.272-.038h-.001zm1.633 1.25c.21.826.698 2.026 1.012 2.609.167.311.5.967.643 1.764.091-.003.193.01.3.037.376-.975-.319-2.022-.636-2.314-.128-.116-.135-.195-.071-.195.344.312.796.917.96 1.608.075.312.093.644.012.974.039.017.079.035.12.04.601.311.824.547.717.896v-.025c-.035-.002-.07 0-.105 0h-.01c.089-.272-.106-.481-.62-.714-.534-.233-.96-.196-1.033.271-.005.025-.008.039-.01.079-.04.013-.082.03-.123.037-.25.157-.386.39-.462.693-.076.31-.1.674-.12 1.09v.002c-.011.195-.099.489-.186.787-.875.626-2.088.897-3.12.195a1.543 1.543 0 00-.234-.31.846.846 0 00-.16-.195.963.963 0 00.27-.04.359.359 0 00.184-.194c.063-.156 0-.407-.201-.678-.201-.273-.543-.58-1.043-.888-.368-.233-.575-.507-.671-.814-.096-.312-.083-.633-.009-.96.143-.624.51-1.23.743-1.611.063-.038.022.078-.238.568-.23.438-.665 1.456-.07 2.248.022-.578.15-1.146.377-1.678.329-.745 1.016-2.044 1.07-3.073.029.021.127.08.17.118.126.078.22.194.343.271a.694.694 0 00.511.196l.065.003c.24 0 .425-.078.581-.156.17-.078.304-.195.432-.233h.003c.272-.08.487-.235.609-.409zm1.275 5.225c.021.35.2.726.514.803.343.078.837-.194 1.045-.446l.123-.006c.184-.004.337.006.494.156l.002.002c.121.116.178.31.228.511.05.233.09.455.239.622.283.307.376.528.37.665l.003-.004v.01l-.002-.007c-.009.153-.108.231-.29.347-.368.234-1.02.416-1.434.916-.36.43-.8.665-1.188.695-.387.03-.721-.117-.918-.524l-.003-.002c-.122-.233-.07-.597.033-.985.103-.39.25-.784.27-1.107.022-.417.044-.779.114-1.058.07-.271.18-.465.374-.574l.026-.013v-.001zm-6.308.028h.006a.53.53 0 01.091.009c.22.032.412.194.597.438l.53.97.003.003c.141.31.44.62.693.955.253.348.45.66.425.915v.004c-.033.434-.28.67-.656.755-.376.079-.887 0-1.397-.27-.565-.314-1.235-.274-1.667-.352-.215-.039-.355-.117-.421-.233-.064-.117-.066-.352.071-.718v-.002l.002-.002c.068-.195.017-.439-.016-.652-.032-.234-.049-.414.025-.549.093-.194.23-.233.402-.31.172-.08.374-.118.534-.275h.001c.15-.157.26-.351.39-.49.11-.117.222-.196.387-.196zM8.45 5.226c-.254.117-.551.312-.868.312-.316 0-.566-.155-.747-.272-.09-.078-.163-.156-.217-.195-.096-.078-.084-.194-.044-.194.064.01.076.078.117.117.056.038.125.116.21.194.17.116.396.272.68.272.283 0 .615-.156.816-.272.114-.078.26-.194.378-.272.09-.08.087-.156.163-.156.074.01.02.078-.086.194-.13.098-.264.189-.403.273zm-.631-.923V4.29c-.004-.012.007-.024.017-.03.043-.024.105-.015.151.003.037 0 .094.04.088.079-.004.029-.05.038-.079.038-.032 0-.054-.025-.082-.04-.03-.01-.085-.004-.095-.037zm-.322 0c-.011.034-.066.028-.097.038-.027.015-.05.04-.081.04-.03 0-.076-.012-.08-.04-.005-.038.052-.077.088-.077.047-.018.107-.028.151-.003.011.005.021.017.018.029v.012h.001z"
|
||
></path>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<br />
|
||
<div style="display: flex; justify-content: center">
|
||
<a
|
||
id="download-linux-btn"
|
||
class="btn-secondary"
|
||
href="https://downloads.samidy.com/Monochrome-linux.zip"
|
||
>Download Linux Version</a
|
||
>
|
||
</div>
|
||
<br />
|
||
<p style="text-align: center; color: #8b8b93">
|
||
Note for Linux version: Please Follow the README.md to install Monochrome.
|
||
</p>
|
||
</div>
|
||
<br />
|
||
<h4 style="text-align: center; padding-top: 15px; color: #8b8b93">
|
||
The App is still in Beta. Please report any issues in our
|
||
<a href="https://monochrome.samidy.com/discord" style="text-decoration: underline"
|
||
>Discord server.</a
|
||
>
|
||
</h4>
|
||
</div>
|
||
</div>
|
||
<div id="page-donate" class="page">
|
||
<h2 class="section-title" style="text-align: center">Donate to Monochrome</h2>
|
||
<div class="donate-content">
|
||
<p style="text-align: center" class="donate-description">
|
||
If Monochrome has been useful to you and you're able to, consider making a donation. <br />
|
||
It helps pay for the domain, and you get to support us :)
|
||
</p>
|
||
<a href="https://ko-fi.com/monochromemusic">
|
||
<button id="donate-btn-page" class="btn-secondary">Donate to Monochrome</button>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<footer class="now-playing-bar">
|
||
<div class="track-info">
|
||
<img src="/assets/appicon.png" alt="Current Track Cover" class="cover" />
|
||
<div class="details">
|
||
<div class="title">Select a song</div>
|
||
<div class="album"></div>
|
||
<div class="artist"></div>
|
||
</div>
|
||
</div>
|
||
<div class="player-controls">
|
||
<div class="buttons">
|
||
<button id="shuffle-btn" title="Shuffle">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-shuffle-icon lucide-shuffle"
|
||
>
|
||
<path d="m18 14 4 4-4 4" />
|
||
<path d="m18 2 4 4-4 4" />
|
||
<path d="M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" />
|
||
<path d="M2 6h1.972a4 4 0 0 1 3.6 2.2" />
|
||
<path d="M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" />
|
||
</svg>
|
||
</button>
|
||
<button id="prev-btn" title="Previous">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-arrow-left-to-line-icon lucide-arrow-left-to-line"
|
||
>
|
||
<path d="M3 19V5" />
|
||
<path d="m13 6-6 6 6 6" />
|
||
<path d="M7 12h14" />
|
||
</svg>
|
||
</button>
|
||
<button class="play-pause-btn" title="Play"></button>
|
||
<button id="next-btn" title="Next">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-arrow-right-to-line-icon lucide-arrow-right-to-line"
|
||
>
|
||
<path d="M17 12H3" />
|
||
<path d="m11 18 6-6-6-6" />
|
||
<path d="M21 5v14" />
|
||
</svg>
|
||
</button>
|
||
<button id="repeat-btn" title="Repeat">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-repeat-icon lucide-repeat"
|
||
>
|
||
<path d="m17 2 4 4-4 4" />
|
||
<path d="M3 11v-1a4 4 0 0 1 4-4h14" />
|
||
<path d="m7 22-4-4 4-4" />
|
||
<path d="M21 13v1a4 4 0 0 1-4 4H3" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="progress-container">
|
||
<span id="current-time">0:00</span>
|
||
<div id="progress-bar" class="progress-bar">
|
||
<div id="progress-fill" class="progress-fill"></div>
|
||
</div>
|
||
<span id="total-duration">0:00</span>
|
||
</div>
|
||
</div>
|
||
<div class="volume-controls">
|
||
<div class="player-actions-row">
|
||
<button
|
||
id="now-playing-like-btn"
|
||
class="like-btn"
|
||
data-action="toggle-like"
|
||
title="Save to Favorites"
|
||
style="display: none"
|
||
></button>
|
||
<button id="now-playing-add-playlist-btn" title="Add to Playlist" class="desktop-only">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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>
|
||
</button>
|
||
|
||
<button
|
||
id="now-playing-mix-btn"
|
||
class="mix-btn"
|
||
data-action="track-mix"
|
||
title="Track Mix"
|
||
style="display: none"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
>
|
||
<path
|
||
d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
<button id="toggle-lyrics-btn" title="Lyrics" style="display: none">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-mic"
|
||
>
|
||
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" />
|
||
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
||
<line x1="12" y1="19" x2="12" y2="22" />
|
||
<line x1="8" y1="22" x2="16" y2="22" />
|
||
</svg>
|
||
</button>
|
||
<button id="download-current-btn" title="Download current track" class="desktop-only">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||
<polyline points="7 10 12 15 17 10"></polyline>
|
||
<line x1="12" y1="15" x2="12" y2="3"></line>
|
||
</svg>
|
||
</button>
|
||
<button id="cast-btn" title="Cast">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"></path>
|
||
<path d="M2 12a9 9 0 0 1 9 9"></path>
|
||
<path d="M2 17a5 5 0 0 1 5 5"></path>
|
||
<path d="M2 22h.01"></path>
|
||
</svg>
|
||
</button>
|
||
|
||
<button id="mobile-add-playlist-btn" class="mobile-only">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<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>
|
||
</button>
|
||
<button id="sleep-timer-btn" title="Sleep Timer" class="mobile-only">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<circle cx="12" cy="12" r="10" />
|
||
<polyline points="12,6 12,12 16,14" />
|
||
</svg>
|
||
</button>
|
||
<button id="queue-btn" title="Queue">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="24"
|
||
height="24"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
class="lucide lucide-list-icon lucide-list"
|
||
>
|
||
<path d="M3 5h.01" />
|
||
<path d="M3 12h.01" />
|
||
<path d="M3 19h.01" />
|
||
<path d="M8 5h13" />
|
||
<path d="M8 12h13" />
|
||
<path d="M8 19h13" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="volume-slider-row desktop-only">
|
||
<button id="volume-btn" title="Mute"></button>
|
||
<div id="volume-bar" class="volume-bar">
|
||
<div id="volume-fill" class="volume-fill"></div>
|
||
</div>
|
||
<button id="sleep-timer-btn-desktop" title="Sleep Timer">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<circle cx="12" cy="12" r="10" />
|
||
<polyline points="12,6 12,12 16,14" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
<script src="https://cdn.jsdelivr.net/npm/pocketbase@0.21.3/dist/pocketbase.umd.js"></script>
|
||
<script src="/neutralino.js"></script>
|
||
<script type="module" src="/js/app.js"></script>
|
||
</body>
|
||
</html>
|