/* eslint-disable @typescript-eslint/no-explicit-any */ import type { Metadata, Viewport } from 'next'; import { Inter } from 'next/font/google'; import './globals.css'; import { getConfig } from '@/lib/config'; import { GlobalErrorIndicator } from '../components/GlobalErrorIndicator'; import { SiteProvider } from '../components/SiteProvider'; import { ThemeProvider } from '../components/ThemeProvider'; import { WatchRoomProvider } from '../components/WatchRoomProvider'; import ChatFloatingWindow from '../components/watch-room/ChatFloatingWindow'; import { DownloadProvider } from '../contexts/DownloadContext'; import { DownloadBubble } from '../components/DownloadBubble'; import { DownloadPanel } from '../components/DownloadPanel'; import { DanmakuCacheCleanup } from '../components/DanmakuCacheCleanup'; import TopProgressBar from '../components/TopProgressBar'; const inter = Inter({ subsets: ['latin'] }); export const dynamic = 'force-dynamic'; // 动态生成 metadata,支持配置更新后的标题变化 export async function generateMetadata(): Promise { const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage'; const config = await getConfig(); let siteName = process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTVPlus'; if (storageType !== 'localstorage') { siteName = config.SiteConfig.SiteName; } return { title: siteName, description: '影视聚合', manifest: '/manifest.json', }; } export const viewport: Viewport = { viewportFit: 'cover', }; export default async function RootLayout({ children, }: { children: React.ReactNode; }) { const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage'; let siteName = process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTVPlus'; let announcement = process.env.ANNOUNCEMENT || '本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。'; let doubanProxyType = process.env.NEXT_PUBLIC_DOUBAN_PROXY_TYPE || 'cmliussss-cdn-tencent'; let doubanProxy = process.env.NEXT_PUBLIC_DOUBAN_PROXY || ''; let doubanImageProxyType = process.env.NEXT_PUBLIC_DOUBAN_IMAGE_PROXY_TYPE || 'cmliussss-cdn-tencent'; let doubanImageProxy = process.env.NEXT_PUBLIC_DOUBAN_IMAGE_PROXY || ''; let disableYellowFilter = process.env.NEXT_PUBLIC_DISABLE_YELLOW_FILTER === 'true'; let fluidSearch = process.env.NEXT_PUBLIC_FLUID_SEARCH !== 'false'; let enableComments = false; let recommendationDataSource = 'Mixed'; let tmdbApiKey = ''; let openListEnabled = false; let embyEnabled = false; let loginBackgroundImage = ''; let registerBackgroundImage = ''; let enableRegistration = false; let loginRequireTurnstile = false; let registrationRequireTurnstile = false; let turnstileSiteKey = ''; let enableOIDCLogin = false; let enableOIDCRegistration = false; let oidcButtonText = ''; let aiEnabled = false; let aiEnableHomepageEntry = false; let aiEnableVideoCardEntry = false; let aiEnablePlayPageEntry = false; let customCategories = [] as { name: string; type: 'movie' | 'tv'; query: string; }[]; if (storageType !== 'localstorage') { const config = await getConfig(); siteName = config.SiteConfig.SiteName; announcement = config.SiteConfig.Announcement; doubanProxyType = config.SiteConfig.DoubanProxyType; doubanProxy = config.SiteConfig.DoubanProxy; doubanImageProxyType = config.SiteConfig.DoubanImageProxyType; doubanImageProxy = config.SiteConfig.DoubanImageProxy; disableYellowFilter = config.SiteConfig.DisableYellowFilter; customCategories = config.CustomCategories.filter( (category) => !category.disabled ).map((category) => ({ name: category.name || '', type: category.type, query: category.query, })); fluidSearch = config.SiteConfig.FluidSearch; enableComments = config.SiteConfig.EnableComments; recommendationDataSource = config.SiteConfig.RecommendationDataSource || 'Mixed'; tmdbApiKey = config.SiteConfig.TMDBApiKey || ''; loginBackgroundImage = config.ThemeConfig?.loginBackgroundImage || ''; registerBackgroundImage = config.ThemeConfig?.registerBackgroundImage || ''; enableRegistration = config.SiteConfig.EnableRegistration || false; loginRequireTurnstile = config.SiteConfig.LoginRequireTurnstile || false; registrationRequireTurnstile = config.SiteConfig.RegistrationRequireTurnstile || false; turnstileSiteKey = config.SiteConfig.TurnstileSiteKey || ''; enableOIDCLogin = config.SiteConfig.EnableOIDCLogin || false; enableOIDCRegistration = config.SiteConfig.EnableOIDCRegistration || false; oidcButtonText = config.SiteConfig.OIDCButtonText || ''; // AI配置 aiEnabled = config.AIConfig?.Enabled || false; aiEnableHomepageEntry = config.AIConfig?.EnableHomepageEntry || false; aiEnableVideoCardEntry = config.AIConfig?.EnableVideoCardEntry || false; aiEnablePlayPageEntry = config.AIConfig?.EnablePlayPageEntry || false; // 检查是否启用了 OpenList 功能 openListEnabled = !!( config.OpenListConfig?.Enabled && config.OpenListConfig?.URL && config.OpenListConfig?.Username && config.OpenListConfig?.Password ); // 检查是否启用了 Emby 功能 embyEnabled = !!( config.EmbyConfig?.Enabled && config.EmbyConfig?.ServerURL && (config.EmbyConfig?.ApiKey || (config.EmbyConfig?.Username && config.EmbyConfig?.Password)) ); } // 将运行时配置注入到全局 window 对象,供客户端在运行时读取 const runtimeConfig = { STORAGE_TYPE: process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage', DOUBAN_PROXY_TYPE: doubanProxyType, DOUBAN_PROXY: doubanProxy, DOUBAN_IMAGE_PROXY_TYPE: doubanImageProxyType, DOUBAN_IMAGE_PROXY: doubanImageProxy, DISABLE_YELLOW_FILTER: disableYellowFilter, CUSTOM_CATEGORIES: customCategories, FLUID_SEARCH: fluidSearch, EnableComments: enableComments, RecommendationDataSource: recommendationDataSource, ENABLE_TVBOX_SUBSCRIBE: process.env.ENABLE_TVBOX_SUBSCRIBE === 'true', ENABLE_OFFLINE_DOWNLOAD: process.env.NEXT_PUBLIC_ENABLE_OFFLINE_DOWNLOAD === 'true', VOICE_CHAT_STRATEGY: process.env.NEXT_PUBLIC_VOICE_CHAT_STRATEGY || 'webrtc-fallback', OPENLIST_ENABLED: openListEnabled, EMBY_ENABLED: embyEnabled, PRIVATE_LIBRARY_ENABLED: openListEnabled || embyEnabled, LOGIN_BACKGROUND_IMAGE: loginBackgroundImage, REGISTER_BACKGROUND_IMAGE: registerBackgroundImage, ENABLE_REGISTRATION: enableRegistration, LOGIN_REQUIRE_TURNSTILE: loginRequireTurnstile, REGISTRATION_REQUIRE_TURNSTILE: registrationRequireTurnstile, TURNSTILE_SITE_KEY: turnstileSiteKey, ENABLE_OIDC_LOGIN: enableOIDCLogin, ENABLE_OIDC_REGISTRATION: enableOIDCRegistration, OIDC_BUTTON_TEXT: oidcButtonText, AI_ENABLED: aiEnabled, AI_ENABLE_HOMEPAGE_ENTRY: aiEnableHomepageEntry, AI_ENABLE_VIDEOCARD_ENTRY: aiEnableVideoCardEntry, AI_ENABLE_PLAYPAGE_ENTRY: aiEnablePlayPageEntry, ENABLE_SOURCE_SEARCH: process.env.NEXT_PUBLIC_ENABLE_SOURCE_SEARCH !== 'false', }; return ( {/* 主题CSS */} {/* 将配置序列化后直接写入脚本,浏览器端可通过 window.RUNTIME_CONFIG 获取 */} {/* eslint-disable-next-line @next/next/no-sync-scripts */}