377 lines
No EOL
23 KiB
HTML
377 lines
No EOL
23 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">
|
|
<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"></audio>
|
|
<div id="context-menu">
|
|
<ul>
|
|
<li data-action="add-to-queue">Add to Queue</li>
|
|
<li data-action="download">Download</li>
|
|
</ul>
|
|
</div>
|
|
<div id="queue-modal-overlay" style="display: none;">
|
|
<div id="queue-modal">
|
|
<div id="queue-modal-header">
|
|
<h3>Queue</h3>
|
|
<button id="close-queue-btn">×</button>
|
|
</div>
|
|
<div id="queue-list"></div>
|
|
</div>
|
|
</div>
|
|
<div class="queue-track-menu" id="queue-track-menu">
|
|
<ul>
|
|
<li data-action="remove">Remove from Queue</li>
|
|
</ul>
|
|
</div>
|
|
<div id="sidebar-overlay"></div>
|
|
|
|
<div class="app-container">
|
|
<aside class="sidebar">
|
|
<div>
|
|
<div class="sidebar-logo">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="14.75 14.75 70.5 70.5" >
|
|
<g fill="white" >
|
|
<path d="M38.25 14.75H85.25V61.75H61.75V38.25H38.25ZM14.75 38.25H38.25V61.75H61.75V85.25H14.75Z" />
|
|
</g>
|
|
</svg>
|
|
<span>Monochrome</span>
|
|
</div>
|
|
<nav class="sidebar-nav">
|
|
<ul>
|
|
<li class="nav-item">
|
|
<a href="#home">
|
|
<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">
|
|
<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">
|
|
<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">
|
|
<a href="https://github.com/eduardprigoana/monochrome" 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" 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>
|
|
<span>GitHub</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="https://status.monochrome.tf" 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" class="lucide lucide-activity-icon lucide-activity"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"/></svg>
|
|
<span>Status</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="main-content">
|
|
<header class="main-header">
|
|
<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">
|
|
<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...">
|
|
</form>
|
|
</header>
|
|
|
|
<div id="page-home" class="page">
|
|
<section class="content-section">
|
|
<h2 class="section-title">Recent Albums</h2>
|
|
<div class="card-grid" id="home-recent-albums"></div>
|
|
</section>
|
|
<section class="content-section">
|
|
<h2 class="section-title">Recent Artists</h2>
|
|
<div class="card-grid" id="home-recent-artists"></div>
|
|
</section>
|
|
</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>
|
|
</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>
|
|
|
|
<div id="page-album" class="page">
|
|
<header class="detail-header">
|
|
<img id="album-detail-image" src="" alt="" class="detail-header-image">
|
|
<div class="detail-header-info">
|
|
<div class="type">Album</div>
|
|
<h1 class="title" id="album-detail-title"></h1>
|
|
<div class="meta" id="album-detail-meta"></div>
|
|
<div class="detail-header-actions">
|
|
<button id="play-album-btn" class="btn-primary">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
|
</svg>
|
|
<span>Play Album</span>
|
|
</button>
|
|
<button id="download-album-btn" class="btn-primary">
|
|
<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>
|
|
<span>Download Album</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<div class="track-list" id="album-detail-tracklist"></div>
|
|
</div>
|
|
|
|
<div id="page-artist" class="page">
|
|
<header class="detail-header">
|
|
<img id="artist-detail-image" src="" alt="Artist" class="detail-header-image artist">
|
|
<div class="detail-header-info">
|
|
<div class="type">Artist</div>
|
|
<h1 class="title" id="artist-detail-name"></h1>
|
|
<div class="meta" id="artist-detail-meta"></div>
|
|
<div class="detail-header-actions">
|
|
<button id="download-discography-btn" class="btn-primary">
|
|
<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>
|
|
<span>Download Discography</span>
|
|
</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>
|
|
</div>
|
|
|
|
<div id="page-settings" class="page">
|
|
<h2 class="section-title">Settings</h2>
|
|
<div class="settings-list">
|
|
<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="monochrome">Black</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="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">
|
|
<div class="info">
|
|
<span class="label">Audio Quality</span>
|
|
<span class="description">Quality for streaming and downloads</span>
|
|
</div>
|
|
<select id="quality-setting">
|
|
<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">Crossfade</span>
|
|
<span class="description">Fade between tracks smoothly</span>
|
|
</div>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="crossfade-toggle">
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="setting-item" id="crossfade-duration-setting" style="display: none;">
|
|
<div class="info">
|
|
<span class="label">Crossfade Duration</span>
|
|
<span class="description">Duration in seconds</span>
|
|
</div>
|
|
<input type="number" id="crossfade-duration" min="1" max="12" value="5">
|
|
</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">Normalize Volume</span>
|
|
<span class="description">Set the same volume level for all tracks</span>
|
|
</div>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox">
|
|
<span class="slider"></span>
|
|
</label>
|
|
</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 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. Automatically sorted by speed.</span>
|
|
</div>
|
|
<button id="refresh-speed-test-btn" class="btn-secondary">Refresh Speed Test</button>
|
|
</div>
|
|
<ul id="api-instance-list"></ul>
|
|
</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.
|
|
</p>
|
|
<div class="about-features">
|
|
<h4>Features</h4>
|
|
<ul>
|
|
<li>High-quality lossless audio streaming</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 and crossfade support</li>
|
|
</ul>
|
|
</div>
|
|
<div class="about-tech">
|
|
<h4>Technology Stack</h4>
|
|
<p>Vanilla JavaScript • ES6 Modules • IndexedDB • Service Workers • Media Session API</p>
|
|
</div>
|
|
<div class="about-links">
|
|
<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
|
|
</a>
|
|
</div>
|
|
<div class="about-footer">
|
|
<p class="version">Version 1.1.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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="now-playing-bar">
|
|
<div class="track-info">
|
|
<img src="https://picsum.photos/seed/placeholder/112" alt="Current Track Cover" class="cover">
|
|
<div class="details">
|
|
<div class="title">Select a song</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">
|
|
<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>
|
|
<button id="volume-btn" title="Mute"></button>
|
|
<div id="volume-bar" class="volume-bar">
|
|
<div id="volume-fill" class="volume-fill"></div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<script type="module" src="js/app.js"></script>
|
|
</body>
|
|
</html> |