Files
Mymusic3/Netease-sync/public/index.html
史悦 50f7869a05 feat: 实现网易云音乐同步服务核心功能与UI改进
添加完整的网易云音乐同步到Navidrome的功能实现,包括:
1. 新增Docker支持与相关配置文件
2. 实现歌单同步逻辑与Navidrome API集成
3. 改进前端UI界面与交互体验
4. 添加状态监控与错误处理机制
5. 实现定时同步功能与进度显示
2026-01-12 20:03:30 +08:00

224 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#000000">
<title>网易云音乐同步</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
netease: '#E60026',
glass: {
100: 'rgba(255, 255, 255, 0.1)',
200: 'rgba(255, 255, 255, 0.2)',
300: 'rgba(255, 255, 255, 0.3)',
}
},
animation: {
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
}
}
}
}
</script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #121212;
background-image:
radial-gradient(at 0% 0%, hsla(253,16%,7%,1) 0, transparent 50%),
radial-gradient(at 50% 0%, hsla(225,39%,30%,1) 0, transparent 50%),
radial-gradient(at 100% 0%, hsla(339,49%,30%,1) 0, transparent 50%);
min-height: 100vh;
}
.glass-panel {
background: rgba(30, 30, 30, 0.6);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
}
.glass-input {
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
color: white;
transition: all 0.3s ease;
}
.glass-input:focus {
background: rgba(0, 0, 0, 0.5);
border-color: rgba(230, 0, 38, 0.5);
outline: none;
box-shadow: 0 0 0 2px rgba(230, 0, 38, 0.2);
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.02);
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.2);
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
animation: spin 1s linear infinite;
}
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>
</head>
<body class="text-white custom-scrollbar selection:bg-netease selection:text-white">
<div class="container mx-auto px-4 py-8 max-w-7xl">
<!-- Header -->
<header class="flex flex-col md:flex-row items-center justify-between mb-10 gap-6">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-full bg-gradient-to-br from-netease to-red-700 flex items-center justify-center shadow-lg shadow-netease/30">
<i class="fa-solid fa-cloud text-xl"></i>
</div>
<div>
<h1 class="text-2xl font-bold tracking-tight">网易云音乐同步</h1>
<p class="text-gray-400 text-sm">Sync Netease Cloud Music to Navidrome</p>
</div>
</div>
<div class="glass-panel rounded-full p-1.5 flex w-full md:w-auto max-w-md">
<input
type="text"
id="playlistInput"
placeholder="输入歌单 ID 或链接..."
class="bg-transparent border-none text-white px-4 py-2 w-full focus:outline-none placeholder-gray-500 text-sm"
>
<button
onclick="addPlaylist()"
class="bg-netease hover:bg-red-700 text-white px-6 py-2 rounded-full font-medium text-sm transition-all shadow-lg shadow-netease/20 flex items-center gap-2 whitespace-nowrap"
>
<i class="fa-solid fa-plus"></i>
<span>添加</span>
</button>
</div>
</header>
<!-- Notifications -->
<div id="toast-container" class="fixed top-6 right-6 z-50 flex flex-col gap-3 pointer-events-none"></div>
<!-- Content -->
<main>
<!-- Stats Overview -->
<div id="stats-container" class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8 hidden">
<div class="glass-panel rounded-2xl p-4 flex flex-col items-center justify-center text-center">
<span class="text-3xl font-bold text-white mb-1" id="stat-total">0</span>
<span class="text-xs text-gray-400 uppercase tracking-wider">总歌单</span>
</div>
<div class="glass-panel rounded-2xl p-4 flex flex-col items-center justify-center text-center">
<span class="text-3xl font-bold text-green-400 mb-1" id="stat-synced">0</span>
<span class="text-xs text-gray-400 uppercase tracking-wider">已同步</span>
</div>
<div class="glass-panel rounded-2xl p-4 flex flex-col items-center justify-center text-center">
<span class="text-3xl font-bold text-blue-400 mb-1" id="stat-songs">0</span>
<span class="text-xs text-gray-400 uppercase tracking-wider">总歌曲</span>
</div>
<div class="glass-panel rounded-2xl p-4 flex flex-col items-center justify-center text-center">
<span class="text-3xl font-bold text-yellow-400 mb-1" id="stat-syncing">0</span>
<span class="text-xs text-gray-400 uppercase tracking-wider">同步中</span>
</div>
</div>
<!-- Empty State -->
<div id="empty-state" class="hidden text-center py-20">
<div class="w-24 h-24 bg-white/5 rounded-full flex items-center justify-center mx-auto mb-6">
<i class="fa-solid fa-music text-4xl text-white/20"></i>
</div>
<h3 class="text-xl font-medium text-white mb-2">还没有歌单</h3>
<p class="text-gray-500 max-w-sm mx-auto">在上方输入框粘贴网易云音乐歌单链接或 ID开始构建你的私人音乐库。</p>
</div>
<!-- Grid Layout -->
<div id="playlist-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<!-- Cards will be injected here -->
</div>
</main>
</div>
<!-- Templates -->
<template id="playlist-card-template">
<div class="glass-panel rounded-2xl overflow-hidden group hover:bg-white/5 transition-all duration-300 relative">
<!-- Cover Image & Gradient Overlay -->
<div class="aspect-square relative overflow-hidden bg-gray-800">
<img src="" alt="Cover" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110 playlist-cover">
<div class="absolute inset-0 bg-gradient-to-t from-black/90 via-black/40 to-transparent opacity-80 group-hover:opacity-60 transition-opacity"></div>
<!-- Status Badge -->
<div class="absolute top-3 right-3">
<span class="playlist-status px-2.5 py-1 rounded-full text-xs font-medium backdrop-blur-md bg-black/40 border border-white/10">
待同步
</span>
</div>
<!-- Action Buttons (Hover) -->
<div class="absolute inset-0 flex items-center justify-center gap-3 opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-black/40 backdrop-blur-sm">
<button class="action-sync w-12 h-12 rounded-full bg-white text-black hover:scale-110 transition-transform flex items-center justify-center shadow-xl">
<i class="fa-solid fa-rotate"></i>
</button>
<button class="action-delete w-12 h-12 rounded-full bg-red-500/80 text-white hover:bg-red-500 hover:scale-110 transition-all flex items-center justify-center shadow-xl backdrop-blur-md">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<!-- Progress Bar Overlay (When Syncing) -->
<div class="sync-overlay absolute inset-x-0 bottom-0 bg-black/80 backdrop-blur-md p-4 transform translate-y-full transition-transform duration-300">
<div class="flex justify-between text-xs text-gray-300 mb-1.5">
<span class="sync-message truncate pr-2">准备中...</span>
<span class="sync-percent font-mono">0%</span>
</div>
<div class="w-full bg-gray-700 rounded-full h-1.5 overflow-hidden">
<div class="sync-progress-bar bg-netease h-1.5 rounded-full transition-all duration-300" style="width: 0%"></div>
</div>
</div>
</div>
<!-- Info Section -->
<div class="p-5">
<h3 class="playlist-name font-bold text-lg mb-1 truncate group-hover:text-netease transition-colors">歌单名称</h3>
<div class="flex items-center gap-2 text-xs text-gray-400 mb-4">
<span class="flex items-center gap-1 bg-white/5 px-2 py-0.5 rounded">
<i class="fa-solid fa-music text-[10px]"></i>
<span class="playlist-count">0</span>
</span>
<span class="playlist-date">刚刚</span>
</div>
<div class="flex items-center justify-between text-xs text-gray-500 border-t border-white/5 pt-3 mt-1">
<span class="font-mono playlist-id opacity-50">ID: 000000</span>
<a href="#" target="_blank" class="playlist-link hover:text-white transition-colors flex items-center gap-1">
打开网易云 <i class="fa-solid fa-arrow-up-right-from-square text-[10px]"></i>
</a>
</div>
</div>
</div>
</template>
<script src="app.js"></script>
</body>
</html>