This commit is contained in:
hibna
2025-10-29 14:37:33 +03:00
parent 0d3559300a
commit ad0c32785b
2 changed files with 176 additions and 33 deletions
+85 -9
View File
@@ -25,24 +25,100 @@
display: none !important; display: none !important;
} }
/* Subtitle styling */ /* Modern Subtitle Styling */
.video-element::cue { .video-element::cue {
background-color: rgba(0, 0, 0, 0.8); /* Typography */
color: white; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
font-size: 1.2em; font-size: 1.75rem;
font-family: Arial, sans-serif; font-weight: 700;
line-height: 1.4; line-height: 1.4;
padding: 0.2em 0.5em; letter-spacing: 0.5px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
/* Colors - No background, only text with strong shadow */
color: #ffffff;
background-color: transparent;
/* Visual Effects - Strong shadow for readability */
text-shadow:
/* Strong black outline */
-2px -2px 0 #000,
2px -2px 0 #000,
-2px 2px 0 #000,
2px 2px 0 #000,
0 -2px 0 #000,
0 2px 0 #000,
-2px 0 0 #000,
2px 0 0 #000,
/* Additional shadow for depth */
0 4px 8px rgba(0, 0, 0, 0.9),
0 0 12px rgba(0, 0, 0, 0.8);
/* Better rendering */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
} }
/* Ensure text tracks are visible */ /* Fullscreen subtitle adjustments */
:fullscreen .video-element::cue,
.video-element:fullscreen::cue {
font-size: 2.25rem;
}
/* Ensure text tracks are properly positioned - above controls */
.video-element::-webkit-media-text-track-container { .video-element::-webkit-media-text-track-container {
overflow: visible !important; overflow: visible !important;
position: relative !important; position: absolute !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
z-index: 1 !important; z-index: 1 !important;
display: flex !important;
flex-direction: column !important;
justify-content: flex-end !important;
align-items: center !important;
/* Position above controls bar (controls bar is ~120px high with padding) */
padding-bottom: 130px !important;
pointer-events: none !important;
} }
.video-element::-webkit-media-text-track-display { .video-element::-webkit-media-text-track-display {
overflow: visible !important; overflow: visible !important;
width: 100% !important;
max-width: 85% !important;
text-align: center !important;
}
/* Multi-line subtitle support */
.video-element::cue-region {
width: 85%;
}
/* Better contrast for different cue types */
.video-element::cue(b) {
font-weight: 900;
}
.video-element::cue(i) {
font-style: italic;
}
.video-element::cue(u) {
text-decoration: underline;
}
/* Responsive adjustments */
@media (max-width: 640px) {
.video-element::cue {
font-size: 1.25rem;
}
.video-element::-webkit-media-text-track-container {
padding-bottom: 110px !important;
}
:fullscreen .video-element::cue,
.video-element:fullscreen::cue {
font-size: 1.75rem;
}
} }
+91 -24
View File
@@ -96,15 +96,9 @@ export const VideoElement: React.FC<VideoElementProps> = ({
if (tracks && processedSubtitles.length > 0) { if (tracks && processedSubtitles.length > 0) {
const defaultSubtitle = processedSubtitles.find((sub) => sub.default) const defaultSubtitle = processedSubtitles.find((sub) => sub.default)
if (defaultSubtitle) { if (defaultSubtitle) {
// Find the corresponding track and set it as showing console.log(`🎯 Found default subtitle in metadata: ${defaultSubtitle.label}`)
for (let i = 0; i < tracks.length; i++) { // Set subtitle in context (this will trigger the useEffect that enables it)
const track = tracks[i] setSubtitle(defaultSubtitle)
if (track.language === defaultSubtitle.lang) {
track.mode = 'showing'
setSubtitle(defaultSubtitle)
break
}
}
} }
} }
@@ -218,6 +212,8 @@ export const VideoElement: React.FC<VideoElementProps> = ({
// Process subtitles - convert SRT to VTT blob URLs // Process subtitles - convert SRT to VTT blob URLs
useEffect(() => { useEffect(() => {
let cancelled = false
// Clean up old blob URLs // Clean up old blob URLs
subtitleBlobUrlsRef.current.forEach((url) => URL.revokeObjectURL(url)) subtitleBlobUrlsRef.current.forEach((url) => URL.revokeObjectURL(url))
subtitleBlobUrlsRef.current = [] subtitleBlobUrlsRef.current = []
@@ -239,8 +235,17 @@ export const VideoElement: React.FC<VideoElementProps> = ({
throw new Error(`Failed to fetch subtitle: ${response.status} ${response.statusText}`) throw new Error(`Failed to fetch subtitle: ${response.status} ${response.statusText}`)
} }
const srtContent = await response.text() const srtContent = await response.text()
console.log(`SRT content length: ${srtContent.length} chars`)
const blobUrl = createSubtitleBlobURL(srtContent, 'srt') const blobUrl = createSubtitleBlobURL(srtContent, 'srt')
subtitleBlobUrlsRef.current.push(blobUrl) subtitleBlobUrlsRef.current.push(blobUrl)
// Debug: fetch the blob URL to verify VTT content
const vttResponse = await fetch(blobUrl)
const vttContent = await vttResponse.text()
console.log(`VTT content preview (first 500 chars):`, vttContent.substring(0, 500))
console.log(`Total VTT length: ${vttContent.length} chars`)
console.log(`Processed SRT subtitle "${subtitle.label}": ${subtitle.src} -> ${blobUrl}`) console.log(`Processed SRT subtitle "${subtitle.label}": ${subtitle.src} -> ${blobUrl}`)
return { ...subtitle, src: blobUrl } return { ...subtitle, src: blobUrl }
} }
@@ -253,13 +258,18 @@ export const VideoElement: React.FC<VideoElementProps> = ({
} }
}) })
) )
setProcessedSubtitles(processed)
// Only update state if not cancelled
if (!cancelled) {
setProcessedSubtitles(processed)
}
} }
processSubtitles() processSubtitles()
// Cleanup function // Cleanup function
return () => { return () => {
cancelled = true
subtitleBlobUrlsRef.current.forEach((url) => URL.revokeObjectURL(url)) subtitleBlobUrlsRef.current.forEach((url) => URL.revokeObjectURL(url))
subtitleBlobUrlsRef.current = [] subtitleBlobUrlsRef.current = []
} }
@@ -475,20 +485,50 @@ export const VideoElement: React.FC<VideoElementProps> = ({
const tracks = video.textTracks const tracks = video.textTracks
if (!tracks || tracks.length === 0) return if (!tracks || tracks.length === 0) return
// Disable all tracks first const enableSubtitle = () => {
for (let i = 0; i < tracks.length; i++) { // Disable all tracks first
tracks[i].mode = 'hidden' for (let i = 0; i < tracks.length; i++) {
tracks[i].mode = 'hidden'
}
// Enable the selected subtitle track
if (settings.subtitle) {
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i]
if (track.language === settings.subtitle.lang) {
// Wait for track to have cues before showing
if (track.cues && track.cues.length > 0) {
track.mode = 'showing'
console.log(`🔊 Enabled subtitle track: ${track.label} (${track.language})`)
console.log(` - cues available: ${track.cues.length}`)
console.log(` - track.mode: ${track.mode}`)
} else {
console.warn(`⚠️ Track ${track.label} has no cues yet, waiting...`)
// Track not ready yet, will be handled by load event
track.mode = 'showing'
}
break
}
}
}
} }
// Enable the selected subtitle track // Try to enable immediately
if (settings.subtitle) { enableSubtitle()
// Also listen for track load events to retry
const handleTrackChange = () => {
console.log(`🔄 Track changed, re-enabling subtitle`)
enableSubtitle()
}
for (let i = 0; i < tracks.length; i++) {
tracks[i].addEventListener('load', handleTrackChange)
}
return () => {
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
const track = tracks[i] tracks[i].removeEventListener('load', handleTrackChange)
if (track.language === settings.subtitle.lang) {
track.mode = 'showing'
console.log(`Enabled subtitle track: ${track.label} (${track.language})`)
break
}
} }
} }
}, [settings.subtitle, videoRef]) }, [settings.subtitle, videoRef])
@@ -500,12 +540,26 @@ export const VideoElement: React.FC<VideoElementProps> = ({
const handleTrackLoad = (e: Event) => { const handleTrackLoad = (e: Event) => {
const track = e.target as HTMLTrackElement const track = e.target as HTMLTrackElement
console.log(`Track loaded: ${track.label} (${track.srclang})`, track.readyState) const textTrack = track.track
console.log(`✅ Track loaded: ${track.label} (${track.srclang})`)
console.log(` - readyState: ${track.readyState}`)
console.log(` - track.mode: ${textTrack.mode}`)
console.log(` - track.cues: ${textTrack.cues?.length || 0}`)
console.log(` - src: ${track.src}`)
// Log first few cues if available
if (textTrack.cues && textTrack.cues.length > 0) {
console.log(` - First cue: ${(textTrack.cues[0] as VTTCue).startTime}s - ${(textTrack.cues[0] as VTTCue).endTime}s: "${(textTrack.cues[0] as VTTCue).text}"`)
} else {
console.warn(` ⚠️ No cues found in track!`)
}
} }
const handleTrackError = (e: Event) => { const handleTrackError = (e: Event) => {
const track = e.target as HTMLTrackElement const track = e.target as HTMLTrackElement
console.error(`Track error: ${track.label} (${track.srclang})`, track.track.cues?.length) console.error(`Track error: ${track.label} (${track.srclang})`)
console.error(` - src: ${track.src}`)
console.error(` - readyState: ${track.readyState}`)
} }
const trackElements = video.querySelectorAll('track') const trackElements = video.querySelectorAll('track')
@@ -520,7 +574,11 @@ export const VideoElement: React.FC<VideoElementProps> = ({
for (let i = 0; i < textTracks.length; i++) { for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i] const track = textTracks[i]
if (track.mode === 'showing') { if (track.mode === 'showing') {
console.log(`Active track: ${track.label}, cues: ${track.cues?.length || 0}, active cues: ${track.activeCues?.length || 0}`) console.log(`🎬 Cuechange: ${track.label}, cues: ${track.cues?.length || 0}, active cues: ${track.activeCues?.length || 0}`)
if (track.activeCues && track.activeCues.length > 0) {
const cue = track.activeCues[0] as VTTCue
console.log(` - Active cue text: "${cue.text}"`)
}
} }
} }
} }
@@ -529,6 +587,15 @@ export const VideoElement: React.FC<VideoElementProps> = ({
textTracks[i].addEventListener('cuechange', handleCueChange) textTracks[i].addEventListener('cuechange', handleCueChange)
} }
// Log all text tracks after a delay to see their state
setTimeout(() => {
console.log(`📊 Text tracks summary (${textTracks.length} total):`)
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i]
console.log(` [${i}] ${track.label} (${track.language}): mode=${track.mode}, cues=${track.cues?.length || 0}`)
}
}, 1000)
return () => { return () => {
trackElements.forEach((track) => { trackElements.forEach((track) => {
track.removeEventListener('load', handleTrackLoad) track.removeEventListener('load', handleTrackLoad)