Files
StarlightRadio/public/index.html
T

398 lines
17 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, maximum-scale=1"
/>
<meta name="theme-color" content="#f4f5f9" />
<title>Xiao の 电台</title>
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<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=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="page-bg"></div>
<div class="bg-shapes">
<div class="s"></div>
<div class="s"></div>
<div class="s"></div>
</div>
<div class="toast-container" id="toastContainer"></div>
<!-- Header -->
<header class="top-header">
<div class="header-inner">
<div class="brand">
<div class="brand-icon">
<img src="favicon.png" alt="logo" />
</div>
<span>Xiao の 电台</span>
<span class="live-pill"
><span class="live-dot"></span>LIVE</span
>
</div>
<div class="header-actions">
<span class="cache-pill" id="cacheInfo"
><i class="bi bi-database"></i> --</span
>
<a
href="/hls/index.m3u8"
class="btn-link-sm"
target="_blank"
><i class="bi bi-link-45deg"></i> 直播流</a
>
</div>
</div>
</header>
<!-- Pages -->
<div class="page-container">
<!-- 1. Search -->
<div class="page active" id="pageSearch">
<div class="search-card">
<div class="search-header">
<h2>导入音源</h2>
<p>输入网易云歌曲 ID、链接或歌名查询并下载</p>
</div>
<div class="search-input-wrap">
<div class="search-field">
<input
type="text"
id="musicId"
placeholder="歌曲 ID、链接或歌名,如 33894312 / 知我"
inputmode="text"
autocomplete="off"
spellcheck="false"
/>
<button class="btn-search" id="btnSearch">
查询
</button>
</div>
</div>
<div class="parse-hint" id="parseHint">
<i class="bi bi-magic"></i>
已自动解析为 ID:<code id="parsedId"></code>
</div>
<!-- Preview: info only -->
<div id="musicPreview" style="display: none">
<div class="preview-card">
<div class="preview-cover-section">
<div class="preview-cover-wrap">
<img
id="musicCover"
class="preview-cover"
src=""
alt=""
/>
</div>
<div class="preview-info">
<div
class="preview-title"
id="musicName"
></div>
<div
class="preview-artist"
id="musicArtist"
></div>
</div>
</div>
<div
class="preview-details"
id="musicDetails"
></div>
<div class="preview-actions">
<button class="btn-download" id="btnDownload">
<i class="bi bi-download"></i> 下载到资源库
</button>
</div>
</div>
</div>
<div
id="searchResults"
class="searchResults"
style="display: none"
></div>
<div class="search-tips" id="searchTips">
<div class="tips-icon">
<i class="bi bi-music-note-beamed"></i>
</div>
<div class="tips-title">搜索并下载音乐</div>
<div class="tips-desc">
在网易云音乐中复制歌曲 ID 或完整链接<br />粘贴到上方输入框即可自动识别
</div>
</div>
</div>
</div>
<!-- 2. Resources -->
<div class="page" id="pageResources">
<div class="section-card">
<div class="section-header">
<div class="section-title">
<i
class="bi bi-folder2-open"
style="color: var(--accent)"
></i>
已有音乐
<span class="section-count" id="resourceCount"
>0</span
>
</div>
<input
type="text"
class="resource-search"
id="resourceSearch"
placeholder="搜索已有音乐..."
autocomplete="off"
spellcheck="false"
/>
</div>
<div class="section-body" id="resourceList">
<div class="empty-state">
<div class="empty-icon">
<i class="bi bi-arrow-repeat"></i>
</div>
<div class="empty-title">加载中...</div>
</div>
</div>
</div>
</div>
<!-- 3. Playlist -->
<div class="page" id="pagePlaylist">
<div class="section-card">
<div class="section-header">
<div class="section-title">
<i
class="bi bi-music-note-list"
style="color: var(--accent)"
></i>
播放列表
<span class="section-count" id="playlistCount"
>0</span
>
</div>
<button class="btn-clear" id="btnClear">
<i class="bi bi-trash3"></i> 清空
</button>
</div>
<div class="section-body" id="playlistList">
<div class="empty-state">
<div class="empty-icon">
<i class="bi bi-arrow-repeat"></i>
</div>
<div class="empty-title">加载中...</div>
</div>
</div>
</div>
</div>
<!-- 4. About — 保持原有风格体系 -->
<div class="page" id="pageAbout">
<div class="about-card">
<!-- Hero -->
<div class="about-hero">
<div class="about-logo">
<img src="favicon.png" alt="logo" />
</div>
<h2>Xiao の 电台</h2>
<p>基于 HLS 的个人在线音乐电台</p>
</div>
<!-- Body -->
<div class="about-body">
<!-- 项目简介 -->
<div class="about-section">
<div class="about-section-icon">
<i class="bi bi-book"></i>
</div>
<div class="about-section-content">
<h3>项目简介</h3>
<p>
一个基于 Web 的个人在线音乐电台,支持通过网易云音乐歌曲 ID、链接或歌名进行搜索、试听与下载,提供 HLS 直播流播放、资源管理与播放列表拖拽排序等功能。界面采用简约玻璃拟态风格,适配移动端与桌面端多种屏幕尺寸。
</p>
</div>
</div>
<!-- 核心功能 -->
<div class="about-section">
<div class="about-section-icon">
<i class="bi bi-lightning-charge"></i>
</div>
<div class="about-section-content">
<h3>核心功能</h3>
<div class="about-feature-list">
<div class="about-feat">
<i class="bi bi-search" style="color: var(--accent)"></i>
<span>智能搜索 — 歌曲 ID、链接或歌名关键词,自动解析并一键导入</span>
</div>
<div class="about-feat">
<i class="bi bi-broadcast" style="color: var(--success)"></i>
<span>直播流 — FFmpeg HLS 切片 + 滑动窗口,5 秒精度连续输出</span>
</div>
<div class="about-feat">
<i class="bi bi-database-check" style="color: var(--warning)"></i>
<span>智能缓存 — 预生成切片、增量追踪、LRU 淘汰与自动清理</span>
</div>
<div class="about-feat">
<i class="bi bi-hand-index-thumb" style="color: #8b5cf6"></i>
<span>播放列表 — 拖拽排序、添加/移除/跳转,SSE 实时状态推送</span>
</div>
<div class="about-feat">
<i class="bi bi-phone" style="color: #06b6d4"></i>
<span>全端适配 — 玻璃拟态 UI,手势滑动、键盘快捷键、拖拽进度条</span>
</div>
</div>
</div>
</div>
<!-- 技术栈 -->
<div class="about-section">
<div class="about-section-icon">
<i class="bi bi-tools"></i>
</div>
<div class="about-section-content">
<h3>技术栈</h3>
<div class="tech-tags">
<span>HTML5</span>
<span>CSS3</span>
<span>JavaScript ES6+</span>
<span>Bootstrap 5</span>
<span>SortableJS</span>
<span>HLS.js</span>
<span>Node.js</span>
<span>Express</span>
<span>FFmpeg</span>
<span>Chokidar</span>
</div>
</div>
</div>
<!-- 开发语言 -->
<div class="about-section">
<div class="about-section-icon">
<i class="bi bi-translate"></i>
</div>
<div class="about-section-content">
<h3>开发语言</h3>
<p>前端:HTML / CSS / JavaScript &nbsp;|&nbsp; 后端:Node.js</p>
</div>
</div>
<!-- 作者 -->
<div class="about-section">
<div class="about-section-icon">
<i class="bi bi-person"></i>
</div>
<div class="about-section-content">
<h3>作者</h3>
<p style="margin-bottom: 0.45rem">Xiao — 独立开发者</p>
<a
href="https://github.com/Xiao-no-love/"
target="_blank"
rel="noopener"
class="about-gh-link"
>
<i class="bi bi-github"></i>
<span>Xiao-no-love</span>
<i class="bi bi-box-arrow-up-right" style="font-size: 0.6rem; opacity: 0.5"></i>
</a>
</div>
</div>
<!-- 免责声明 -->
<div class="about-section disclaimer-section">
<div class="about-section-icon">
<i class="bi bi-exclamation-triangle"></i>
</div>
<div class="about-section-content">
<h3>免责声明</h3>
<p>
本项目所有音源均来自网易云音乐,仅供个人学习与技术研究使用,严禁用于任何商业用途。音乐版权归原创作者及网易云音乐平台所有。如有侵权,请联系删除。请于下载后 24 小时内清除,支持正版音乐。
</p>
</div>
</div>
</div>
<!-- Footer -->
<div class="about-version">
v1.0.0 &middot; Built with &hearts; &middot;
<a
href="https://github.com/Xiao-no-love/"
target="_blank"
rel="noopener"
style="color: var(--accent); text-decoration: none"
>GitHub</a>
</div>
</div>
</div>
</div>
<!-- Mini Player — full background progress + drag seek -->
<div class="mini-player" id="miniPlayer">
<div class="mp-progress" id="mpProgress"></div>
<div class="mp-body" id="mpBody">
<img
class="mp-cover"
id="mpCover"
src=""
alt=""
onerror="this.style.display = 'none'"
/>
<div class="mp-info">
<div class="mp-name" id="mpName">未在播放</div>
<div class="mp-artist" id="mpArtist">--</div>
</div>
<div class="mp-controls">
<span class="mp-time" id="mpTime">0:00</span>
<button class="mp-play" id="mpPlay">
<i class="bi bi-play-fill"></i>
</button>
<button class="mp-expand" id="mpExpand" title="查看详情">
<i class="bi bi-chevron-up"></i>
</button>
</div>
</div>
</div>
<!-- Bottom Nav — 4 tabs, full-width aligned with content -->
<nav class="bottom-nav">
<div class="bottom-nav-inner">
<button class="nav-tab active" data-page="pageSearch">
<i class="bi bi-search"></i><span>搜索</span>
</button>
<button class="nav-tab" data-page="pageResources">
<i class="bi bi-folder"></i><span>资源</span>
</button>
<button class="nav-tab" data-page="pagePlaylist">
<i class="bi bi-list-ul"></i><span>列表</span>
</button>
<button class="nav-tab" data-page="pageAbout">
<i class="bi bi-info-circle"></i><span>关于</span>
</button>
</div>
</nav>
<!-- Global audio element -->
<audio id="musicPlayer" preload="none" style="display: none"></audio>
<script src="js/app.js"></script>
</body>
</html>