Update MediaStationGo branding and deployment docs
@@ -5,7 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#faf8f5" />
|
||||
<meta name="description" content="MediaStationGo — 个人家庭媒体中心" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" type="image/png" href="/brand/mgo-emby-icon.png" />
|
||||
<link rel="apple-touch-icon" href="/brand/mgo-emby-icon.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg width="920" height="160" viewBox="0 0 460 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Logo Mark -->
|
||||
<rect x="10" y="15" width="50" height="50" rx="14" fill="url(#horizonPurpleGrad)" />
|
||||
<path d="M26 30 L43 40 L26 50" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M26 43 L26 54 L34 48" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="46" cy="28" r="3.5" fill="#06B6D4" />
|
||||
|
||||
<!-- Typography -->
|
||||
<text x="75" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="28" fill="#111827" letter-spacing="-0.5">Mgo-Emby</text>
|
||||
<text x="238" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="300" font-size="28" fill="#9CA3AF" letter-spacing="-0.5"> 社区</text>
|
||||
<rect x="315" y="26" width="48" height="22" rx="6" fill="#06B6D4" fill-opacity="0.1" stroke="#06B6D4" stroke-width="1" />
|
||||
<text x="339" y="41" font-family="'JetBrains Mono', monospace" font-weight="700" font-size="11" fill="#06B6D4" text-anchor="middle">BBS</text>
|
||||
|
||||
<defs>
|
||||
<linearGradient id="horizonPurpleGrad" x1="10" y1="15" x2="60" y2="65" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7C3AED" />
|
||||
<stop offset="1" stop-color="#5B21B6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg width="64" height="64" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="8" fill="url(#mgoPurpleGrad)" />
|
||||
<path d="M11 10 L21 16 L11 22" stroke="white" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M11 18 L11 24 L16 21" stroke="white" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="23" cy="9" r="2" fill="#06B6D4" />
|
||||
<defs>
|
||||
<linearGradient id="mgoPurpleGrad" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7C3AED" />
|
||||
<stop offset="1" stop-color="#5B21B6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 705 B |
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="200" height="200" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="5" y="5" width="90" height="90" rx="24" stroke="currentColor" stroke-width="6" />
|
||||
<path d="M35 30 L65 47 L35 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M35 51 L35 72 L48 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="69" cy="29" r="6" fill="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 525 B |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,16 @@
|
||||
<svg width="400" height="480" viewBox="0 0 200 240" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="40" y="20" width="120" height="120" rx="32" fill="url(#primaryPurpleGrad)" />
|
||||
<path d="M80 55 L120 80 L80 105" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M80 87 L80 114 L100 101" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="128" cy="52" r="8" fill="#06B6D4" />
|
||||
|
||||
<text x="100" y="185" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="24" fill="#111827" text-anchor="middle" letter-spacing="1">Mgo-Emby</text>
|
||||
<text x="100" y="215" font-family="'Inter', system-ui, sans-serif" font-weight="500" font-size="14" fill="#06B6D4" text-anchor="middle" letter-spacing="4">社区</text>
|
||||
|
||||
<defs>
|
||||
<linearGradient id="primaryPurpleGrad" x1="40" y1="20" x2="160" y2="140" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7C3AED" />
|
||||
<stop offset="1" stop-color="#5B21B6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg width="460" height="80" viewBox="0 0 460 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Logo Mark -->
|
||||
<rect x="10" y="15" width="50" height="50" rx="14" fill="url(#horizonPurpleGrad)" />
|
||||
<path d="M26 30 L43 40 L26 50" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M26 43 L26 54 L34 48" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="46" cy="28" r="3.5" fill="#06B6D4" />
|
||||
|
||||
<!-- Typography -->
|
||||
<text x="75" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="28" fill="#111827" letter-spacing="-0.5">Mgo-Emby</text>
|
||||
<text x="238" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="300" font-size="28" fill="#9CA3AF" letter-spacing="-0.5"> 社区</text>
|
||||
<rect x="315" y="26" width="48" height="22" rx="6" fill="#06B6D4" fill-opacity="0.1" stroke="#06B6D4" stroke-width="1" />
|
||||
<text x="339" y="41" font-family="'JetBrains Mono', monospace" font-weight="700" font-size="11" fill="#06B6D4" text-anchor="middle">BBS</text>
|
||||
|
||||
<defs>
|
||||
<linearGradient id="horizonPurpleGrad" x1="10" y1="15" x2="60" y2="65" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7C3AED" />
|
||||
<stop offset="1" stop-color="#5B21B6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 981 B |
@@ -0,0 +1,12 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="8" fill="url(#mgoPurpleGrad)" />
|
||||
<path d="M11 10 L21 16 L11 22" stroke="white" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M11 18 L11 24 L16 21" stroke="white" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="23" cy="9" r="2" fill="#06B6D4" />
|
||||
<defs>
|
||||
<linearGradient id="mgoPurpleGrad" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7C3AED" />
|
||||
<stop offset="1" stop-color="#5B21B6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 705 B |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="5" y="5" width="90" height="90" rx="24" stroke="currentColor" stroke-width="6" />
|
||||
<path d="M35 30 L65 47 L35 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M35 51 L35 72 L48 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="69" cy="29" r="6" fill="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 525 B |
|
After Width: | Height: | Size: 6.6 KiB |
@@ -0,0 +1,16 @@
|
||||
<svg width="200" height="240" viewBox="0 0 200 240" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="40" y="20" width="120" height="120" rx="32" fill="url(#primaryPurpleGrad)" />
|
||||
<path d="M80 55 L120 80 L80 105" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<path d="M80 87 L80 114 L100 101" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
||||
<circle cx="128" cy="52" r="8" fill="#06B6D4" />
|
||||
|
||||
<text x="100" y="185" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="24" fill="#111827" text-anchor="middle" letter-spacing="1">Mgo-Emby</text>
|
||||
<text x="100" y="215" font-family="'Inter', system-ui, sans-serif" font-weight="500" font-size="14" fill="#06B6D4" text-anchor="middle" letter-spacing="4">社区</text>
|
||||
|
||||
<defs>
|
||||
<linearGradient id="primaryPurpleGrad" x1="40" y1="20" x2="160" y2="140" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7C3AED" />
|
||||
<stop offset="1" stop-color="#5B21B6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,17 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="MediaStationGo">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="10" y1="6" x2="54" y2="58" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#7c3aed"/>
|
||||
<stop offset="0.48" stop-color="#2563eb"/>
|
||||
<stop offset="1" stop-color="#06b6d4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="play" x1="23" y1="18" x2="45" y2="46" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#fff7ed"/>
|
||||
<stop offset="1" stop-color="#ffffff"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="64" height="64" rx="18" fill="url(#bg)"/>
|
||||
<path d="M18 21.5A7.5 7.5 0 0 1 25.5 14h13A7.5 7.5 0 0 1 46 21.5v21A7.5 7.5 0 0 1 38.5 50h-13A7.5 7.5 0 0 1 18 42.5v-21Z" fill="#0f172a" opacity=".22"/>
|
||||
<path d="M22 22.9c0-3.05 3.32-4.95 5.96-3.42l17.08 9.86c2.64 1.52 2.64 5.34 0 6.86L27.96 46.06C25.32 47.58 22 45.68 22 42.63V22.9Z" fill="url(#play)"/>
|
||||
<path d="M13 18h6M13 28h6M13 38h6M45 18h6M45 28h6M45 38h6" stroke="#ffffff" stroke-width="3" stroke-linecap="round" opacity=".72"/>
|
||||
<image href="/brand/mgo-emby-icon.png" width="64" height="64" preserveAspectRatio="xMidYMid meet"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 209 B |
@@ -113,7 +113,7 @@ class AppErrorBoundary extends Component<{ children: ReactNode }, { hasError: bo
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
console.error('MediaStation UI crashed', error, info)
|
||||
console.error('MediaStationGo UI crashed', error, info)
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { api } from './client'
|
||||
|
||||
// ── License Server types (generic, ready for MediaStationLicenseServer integration) ──
|
||||
// ── License Server types (generic, ready for MediaStationGo license integration) ──
|
||||
|
||||
/** Response after binding/activating a license. */
|
||||
export interface LicenseActivation {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Link, NavLink, Outlet, useLocation, useNavigate } from 'react-router-do
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import toast from 'react-hot-toast'
|
||||
import {
|
||||
Activity, Bell, Clock, CloudDownload, Compass, Film,
|
||||
Activity, Bell, Clock, CloudDownload, Compass,
|
||||
Cast, Globe, HardDrive, Heart, Home, Image, KeySquare,
|
||||
ListMusic, LogOut, Rss, Search, Trash2,
|
||||
Settings, Sliders, Sparkles, UserCog,
|
||||
@@ -139,16 +139,18 @@ export function Layout() {
|
||||
{/* Brand Logo & Brand Title */}
|
||||
<div className="flex h-20 items-center justify-between px-6 border-b border-gray-100">
|
||||
<Link to="/" className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-[#111827] to-[#1f2937] shadow-sm">
|
||||
<Film className="h-5 w-5 text-[#c9954a]" />
|
||||
</div>
|
||||
<img
|
||||
src="/brand/mgo-emby-icon.png"
|
||||
alt="MediaStationGo"
|
||||
className="h-10 w-10 shrink-0 rounded-xl object-contain shadow-sm"
|
||||
/>
|
||||
{(isSidebarOpen || isMobileDrawerOpen) && (
|
||||
<motion.span
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="font-display text-lg font-extrabold tracking-tight text-[#111827]"
|
||||
>
|
||||
MediaStation
|
||||
MediaStationGo
|
||||
</motion.span>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { motion } from 'framer-motion'
|
||||
import toast from 'react-hot-toast'
|
||||
import { Eye, EyeOff, Film, ArrowRight, ShieldCheck, Lock, User } from 'lucide-react'
|
||||
import { Eye, EyeOff, ArrowRight, ShieldCheck, Lock, User } from 'lucide-react'
|
||||
import { AppFooter } from '../components/AppFooter'
|
||||
import { authAPI } from '../api/auth'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
@@ -50,14 +50,14 @@ export function LoginPage() {
|
||||
|
||||
{/* Logo & Headline */}
|
||||
<div className="flex flex-col items-center text-center pb-8">
|
||||
<motion.div
|
||||
<motion.img
|
||||
initial={{ scale: 0.8, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ delay: 0.15, type: 'spring', stiffness: 200 }}
|
||||
className="mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-[#111827] shadow-sm"
|
||||
>
|
||||
<Film className="h-7 w-7 text-[#c9954a]" />
|
||||
</motion.div>
|
||||
src="/brand/mgo-emby-icon.png"
|
||||
alt="MediaStationGo"
|
||||
className="mb-4 h-14 w-14 rounded-2xl object-contain shadow-sm"
|
||||
/>
|
||||
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
@@ -65,7 +65,7 @@ export function LoginPage() {
|
||||
transition={{ delay: 0.25 }}
|
||||
className="font-display text-2xl font-extrabold tracking-tight text-gray-900"
|
||||
>
|
||||
MediaStation
|
||||
MediaStationGo
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
@@ -195,4 +195,4 @@ export function LoginPage() {
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,14 @@ const TYPE_LABELS: Record<NotifyChannel['type'], string> = {
|
||||
email: 'Email',
|
||||
}
|
||||
|
||||
const EVENT_OPTIONS = [
|
||||
{ value: 'subscription_hit', label: '订阅命中新资源' },
|
||||
{ value: 'download_complete', label: '下载任务完成' },
|
||||
{ value: 'library_ingest', label: '入库完成' },
|
||||
{ value: 'scrape_failed', label: '刮削失败告警' },
|
||||
{ value: 'system_alert', label: '系统异常通知' },
|
||||
]
|
||||
|
||||
function ChannelCard({
|
||||
channel,
|
||||
onTest,
|
||||
@@ -154,6 +162,7 @@ function ChannelCard({
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 truncate text-xs text-ink-50">{summary}</div>
|
||||
<div className="mt-1 truncate text-xs text-sand-500">{eventSummary(channel.events)}</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 gap-2">
|
||||
<button
|
||||
@@ -179,6 +188,12 @@ function ChannelCard({
|
||||
)
|
||||
}
|
||||
|
||||
function eventSummary(events: string[] | undefined): string {
|
||||
if (!events || events.length === 0) return '事件:全部'
|
||||
const labels = events.map((event) => EVENT_OPTIONS.find((item) => item.value === event)?.label ?? event)
|
||||
return `事件:${labels.join('、')}`
|
||||
}
|
||||
|
||||
function channelSummary(ch: NotifyChannel): string {
|
||||
const cfg = ch.config ?? {}
|
||||
switch (ch.type) {
|
||||
@@ -245,6 +260,7 @@ function ChannelFormModal({
|
||||
const [config, setConfig] = useState<Record<string, string>>(
|
||||
normalizeInitialConfig(editing?.type ?? 'telegram', editing?.config ?? {}),
|
||||
)
|
||||
const [events, setEvents] = useState<string[]>(editing?.events ?? [])
|
||||
const [enabled, setEnabled] = useState(editing?.enabled ?? true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
@@ -275,6 +291,7 @@ function ChannelFormModal({
|
||||
name: name.trim(),
|
||||
type: type,
|
||||
config: cleanedConfig,
|
||||
events,
|
||||
enabled,
|
||||
}
|
||||
if (editing) {
|
||||
@@ -296,6 +313,12 @@ function ChannelFormModal({
|
||||
const updateConfig = (k: string, v: string) =>
|
||||
setConfig((c) => ({ ...c, [k]: v }))
|
||||
|
||||
const toggleEvent = (event: string) => {
|
||||
setEvents((current) =>
|
||||
current.includes(event) ? current.filter((item) => item !== event) : [...current, event],
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm">
|
||||
<div className="glass-panel w-full max-w-lg max-h-[90vh] overflow-y-auto">
|
||||
@@ -532,6 +555,33 @@ function ChannelFormModal({
|
||||
</>
|
||||
)}
|
||||
|
||||
<Field label="推送事件">
|
||||
<div className="space-y-2 rounded-lg border border-gray-200 bg-gray-50/60 p-3">
|
||||
<label className="flex cursor-pointer items-center gap-2 text-sm text-ink-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-4 w-4 accent-primary-400"
|
||||
checked={events.length === 0}
|
||||
onChange={() => setEvents([])}
|
||||
/>
|
||||
全部事件
|
||||
</label>
|
||||
<div className="grid gap-2 sm:grid-cols-2">
|
||||
{EVENT_OPTIONS.map((event) => (
|
||||
<label key={event.value} className="flex cursor-pointer items-center gap-2 text-sm text-ink-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-4 w-4 accent-primary-400"
|
||||
checked={events.includes(event.value)}
|
||||
onChange={() => toggleEvent(event.value)}
|
||||
/>
|
||||
{event.label}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
<label className="flex cursor-pointer items-center gap-2 text-sm text-ink-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
@@ -140,7 +140,7 @@ const GROUPS: SettingGroup[] = [
|
||||
{
|
||||
key: 'license',
|
||||
label: '授权服务',
|
||||
description: '连接私有 MediaStationLicenseServer;开源版默认最多 20 个用户,激活后按授权策略提升额度。',
|
||||
description: '连接私有 MediaStationGo 授权服务;开源版默认最多 20 个用户,激活后按授权策略提升额度。',
|
||||
items: [
|
||||
{
|
||||
key: 'license.server_url',
|
||||
|
||||
@@ -312,7 +312,7 @@ export interface NotifyChannel {
|
||||
name: string
|
||||
type: 'telegram' | 'wechat' | 'bark' | 'webhook' | 'email'
|
||||
enabled: boolean
|
||||
events: string
|
||||
events: string[]
|
||||
config: Record<string, string>
|
||||
created_at: string
|
||||
updated_at: string
|
||||
|
||||