mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
feat: Introduce 'auto' fullscreen type with device-specific defaults, update player logic and settings, and add dedicated iOS fullscreen documentation.
This commit is contained in:
@@ -9,7 +9,7 @@ import { useStallDetection } from './hooks/useStallDetection';
|
||||
import { DesktopControlsWrapper } from './desktop/DesktopControlsWrapper';
|
||||
import { DesktopOverlayWrapper } from './desktop/DesktopOverlayWrapper';
|
||||
import { usePlayerSettings } from './hooks/usePlayerSettings';
|
||||
import { useIsIOS } from '@/lib/hooks/mobile/useDeviceDetection';
|
||||
import { useIsIOS, useIsMobile } from '@/lib/hooks/mobile/useDeviceDetection';
|
||||
import './web-fullscreen.css';
|
||||
|
||||
interface DesktopVideoPlayerProps {
|
||||
@@ -41,6 +41,7 @@ export function DesktopVideoPlayer({
|
||||
const { refs, data, actions } = useDesktopPlayerState();
|
||||
const { fullscreenType: settingsFullscreenType } = usePlayerSettings();
|
||||
const isIOS = useIsIOS();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
// State to track if device is in landscape mode
|
||||
const [isLandscape, setIsLandscape] = React.useState(true);
|
||||
@@ -62,8 +63,13 @@ export function DesktopVideoPlayer({
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Force windowed fullscreen on iOS to avoid native player hijacking
|
||||
const fullscreenType = isIOS ? 'window' : settingsFullscreenType;
|
||||
// Use user preference for fullscreen type, resolving 'auto' to device default
|
||||
// Auto Rules:
|
||||
// - Mobile: Window Fullscreen (Better for Danmaku/Controls)
|
||||
// - Desktop: Native Fullscreen (Better for PiP/Performance)
|
||||
const fullscreenType = settingsFullscreenType === 'auto'
|
||||
? (isIOS ? 'window' : isMobile ? 'window' : 'native') // Treat all mobile as window for consistency if auto
|
||||
: settingsFullscreenType;
|
||||
|
||||
// Check if we need to force landscape (iOS + Fullscreen + Portrait)
|
||||
const shouldForceLandscape = data.isFullscreen && fullscreenType === 'window' && isIOS && !isLandscape;
|
||||
|
||||
@@ -282,11 +282,15 @@ export function DesktopMoreMenu({
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => {
|
||||
setFullscreenType(fullscreenType === 'native' ? 'window' : 'native');
|
||||
if (fullscreenType === 'auto') setFullscreenType('native');
|
||||
else if (fullscreenType === 'native') setFullscreenType('window');
|
||||
else setFullscreenType('auto');
|
||||
}}
|
||||
className={`flex items-center gap-1 bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] rounded-[var(--radius-2xl)] outline-none hover:border-[var(--accent-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_5%,transparent)] transition-all cursor-pointer whitespace-nowrap ${isRotated ? 'px-1.5 py-0.5 text-[9px]' : 'px-2 sm:px-2.5 py-1 sm:py-1.5 text-[10px] sm:text-xs'}`}
|
||||
>
|
||||
<span>{fullscreenType === 'native' ? '系统全屏' : '网页全屏'}</span>
|
||||
<span>
|
||||
{fullscreenType === 'auto' ? '自动 (Auto)' : fullscreenType === 'native' ? '系统全屏' : '网页全屏'}
|
||||
</span>
|
||||
<Icons.Maximize size={isRotated ? 10 : 12} className="text-[var(--text-color-secondary)]" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -93,7 +93,7 @@ export function usePlayerSettings() {
|
||||
updateSetting('adKeywords', value);
|
||||
}, [updateSetting]);
|
||||
|
||||
const setFullscreenType = useCallback((value: 'native' | 'window') => {
|
||||
const setFullscreenType = useCallback((value: 'auto' | 'native' | 'window') => {
|
||||
updateSetting('fullscreenType', value);
|
||||
}, [updateSetting]);
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# iOS Fullscreen Guide
|
||||
|
||||
## Why can't I switch fullscreen modes?
|
||||
|
||||
Previously, the player forced **Web Fullscreen** (Custom UI) on all iOS devices to provide advanced features like Danmaku and custom controls. This prevented the "System Fullscreen" option in settings from working even if selected.
|
||||
|
||||
**We have now fixed this behavior.** You can now switch between the two modes in the player settings.
|
||||
|
||||
---
|
||||
|
||||
## Fullscreen Modes Explained
|
||||
|
||||
### 1. System Fullscreen (Native)
|
||||
**Default behavior on iOS.** Best for standard playback, AirPlay, Picture-in-Picture.
|
||||
|
||||
- **Pros:**
|
||||
- Uses the native iOS video player.
|
||||
- Smoothest performance and battery life.
|
||||
- Native support for **AirPlay** and **Picture-in-Picture (PiP)**.
|
||||
- Familiar iOS gestures (pinch to zoom, etc.).
|
||||
- **Cons:**
|
||||
- **No Danmaku (Bullet Comments)** in fullscreen.
|
||||
- Custom subtitle styling may be limited.
|
||||
- Default iOS controls instead of KVideo's custom controls.
|
||||
|
||||
### 2. Web Fullscreen (Window/Custom)
|
||||
**Best for:** Danmaku, Advanced Controls, Subtitles.
|
||||
|
||||
- **Pros:**
|
||||
- **Danmaku works in fullscreen!**
|
||||
- Uses KVideo's custom interface and controls.
|
||||
- Better subtitle styling and positioning.
|
||||
- Quick access to playback speed, quality, and episode selection without leaving fullscreen.
|
||||
- **Cons:**
|
||||
- **Address Bar Issue:** On some iPhones, the Safari address bar may not disappear completely in landscape mode, requiring manual hiding (scrolling up) or adding the app to the Home Screen.
|
||||
- **Not true fullscreen:** It's actually a "rotated" web page element that fills the screen (hence why it's sometimes called "Window Fullscreen" or "Fake Fullscreen").
|
||||
- Native gestures like standard PiP might require an extra tap.
|
||||
|
||||
---
|
||||
|
||||
## How to Switch
|
||||
|
||||
1. Open a video in the player.
|
||||
2. Tap the **Settings (Gear Icon)** in the top right corner.
|
||||
3. Tap **Fullscreen Mode** (全屏方式) to cycle through:
|
||||
- **Auto (Default)**:
|
||||
- **Mobile**: Uses Web Fullscreen.
|
||||
- **Desktop**: Uses System Fullscreen.
|
||||
- **System Fullscreen** (系统全屏) - Standard Experience
|
||||
- **Web Fullscreen** (网页全屏) - Enhanced Experience (Danmaku)
|
||||
5. Tap the Fullscreen button on the video player to enter fullscreen.
|
||||
|
||||
> **Note for iOS Users:** If "Web Fullscreen" feels buggy (e.g., orientation issues), try locking your phone's orientation to Portrait before entering fullscreen, or switch to "System Fullscreen" for a more stable experience.
|
||||
@@ -44,7 +44,7 @@ export interface AppSettings {
|
||||
realtimeLatency: boolean; // Enable real-time latency ping updates
|
||||
searchDisplayMode: SearchDisplayMode; // 'normal' = individual cards, 'grouped' = group same-name videos
|
||||
episodeReverseOrder: boolean; // Persist episode list reverse state
|
||||
fullscreenType: 'native' | 'window'; // Fullscreen mode preference
|
||||
fullscreenType: 'auto' | 'native' | 'window'; // Fullscreen mode preference: 'auto' (native on desktop, window on mobile) | 'native' | 'window'
|
||||
proxyMode: ProxyMode; // Proxy behavior: 'retry' | 'none' | 'always'
|
||||
rememberScrollPosition: boolean; // Remember scroll position when navigating back or refreshing
|
||||
}
|
||||
@@ -118,7 +118,7 @@ function getDefaultAppSettings(): AppSettings {
|
||||
realtimeLatency: false,
|
||||
searchDisplayMode: 'normal',
|
||||
episodeReverseOrder: false,
|
||||
fullscreenType: 'native',
|
||||
fullscreenType: 'auto',
|
||||
proxyMode: 'retry',
|
||||
rememberScrollPosition: true,
|
||||
};
|
||||
@@ -196,7 +196,7 @@ export const settingsStore = {
|
||||
realtimeLatency: parsed.realtimeLatency !== undefined ? parsed.realtimeLatency : false,
|
||||
searchDisplayMode: parsed.searchDisplayMode === 'grouped' ? 'grouped' : 'normal',
|
||||
episodeReverseOrder: parsed.episodeReverseOrder !== undefined ? parsed.episodeReverseOrder : false,
|
||||
fullscreenType: parsed.fullscreenType === 'window' ? 'window' : 'native',
|
||||
fullscreenType: (parsed.fullscreenType === 'window' || parsed.fullscreenType === 'native' || parsed.fullscreenType === 'auto') ? parsed.fullscreenType : 'auto',
|
||||
proxyMode: (parsed.proxyMode === 'retry' || parsed.proxyMode === 'none' || parsed.proxyMode === 'always') ? parsed.proxyMode : 'retry',
|
||||
rememberScrollPosition: parsed.rememberScrollPosition !== undefined ? parsed.rememberScrollPosition : true,
|
||||
};
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "kvideo",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kvideo",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kvideo",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
Reference in New Issue
Block a user