diff --git a/index.html b/index.html index 6add97e..bb01af3 100644 --- a/index.html +++ b/index.html @@ -1,24 +1,1195 @@ -AM Downloader + + +AM Downloader - 音乐下载与同步 + + + +:root { + --bg-base: #080c14; + --bg-surface: #0f1624; + --bg-surface-elevated: #162032; + --bg-card: rgba(17, 26, 42, 0.75); + --bg-card-hover: rgba(24, 37, 60, 0.85); + --border-subtle: rgba(255, 255, 255, 0.08); + --border-focus: rgba(16, 185, 129, 0.5); + --text-main: #f1f5f9; + --text-muted: #94a3b8; + --text-dim: #64748b; + --primary: #10b981; + --primary-hover: #059669; + --primary-glow: rgba(16, 185, 129, 0.25); + --accent: #38bdf8; + --danger: #f43f5e; + --warning: #f59e0b; + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --radius-full: 9999px; + --transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.2); + --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.35); + --shadow-glow: 0 0 30px rgba(16, 185, 129, 0.15); +} + +* { box-sizing: border-box; margin: 0; padding: 0; } +body { + margin: 0; + background-color: var(--bg-base); + background-image: + radial-gradient(at 0% 0%, rgba(16, 185, 129, 0.07) 0px, transparent 50%), + radial-gradient(at 100% 100%, rgba(56, 189, 248, 0.06) 0px, transparent 50%); + background-attachment: fixed; + color: var(--text-main); + font-family: 'Plus Jakarta Sans', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + font-size: 14px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + min-height: 100vh; +} + +.wrap { + max-width: 1120px; + margin: auto; + padding: 24px 20px 96px; +} + +/* Header & Top Bar */ +.top { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + margin-bottom: 20px; +} + +.brand { + font-size: 22px; + font-weight: 800; + letter-spacing: -0.5px; + display: flex; + align-items: center; + gap: 8px; +} +.brand b { + color: var(--primary); + text-shadow: 0 0 16px var(--primary-glow); +} +.sub { + color: var(--text-muted); + font-size: 12px; + margin-top: 2px; + font-weight: 500; +} + +.nav { + display: flex; + gap: 4px; + background: rgba(15, 22, 36, 0.85); + backdrop-filter: blur(12px); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 5px; + box-shadow: var(--shadow-sm); +} + +.nav button { + border: 0; + background: transparent; + color: var(--text-muted); + padding: 8px 16px; + border-radius: var(--radius-md); + cursor: pointer; + font-weight: 600; + font-size: 13.5px; + transition: var(--transition); + display: flex; + align-items: center; + gap: 6px; +} +.nav button:hover { + color: var(--text-main); + background: rgba(255, 255, 255, 0.04); +} +.nav button.active { + background: var(--bg-surface-elevated); + color: #fff; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); + border: 1px solid var(--border-subtle); +} + +.badge { + display: inline-flex; + min-width: 18px; + height: 18px; + padding: 0 5px; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + background: var(--primary); + color: #032014; + font-size: 11px; + font-weight: 700; +} + +/* User Pill & Auth Bar */ +.authbar { + display: flex; + align-items: center; + gap: 10px; + justify-content: flex-end; + margin: -6px 0 18px; +} +.userpill { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 14px; + border: 1px solid var(--border-subtle); + border-radius: var(--radius-full); + background: rgba(17, 26, 42, 0.6); + backdrop-filter: blur(8px); + color: var(--text-muted); + font-size: 12.5px; + font-weight: 500; +} +.userpill::before { + content: ''; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--primary); + box-shadow: 0 0 8px var(--primary); +} + +/* Views */ +.view { display: none; } +.view.active { + display: block; + animation: fadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1); +} +@keyframes fadeIn { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Cards & Layout */ +.card { + background: var(--bg-card); + backdrop-filter: blur(16px); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 22px; + margin-bottom: 18px; + box-shadow: var(--shadow-sm); + transition: var(--transition); +} +.card:hover { + border-color: rgba(255, 255, 255, 0.12); +} + +.section-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + margin-bottom: 18px; +} +.title { + font-size: 17px; + font-weight: 700; + letter-spacing: -0.3px; + color: var(--text-main); +} +.muted { + color: var(--text-muted); + font-size: 12.5px; + margin-top: 2px; +} + +/* Hero Section */ +.hero { + padding: 56px 16px 42px; + text-align: center; + position: relative; +} +.hero h1 { + font-size: 38px; + font-weight: 800; + letter-spacing: -1px; + margin: 0 0 10px; + background: linear-gradient(135deg, #ffffff 40%, #94a3b8 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.hero p { + color: var(--text-muted); + font-size: 15px; + max-width: 500px; + margin: 0 auto 28px; +} + +/* Inputs & Form Controls */ +.input { + width: 100%; + padding: 11px 14px; + border-radius: var(--radius-md); + border: 1px solid var(--border-subtle); + background: rgba(10, 16, 28, 0.85); + color: #fff; + font-size: 13.5px; + outline: none; + transition: var(--transition); + font-family: inherit; +} +.input:focus { + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--primary-glow); + background: rgba(12, 20, 36, 0.95); +} +select.input { + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + padding-right: 36px; + cursor: pointer; +} + +/* Buttons */ +.btn { + border: 0; + border-radius: var(--radius-md); + padding: 10px 18px; + background: var(--primary); + color: #032014; + font-weight: 700; + font-size: 13.5px; + cursor: pointer; + white-space: nowrap; + transition: var(--transition); + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + box-shadow: 0 2px 10px var(--primary-glow); +} +.btn:hover:not(:disabled) { + background: var(--primary-hover); + transform: translateY(-1px); + box-shadow: 0 4px 14px rgba(16, 185, 129, 0.35); +} +.btn:active:not(:disabled) { + transform: translateY(0); +} +.btn.alt { + background: rgba(255, 255, 255, 0.06); + border: 1px solid var(--border-subtle); + color: var(--text-main); + box-shadow: none; +} +.btn.alt:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.16); + transform: translateY(-1px); +} +.btn:disabled { + opacity: 0.45; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +/* Search Bar */ +.searchbar { + display: flex; + max-width: 740px; + margin: auto; + gap: 10px; + background: rgba(15, 22, 36, 0.7); + padding: 6px; + border-radius: var(--radius-lg); + border: 1px solid var(--border-subtle); + backdrop-filter: blur(12px); + box-shadow: var(--shadow-md); +} +.searchbar .input { + border: 1px solid transparent; + background: transparent; +} +.searchbar .input:focus { + background: rgba(255, 255, 255, 0.03); + box-shadow: none; +} +.searchbar select.input { + max-width: 110px; + background-color: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); +} + +/* Row & Field Layout */ +.row { display: flex; gap: 10px; align-items: center; } +.row > * { min-width: 0; flex: 1; } +.field { margin-top: 14px; } +.field label { + display: block; + color: var(--text-muted); + font-size: 12.5px; + font-weight: 600; + margin-bottom: 6px; +} + +/* Music Result Card & List */ +.results { display: grid; gap: 6px; } +.result { + display: grid; + grid-template-columns: 52px minmax(0, 1fr) auto; + gap: 14px; + align-items: center; + padding: 12px 14px; + border-radius: var(--radius-md); + background: rgba(255, 255, 255, 0.015); + border: 1px solid transparent; + transition: var(--transition); +} +.result:hover { + background: rgba(255, 255, 255, 0.04); + border-color: var(--border-subtle); +} +.cover { + width: 52px; + height: 52px; + border-radius: var(--radius-md); + object-fit: cover; + background: #151e30; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} +.name { + font-weight: 700; + font-size: 14.5px; + color: var(--text-main); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.meta { + font-size: 12px; + color: var(--text-muted); + margin-top: 3px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.toolbar { display: flex; gap: 8px; align-items: center; } +.quality-select { + max-width: 136px; + padding: 8px 12px; + font-size: 12.5px; +} + +/* Chips & Quality Badges */ +.file-info { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; } +.chip { + display: inline-flex; + align-items: center; + padding: 3px 8px; + border-radius: var(--radius-sm); + background: rgba(255, 255, 255, 0.05); + color: var(--text-muted); + font-size: 11px; + font-family: 'JetBrains Mono', monospace; + font-weight: 500; + border: 1px solid var(--border-subtle); +} +.chip.actual { + color: #6ee7b7; + background: rgba(16, 185, 129, 0.1); + border-color: rgba(16, 185, 129, 0.25); +} +.chip.warn { + color: #fcd34d; + background: rgba(245, 158, 11, 0.1); + border-color: rgba(245, 158, 11, 0.25); +} + +/* Progress & Status */ +.progress { + height: 6px; + background: rgba(255, 255, 255, 0.06); + border-radius: var(--radius-full); + margin-top: 8px; + overflow: hidden; +} +.progress i { + display: block; + height: 100%; + background: linear-gradient(90deg, var(--primary), #34d399); + border-radius: var(--radius-full); + transition: width 0.3s ease; +} + +/* Task Tabs */ +.task-tabs { + display: flex; + gap: 6px; + overflow-x: auto; + margin-bottom: 16px; + padding-bottom: 4px; +} +.task-tabs button { + border: 1px solid var(--border-subtle); + background: rgba(15, 22, 36, 0.6); + color: var(--text-muted); + border-radius: var(--radius-md); + padding: 7px 14px; + white-space: nowrap; + cursor: pointer; + font-weight: 600; + font-size: 13px; + transition: var(--transition); +} +.task-tabs button:hover { + color: var(--text-main); + background: rgba(255, 255, 255, 0.05); +} +.task-tabs button.active { + background: var(--bg-surface-elevated); + border-color: rgba(255, 255, 255, 0.16); + color: #fff; +} + +/* Settings & Account Grids */ +.settings-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; +} +.setting-card h3 { + margin: 0 0 4px; + font-size: 16px; + font-weight: 700; + color: var(--text-main); +} +.setting-card p { + margin: 0 0 16px; + color: var(--text-muted); + font-size: 12.5px; +} + +/* Notices & Alerts */ +.notice { + padding: 12px 14px; + background: rgba(16, 26, 44, 0.6); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + color: var(--text-muted); + font-size: 12.5px; + margin-top: 14px; + line-height: 1.5; +} + +/* Admin Shell & Dashboard */ +.admin-shell { display: grid; gap: 18px; } +.admin-header { padding: 4px 2px; } +.admin-header h2 { + margin: 4px 0 6px; + font-size: 28px; + font-weight: 800; + letter-spacing: -0.6px; +} +.eyebrow { + font-size: 11px; + letter-spacing: 2px; + color: var(--primary); + font-weight: 800; + text-transform: uppercase; +} +.admin-dashboard { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} +.admin-tile { + border: 1px solid var(--border-subtle); + background: var(--bg-card); + backdrop-filter: blur(12px); + color: var(--text-main); + border-radius: var(--radius-lg); + padding: 22px; + text-align: left; + display: grid; + gap: 6px; + cursor: pointer; + transition: var(--transition); + box-shadow: var(--shadow-sm); +} +.admin-tile:hover { + background: var(--bg-card-hover); + border-color: rgba(255, 255, 255, 0.16); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} +.admin-tile span { font-size: 26px; } +.admin-tile strong { font-size: 16.5px; font-weight: 700; color: #fff; } +.admin-tile small { color: var(--text-muted); font-size: 12px; } + +/* Admin Panels & Lists */ +.admin-panel { + background: var(--bg-card); + backdrop-filter: blur(16px); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 20px; +} +.panel-head { + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 20px; +} +.panel-head > div:nth-child(2) { flex: 1; } + +.user-filters { + display: flex; + gap: 6px; + overflow-x: auto; + margin: 16px 0; + padding-bottom: 4px; +} +.user-filters button { + border: 1px solid var(--border-subtle); + background: rgba(15, 22, 36, 0.6); + color: var(--text-muted); + border-radius: var(--radius-md); + padding: 7px 14px; + white-space: nowrap; + font-weight: 600; + font-size: 12.5px; + cursor: pointer; + transition: var(--transition); +} +.user-filters button.active { + background: var(--bg-surface-elevated); + border-color: rgba(255, 255, 255, 0.16); + color: #fff; +} + +.user-group { margin-top: 16px; } +.group-head { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 2px 10px; + border-bottom: 1px solid var(--border-subtle); + margin-bottom: 10px; +} +.group-list { display: grid; gap: 8px; } +.user-card { + background: rgba(11, 18, 30, 0.6); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 14px; + transition: var(--transition); +} +.user-card:hover { + border-color: rgba(255, 255, 255, 0.14); + background: rgba(14, 23, 38, 0.7); +} +.user-main { + display: flex; + justify-content: space-between; + gap: 14px; + align-items: center; +} +.user-stats { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; } +.stat-chip { + background: rgba(255, 255, 255, 0.04); + color: var(--text-muted); + padding: 3px 8px; + border-radius: var(--radius-sm); + font-size: 11.5px; + border: 1px solid var(--border-subtle); +} +.user-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; } + +/* Admin Summaries & Ledgers */ +.admin-summary { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px; + margin-top: 16px; +} +.summary-card { + background: rgba(11, 18, 30, 0.6); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 16px; +} +.summary-card strong { + display: block; + font-size: 24px; + font-weight: 800; + color: var(--primary); + margin-top: 4px; +} +.ledger { display: grid; gap: 8px; margin-top: 14px; } +.ledger-item { + padding: 12px 14px; + border: 1px solid var(--border-subtle); + background: rgba(11, 18, 30, 0.6); + border-radius: var(--radius-md); +} + +.detail-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; +} +.detail-card { + background: rgba(11, 18, 30, 0.6); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 18px; +} +.detail-card h4 { margin: 0 0 12px; font-size: 15px; } +.detail-value { font-size: 28px; font-weight: 800; color: var(--primary); } +.detail-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; } + +/* Empty state */ +.empty { + text-align: center; + padding: 48px 16px; + color: var(--text-dim); + font-size: 13.5px; +} + +/* Login Modal */ +.login-overlay { + position: fixed; + inset: 0; + background: rgba(6, 10, 18, 0.85); + backdrop-filter: blur(20px); + z-index: 999; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} +.login-box { + width: min(390px, 100%); + background: var(--bg-surface); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 28px; + box-shadow: var(--shadow-md); + animation: modalIn 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} +@keyframes modalIn { + from { opacity: 0; transform: scale(0.96) translateY(8px); } + to { opacity: 1; transform: scale(1) translateY(0); } +} +.login-box h2 { + margin: 0 0 6px; + font-size: 22px; + font-weight: 800; + letter-spacing: -0.5px; +} +.login-box p { + color: var(--text-muted); + font-size: 13px; + margin: 0 0 20px; +} +.login-error { + color: var(--danger); + font-size: 12px; + margin-top: 10px; + min-height: 16px; +} + +/* Mobile Bottom Navigation Dock */ +.mobile-nav { + display: none; +} + +@media(max-width: 720px) { + .wrap { padding: 16px 14px 110px; } + .top .nav { display: none; } + .hero { padding: 36px 8px 26px; } + .hero h1 { font-size: 28px; } + .searchbar { flex-wrap: wrap; } + .searchbar .input { flex-basis: 100%; } + .searchbar select, .searchbar button { flex: 1; } + .settings-grid { grid-template-columns: 1fr; } + + .result { + grid-template-columns: 44px minmax(0, 1fr); + gap: 10px; + padding: 10px; + } + .cover { width: 44px; height: 44px; } + .result .toolbar { + grid-column: 2; + display: flex; + width: 100%; + gap: 8px; + margin-top: 6px; + } + .result .quality-select { max-width: none; min-width: 0; flex: 1; } + .result .toolbar .btn { padding: 9px 14px; min-width: 76px; } + + .mobile-nav { + position: fixed; + left: 12px; + right: 12px; + bottom: max(12px, env(safe-area-inset-bottom)); + display: flex; + justify-content: space-around; + gap: 4px; + padding: 6px; + background: rgba(15, 22, 36, 0.88); + backdrop-filter: blur(20px); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + z-index: 100; + box-shadow: var(--shadow-md); + } + .mobile-nav button { + flex: 1; + min-width: 0; + border: 0; + background: transparent; + color: var(--text-muted); + padding: 8px 2px; + border-radius: var(--radius-md); + font-size: 11px; + font-weight: 600; + display: flex; + flex-direction: column; + align-items: center; + gap: 3px; + cursor: pointer; + transition: var(--transition); + } + .mobile-nav button.active { + background: var(--bg-surface-elevated); + color: #fff; + } + + .admin-dashboard { grid-template-columns: 1fr; } + .admin-panel { padding: 14px; } + .admin-create > * { flex-basis: 100%; } + .user-main { align-items: flex-start; flex-direction: column; } + .user-actions .btn { flex: 1; min-width: 100px; } + .admin-summary { grid-template-columns: 1fr 1fr; } + .detail-grid { grid-template-columns: 1fr; } + .row { flex-wrap: wrap; } + .row > * { flex-basis: 100%; } +} + -

AM Downloader

登录后管理你的下载任务、积分和同步设置。

-
AM Downloader
搜索 · 下载 · WebDAV 同步
-
-

找到你想听的音乐

搜索歌曲、歌手或专辑,加入下载队列即可。

最近下载
- -
下载队列
任务会自动处理;本地文件是否保留由“设置 → 下载 → 本地文件策略”决定。
-

账户与积分

积分用于新的实际下载;失败会自动退回。

加载中…

我的 WebDAV

仅 privileged / admin 可配置。密码不会回显。

-
ADMIN CENTER

管理中心

用户、积分、兑换码与审计统一管理。

-

音乐 API

用于搜索和解析音乐资源,与 Downloader Web 服务分离。

当前服务架构:Downloader Web 使用 9540,音乐 API 使用 9530。

下载

设置文件保存位置与默认音质。

仅在 WebDAV 上传成功或远端已存在相同大小文件后删除本地成品;上传失败不会删除。

WebDAV

可选的 NAS/远程音乐库同步配置。

“清理旧低音质”只删除数据库中已确认属于同一首歌、且音质低于当前版本的旧 WebDAV 文件,不会扫描或删除未知文件。
-
+ +
+
+

AM Downloader

+

登录后管理你的下载任务、积分和同步设置。

+
+ + +
+
+ + +
+ + +
+
+ +
+
+
+
AM Downloader
+
搜索 · 下载 · WebDAV 云端同步
+
+ +
+ +
+ 加载中… + +
+ +
+ +
+
+

找到你想听的音乐

+

支持多源音乐检索、无损解析,一键加入下载队列并自动同步。

+ +
+
+
+
+
最近下载
+
最近提交的音频下载任务历史
+
+ +
+
+
+
+ + + + + +
+
+
+
+
下载队列
+
任务会自动处理;本地文件是否保留由“设置 → 下载 → 本地文件策略”决定。
+
+
+
+ + + + +
+
+
+
+ + +
+
+
+

账户与积分

+

积分用于新的实际下载;失败会自动全额退回流水。

+
加载中…
+
+ + + +
+
+ +
+

我的 WebDAV

+

高级用户与管理员专属。文件下载后将自动同步至你的私有 WebDAV。

+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+
Admin Center
+

管理中心

+

系统用户、积分账本、卡密批次与全局审计监控。

+
+
+ +
+ + + + +
+ + + + + + + + + + + + + + + +
+
+ + +
+
+
+

音乐解析 API

+

用于上游音乐搜索和音源解析(独立微服务)。

+
+ + +
+
架构说明:Downloader Web 运行于端口 9540,解析 API 默认运行于端口 9530。
+
+ +
+

下载设置

+

文件保存路径、音质默认偏好与转存策略。

+
+ + +
+
+ + +
仅在 WebDAV 校验上传完整后安全删除;未开启 WebDAV 或上传失败绝不删除。
+
+
+ + +
+
+ +
+

系统全局 WebDAV

+

全局云盘存储同步配置(未配置私有 WebDAV 时兜底使用)。

+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + + +
+ + + + + \ No newline at end of file