feat: Background audio via Media Session and Minimal App Icon; Bump v4.0.4

This commit is contained in:
KV-Tube Deployer 2026-02-23 07:02:25 +07:00
parent 657f54855b
commit fd449cce45
7 changed files with 40 additions and 5 deletions

View file

@ -10,6 +10,7 @@ A modern, fast, and fully-featured YouTube-like video streaming platform. Built
- **Watch History & Suggestions**: Keep track of what you've watched seamlessly! Fully integrated library history tracking. - **Watch History & Suggestions**: Keep track of what you've watched seamlessly! Fully integrated library history tracking.
- **Subscriptions Management**: Keep up to date with seamless subscription updates for YouTube channels. - **Subscriptions Management**: Keep up to date with seamless subscription updates for YouTube channels.
- **Optimized for Safari**: Stutter-free playback algorithms and high-tolerance Hls.js configurations tailored for macOS users. - **Optimized for Safari**: Stutter-free playback algorithms and high-tolerance Hls.js configurations tailored for macOS users.
- **Background Audio**: Allows videos to continue playing audio when the browser tab is hidden or device locked (perfect for music).
- **Progressive Web App**: Fully installable PWA out of the box with offline fallbacks and custom vector iconography. - **Progressive Web App**: Fully installable PWA out of the box with offline fallbacks and custom vector iconography.
- **Region Selection**: Tailor your content to specific regions (e.g., Vietnam). - **Region Selection**: Tailor your content to specific regions (e.g., Vietnam).
- **Responsive Design**: Beautiful, mobile-friendly interface with light and dark theme support. - **Responsive Design**: Beautiful, mobile-friendly interface with light and dark theme support.
@ -37,7 +38,7 @@ version: '3.8'
services: services:
kv-tube-app: kv-tube-app:
image: git.khoavo.myds.me/vndangkhoa/kv-tube-app:v4.0.3 image: git.khoavo.myds.me/vndangkhoa/kv-tube-app:v4.0.4
container_name: kv-tube-app container_name: kv-tube-app
restart: unless-stopped restart: unless-stopped
ports: ports:

View file

@ -5,7 +5,7 @@ version: '3.8'
services: services:
kv-tube-app: kv-tube-app:
image: git.khoavo.myds.me/vndangkhoa/kv-tube-app:v4.0.3 image: git.khoavo.myds.me/vndangkhoa/kv-tube-app:v4.0.4
container_name: kv-tube-app container_name: kv-tube-app
platform: linux/amd64 platform: linux/amd64
restart: unless-stopped restart: unless-stopped

View file

@ -97,13 +97,18 @@ export default function VideoPlayer({ videoId, title }: VideoPlayerProps) {
const audio = audioRef.current; const audio = audioRef.current;
if (!video || !audio || !hasSeparateAudio) return; if (!video || !audio || !hasSeparateAudio) return;
// Relax the tolerance to 0.4s to prevent choppy audio resetting on Safari const isHidden = document.visibilityState === 'hidden';
if (Math.abs(video.currentTime - audio.currentTime) > 0.4) { if (Math.abs(video.currentTime - audio.currentTime) > 0.4) {
audio.currentTime = video.currentTime; if (!isHidden || !video.paused) {
audio.currentTime = video.currentTime;
}
} }
if (video.paused && !audio.paused) { if (video.paused && !audio.paused) {
audio.pause(); if (!isHidden) {
audio.pause();
}
} else if (!video.paused && audio.paused) { } else if (!video.paused && audio.paused) {
audio.play().catch(() => { }); audio.play().catch(() => { });
} }
@ -189,10 +194,21 @@ export default function VideoPlayer({ videoId, title }: VideoPlayerProps) {
video.addEventListener(event, handler); video.addEventListener(event, handler);
}); });
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible' && video && audioRef.current && hasSeparateAudio) {
if (video.paused && !audioRef.current.paused) {
video.currentTime = audioRef.current.currentTime;
video.play().catch(() => { });
}
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => { return () => {
Object.entries(handlers).forEach(([event, handler]) => { Object.entries(handlers).forEach(([event, handler]) => {
video.removeEventListener(event, handler); video.removeEventListener(event, handler);
}); });
document.removeEventListener('visibilitychange', handleVisibilityChange);
}; };
}, [hasSeparateAudio]); }, [hasSeparateAudio]);
@ -210,6 +226,24 @@ export default function VideoPlayer({ videoId, title }: VideoPlayerProps) {
const handleWaiting = () => setIsBuffering(true); const handleWaiting = () => setIsBuffering(true);
const handleLoadStart = () => setIsLoading(true); const handleLoadStart = () => setIsLoading(true);
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: title || 'KV-Tube Video',
artist: 'KV-Tube',
artwork: [
{ src: `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`, sizes: '480x360', type: 'image/jpeg' }
]
});
navigator.mediaSession.setActionHandler('play', () => {
video.play().catch(() => { });
if (needsSeparateAudio && audioRef.current) audioRef.current.play().catch(() => { });
});
navigator.mediaSession.setActionHandler('pause', () => {
video.pause();
if (needsSeparateAudio && audioRef.current) audioRef.current.pause();
});
}
video.addEventListener('canplay', handleCanPlay); video.addEventListener('canplay', handleCanPlay);
video.addEventListener('playing', handlePlaying); video.addEventListener('playing', handlePlaying);
video.addEventListener('waiting', handleWaiting); video.addEventListener('waiting', handleWaiting);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 14 KiB