From 0696d84351d0ead63b24a54cf813de56ee50a9eb Mon Sep 17 00:00:00 2001 From: Tsachi Shlidor Date: Tue, 26 Nov 2024 18:38:44 +0200 Subject: [PATCH 1/5] feat: auto-fetch transcripts from language --- src/plugins/paced-transcript/index.js | 57 +++++++++++++++------------ 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/plugins/paced-transcript/index.js b/src/plugins/paced-transcript/index.js index 0756ab86..98c1b8bf 100644 --- a/src/plugins/paced-transcript/index.js +++ b/src/plugins/paced-transcript/index.js @@ -3,16 +3,18 @@ import { getCloudinaryUrl, extendCloudinaryConfig } from 'plugins/cloudinary/com function pacedTranscript(config) { const player = this; const source = player.cloudinary.source(); + const baseUrl = getCloudinaryUrl( + source.publicId(), + extendCloudinaryConfig(player.cloudinary.cloudinaryConfig(), { resource_type: 'raw' }) + ); const options = { kind: config.kind || 'captions', label: config.label || 'Captions', default: config.default, srclang: config.srclang || 'en', - src: config.src || getCloudinaryUrl( - source.publicId(), - extendCloudinaryConfig(player.cloudinary.cloudinaryConfig(), { resource_type: 'raw' }) - ) + '.transcript', + src: config.src || baseUrl + (config.srclang ? '.' + config.srclang : '') + '.transcript', + fallbackSrc: config.src || baseUrl + '.transcript', maxWords: config.maxWords, wordHighlight: config.wordHighlight, timeOffset: config.timeOffset || 0 @@ -23,30 +25,35 @@ function pacedTranscript(config) { // Load the transcription file const initTranscript = async () => { + let transcriptResponse; try { - const transcriptResponse = await fetch(options.src); - const transcriptData = await transcriptResponse.json(); - const captions = parseTranscript(transcriptData); - - const captionsTrack = player.addRemoteTextTrack({ - kind: options.kind, - label: options.label, - srclang: options.srclang, - default: options.default, - mode: options.default ? 'showing' : 'disabled' - }); - - // required for Safari to display the captions - // https://github.com/videojs/video.js/issues/8519 - await new Promise(resolve => setTimeout(resolve, 100)); - - captions.forEach(caption => { - captionsTrack.track.addCue(new VTTCue(caption.startTime, caption.endTime, caption.text)); - }); - + transcriptResponse = await fetch(options.src); + if (!transcriptResponse.ok) { + throw new Error(`loading transcription from ${options.src} failed, trying fallback URL`); + } } catch (error) { - console.error('Error loading transcription file:', error); + console.error(error); + transcriptResponse = await fetch(options.fallbackSrc); } + if (!transcriptResponse.ok) return; + const transcriptData = await transcriptResponse.json(); + const captions = parseTranscript(transcriptData); + + const captionsTrack = player.addRemoteTextTrack({ + kind: options.kind, + label: options.label, + srclang: options.srclang, + default: options.default, + mode: options.default ? 'showing' : 'disabled' + }); + + // required for Safari to display the captions + // https://github.com/videojs/video.js/issues/8519 + await new Promise(resolve => setTimeout(resolve, 100)); + + captions.forEach(caption => { + captionsTrack.track.addCue(new VTTCue(caption.startTime, caption.endTime, caption.text)); + }); }; // Generate captions from the transcription data From 9091c9268075191a352ae0394bf3a4e110175178 Mon Sep 17 00:00:00 2001 From: Tsachi Shlidor Date: Wed, 27 Nov 2024 09:46:38 +0200 Subject: [PATCH 2/5] chore: update tests --- docs/subtitles-and-captions.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/subtitles-and-captions.html b/docs/subtitles-and-captions.html index 208fb8bc..fee86a28 100644 --- a/docs/subtitles-and-captions.html +++ b/docs/subtitles-and-captions.html @@ -140,7 +140,6 @@ }, captions: { label: 'English Paced', - language: 'en', maxWords: 4, default: true, }, @@ -200,7 +199,6 @@ }, captions: { label: 'KARAOKE', - language: 'en', wordHighlight: true, maxWords: 5, timeOffset: -0.2, @@ -454,7 +452,6 @@

Example Code:

}, captions: { label: 'English Paced', - language: 'en', maxWords: 4, default: true }, @@ -497,7 +494,6 @@

Example Code:

}, captions: { label: 'KARAOKE', - language: 'en', wordHighlight: true, maxWords: 5, timeOffset: -0.2, @@ -510,7 +506,7 @@

Example Code:

const urlTranscriptPlayer = cloudinary.videoPlayer('url-transcript', { cloudName: 'demo' }); - + urlTranscriptPlayer.source('elephants', { textTracks: { captions: { From 0740ad2a83452daf138a6982abc738635fa0cf8e Mon Sep 17 00:00:00 2001 From: Tsachi Shlidor Date: Wed, 27 Nov 2024 14:38:24 +0200 Subject: [PATCH 3/5] feat: auto-fetch transcripts from language --- src/plugins/paced-transcript/index.js | 47 ++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/plugins/paced-transcript/index.js b/src/plugins/paced-transcript/index.js index 98c1b8bf..ed5ff0a3 100644 --- a/src/plugins/paced-transcript/index.js +++ b/src/plugins/paced-transcript/index.js @@ -1,20 +1,29 @@ import { getCloudinaryUrl, extendCloudinaryConfig } from 'plugins/cloudinary/common'; +const fallbackFetch = async (url, fallback) => { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed fetching from ${url} with status code ${response.status}`); + } + return response; + } catch (error) { + console.error(error); + if (fallback) { + return fallbackFetch(fallback); + } + } +}; + function pacedTranscript(config) { const player = this; - const source = player.cloudinary.source(); - const baseUrl = getCloudinaryUrl( - source.publicId(), - extendCloudinaryConfig(player.cloudinary.cloudinaryConfig(), { resource_type: 'raw' }) - ); const options = { kind: config.kind || 'captions', label: config.label || 'Captions', default: config.default, - srclang: config.srclang || 'en', - src: config.src || baseUrl + (config.srclang ? '.' + config.srclang : '') + '.transcript', - fallbackSrc: config.src || baseUrl + '.transcript', + srclang: config.srclang, + src: config.src, maxWords: config.maxWords, wordHighlight: config.wordHighlight, timeOffset: config.timeOffset || 0 @@ -25,15 +34,23 @@ function pacedTranscript(config) { // Load the transcription file const initTranscript = async () => { + let transcriptResponse; - try { - transcriptResponse = await fetch(options.src); - if (!transcriptResponse.ok) { - throw new Error(`loading transcription from ${options.src} failed, trying fallback URL`); + if (options.src) { + // Fetch from src, if explicitly provided + transcriptResponse = await fallbackFetch(options.src); + } else { + // If not, and provided language, try fetching translated transcript, fallback to base transcript + const source = player.cloudinary.source(); + const basePath = getCloudinaryUrl( + source.publicId(), + extendCloudinaryConfig(player.cloudinary.cloudinaryConfig(), { resource_type: 'raw' }) + ); + if (options.srclang) { + transcriptResponse = await fallbackFetch(`${basePath}.${options.srclang}.transcript`, `${basePath}.transcript`); + } else { + transcriptResponse = await fallbackFetch(`${basePath}.transcript`); } - } catch (error) { - console.error(error); - transcriptResponse = await fetch(options.fallbackSrc); } if (!transcriptResponse.ok) return; const transcriptData = await transcriptResponse.json(); From 9121b9fc1718e215fe2388fa580da8341b71d9d8 Mon Sep 17 00:00:00 2001 From: Tsachi Shlidor Date: Wed, 27 Nov 2024 15:35:31 +0200 Subject: [PATCH 4/5] feat: auto-fetch transcripts from language --- docs/subtitles-and-captions.html | 182 +++++++++++++++----------- src/plugins/paced-transcript/index.js | 68 +++++----- 2 files changed, 141 insertions(+), 109 deletions(-) diff --git a/docs/subtitles-and-captions.html b/docs/subtitles-and-captions.html index fee86a28..06d3e748 100644 --- a/docs/subtitles-and-captions.html +++ b/docs/subtitles-and-captions.html @@ -117,10 +117,10 @@ // Paced const pacedPlayer = cloudinary.videoPlayer('paced', { - cloudName: 'demo' + cloudName: 'prod' }); - const publicId = 'lincoln'; + const publicId = 'video/examples/cloudinary-marketing'; const textTracks = { options: { @@ -142,19 +142,7 @@ label: 'English Paced', maxWords: 4, default: true, - }, - subtitles: [ - { - label: 'German subtitles', - language: 'de', - url: 'https://res.cloudinary.com/demo/raw/upload/v1636970250/video-player/vtt/Meetup_german.vtt' - }, - { - label: 'Russian subtitles', - language: 'ru', - url: 'https://res.cloudinary.com/demo/raw/upload/v1636970275/video-player/vtt/Meetup_russian.vtt' - } - ] + } } pacedPlayer.source(publicId, { textTracks @@ -177,10 +165,10 @@ // Karaoke const karaokePlayer = cloudinary.videoPlayer('karaoke', { - cloudName: 'demo' + cloudName: 'prod' }); - karaokePlayer.source('lincoln', { + karaokePlayer.source('video/examples/cld-imagine_1080p', { textTracks: { options: { fontFace: 'Lobster', @@ -200,26 +188,46 @@ captions: { label: 'KARAOKE', wordHighlight: true, - maxWords: 5, - timeOffset: -0.2, + maxWords: 8, + url: 'https://res.cloudinary.com/prod/raw/upload/v1/video/examples/cld-imagine_1080p.transcript', default: true } } }); - // Transcript from URL - const urlTranscriptPlayer = cloudinary.videoPlayer('url-transcript', { - cloudName: 'demo' + // Auto-translated transcript + const translatedTranscriptPlayer = cloudinary.videoPlayer('translated-transcript', { + cloudName: 'prod' }); - urlTranscriptPlayer.source('elephants', { + translatedTranscriptPlayer.source('video/examples/cloudinary-marketing-pm', { textTracks: { captions: { - label: "Lincoln's Transcript", - url: 'https://res.cloudinary.com/demo/raw/upload/lincoln.transcript', - wordHighlight: true, - maxWords: 8 - } + label: "Original", + default: true, + }, + subtitles: [ + { + label: "English", + language: "en-US", + }, + { + label: "Polish", + language: "pl-PL", + }, + { + label: "Hebrew", + language: "he-IL", + }, + { + label: "Italian", + language: "it-IT", + }, + { + label: "Ukrainian", + language: "uk-UA", + } + ] } }); }, false); @@ -316,7 +324,7 @@

Karaoke player

Transcript from URL