mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
gate VideoTogether behind settings
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { settingsStore } from '@/lib/store/settings-store';
|
||||
|
||||
const HIDE_STYLE_ID = 'kvideo-videotogether-visibility';
|
||||
const SCRIPT_ID = 'kvideo-videotogether-script';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
VideoTogetherLoading?: boolean;
|
||||
VideoTogetherSettingEnabled?: boolean;
|
||||
videoTogetherWebsiteSettingUrl?: string;
|
||||
videoTogetherExtension?: unknown;
|
||||
videoTogetherFlyPannel?: {
|
||||
Minimize?: (isDefault?: boolean) => void;
|
||||
} | null;
|
||||
}
|
||||
}
|
||||
|
||||
interface VideoTogetherControllerProps {
|
||||
envEnabled: boolean;
|
||||
scriptUrl: string;
|
||||
settingUrl?: string;
|
||||
}
|
||||
|
||||
function isSupportedRoute(pathname: string | null): boolean {
|
||||
return pathname?.startsWith('/player') === true || pathname?.startsWith('/iptv') === true;
|
||||
}
|
||||
|
||||
function syncMinimizedDefaults(forceCurrentPageMinimized: boolean) {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem('EnableMiniBar', JSON.stringify(true));
|
||||
localStorage.setItem('MinimiseDefault', JSON.stringify(true));
|
||||
|
||||
if (forceCurrentPageMinimized) {
|
||||
localStorage.setItem('VideoTogetherMinimizedHere', '1');
|
||||
window.videoTogetherFlyPannel?.Minimize?.(true);
|
||||
}
|
||||
} catch {
|
||||
// Ignore storage failures so player pages continue working.
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisibility(hidden: boolean) {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
let style = document.getElementById(HIDE_STYLE_ID) as HTMLStyleElement | null;
|
||||
if (!style) {
|
||||
style = document.createElement('style');
|
||||
style.id = HIDE_STYLE_ID;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
style.textContent = hidden
|
||||
? `
|
||||
#VideoTogetherWrapper,
|
||||
#VideoTogetherfullscreenSWrapper,
|
||||
#videoTogetherLoading,
|
||||
#videoTogetherTxtMsgTouch {
|
||||
display: none !important;
|
||||
}
|
||||
`
|
||||
: `
|
||||
#videoTogetherLoading {
|
||||
display: none !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export function VideoTogetherController({
|
||||
envEnabled,
|
||||
scriptUrl,
|
||||
settingUrl,
|
||||
}: VideoTogetherControllerProps) {
|
||||
const pathname = usePathname();
|
||||
const [videoTogetherEnabled, setVideoTogetherEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const sync = () => {
|
||||
setVideoTogetherEnabled(settingsStore.getSettings().videoTogetherEnabled);
|
||||
};
|
||||
|
||||
sync();
|
||||
return settingsStore.subscribe(sync);
|
||||
}, []);
|
||||
|
||||
const supportedRoute = isSupportedRoute(pathname);
|
||||
const shouldActivate = envEnabled && videoTogetherEnabled && supportedRoute;
|
||||
|
||||
useEffect(() => {
|
||||
if (!envEnabled || !videoTogetherEnabled) {
|
||||
updateVisibility(true);
|
||||
return;
|
||||
}
|
||||
|
||||
syncMinimizedDefaults(supportedRoute);
|
||||
}, [envEnabled, videoTogetherEnabled, supportedRoute]);
|
||||
|
||||
useEffect(() => {
|
||||
updateVisibility(!shouldActivate);
|
||||
}, [shouldActivate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldActivate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settingUrl) {
|
||||
window.videoTogetherWebsiteSettingUrl = settingUrl;
|
||||
}
|
||||
|
||||
if (
|
||||
document.getElementById(SCRIPT_ID) ||
|
||||
document.getElementById('videotogether-script') ||
|
||||
window.VideoTogetherLoading ||
|
||||
window.videoTogetherExtension ||
|
||||
window.videoTogetherFlyPannel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.id = SCRIPT_ID;
|
||||
script.src = scriptUrl;
|
||||
script.async = true;
|
||||
|
||||
document.body.appendChild(script);
|
||||
}, [scriptUrl, settingUrl, shouldActivate]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import Script from 'next/script';
|
||||
|
||||
const DEFAULT_VIDEOTOGETHER_SCRIPT_URL =
|
||||
'https://fastly.jsdelivr.net/gh/VideoTogether/VideoTogether@latest/release/extension.website.user.js';
|
||||
|
||||
export function VideoTogetherScript() {
|
||||
if (process.env.VIDEOTOGETHER_ENABLED === 'false') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scriptUrl = process.env.VIDEOTOGETHER_SCRIPT_URL?.trim() || DEFAULT_VIDEOTOGETHER_SCRIPT_URL;
|
||||
const settingUrl = process.env.VIDEOTOGETHER_SETTING_URL?.trim();
|
||||
|
||||
return (
|
||||
<>
|
||||
{settingUrl ? (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.videoTogetherWebsiteSettingUrl = ${JSON.stringify(settingUrl)};`,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Script
|
||||
id="videotogether-script"
|
||||
src={scriptUrl}
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { Icons } from '@/components/ui/Icon';
|
||||
import { useRuntimeFeatures } from '@/components/RuntimeFeaturesProvider';
|
||||
import { Switch } from '@/components/ui/Switch';
|
||||
import {
|
||||
type ProxyMode,
|
||||
DEFAULT_SEEK_STEP_SECONDS,
|
||||
@@ -21,6 +22,8 @@ interface PlayerSettingsProps {
|
||||
onProxyModeChange: (mode: ProxyMode) => void;
|
||||
seekStepSeconds: number;
|
||||
onSeekStepSecondsChange: (value: number) => void;
|
||||
videoTogetherEnabled?: boolean;
|
||||
onVideoTogetherEnabledChange?: (enabled: boolean) => void;
|
||||
danmakuApiUrl: string;
|
||||
onDanmakuApiUrlChange: (url: string) => void;
|
||||
danmakuOpacity: number;
|
||||
@@ -48,6 +51,8 @@ export function PlayerSettings({
|
||||
onProxyModeChange,
|
||||
seekStepSeconds,
|
||||
onSeekStepSecondsChange,
|
||||
videoTogetherEnabled,
|
||||
onVideoTogetherEnabledChange,
|
||||
danmakuApiUrl,
|
||||
onDanmakuApiUrlChange,
|
||||
danmakuOpacity,
|
||||
@@ -209,6 +214,31 @@ export function PlayerSettings({
|
||||
|
||||
<div className="border-t border-[var(--glass-border)]" />
|
||||
|
||||
{typeof videoTogetherEnabled === 'boolean' && onVideoTogetherEnabledChange ? (
|
||||
<>
|
||||
<div>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h3 className="font-medium text-[var(--text-color)] mb-2 inline-flex items-center gap-2">
|
||||
<Icons.Users size={18} className="text-[var(--accent-color)]" />
|
||||
一起看 (VideoTogether)
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--text-color-secondary)]">
|
||||
关闭后不显示一起看悬浮入口;开启后仅在播放器和 IPTV 页面显示,且默认折叠为小图标,用户可自行展开。
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={videoTogetherEnabled}
|
||||
onChange={onVideoTogetherEnabledChange}
|
||||
ariaLabel="一起看开关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[var(--glass-border)]" />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{/* Danmaku Settings */}
|
||||
<div>
|
||||
<h3 className="font-medium text-[var(--text-color)] mb-2 inline-flex items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user