From 9d3c710a2c90fc27b36bbf92d4fa72aba3f0d665 Mon Sep 17 00:00:00 2001 From: Ivan Borzenkov Date: Sat, 21 Aug 2021 10:50:44 +0300 Subject: [PATCH] add str2vtt convert for custom subs --- src/app/vendor/videojsplugins.js | 243 ++++++++++++------------------- 1 file changed, 94 insertions(+), 149 deletions(-) diff --git a/src/app/vendor/videojsplugins.js b/src/app/vendor/videojsplugins.js index 65a353ab59..26cdde6e5e 100644 --- a/src/app/vendor/videojsplugins.js +++ b/src/app/vendor/videojsplugins.js @@ -4,8 +4,6 @@ var Button = videojs.getComponent('Button'); var MenuButton = videojs.getComponent('MenuButton'); var SubtitlesButton = videojs.getComponent('SubtitlesButton'); var MenuItem = videojs.getComponent('MenuItem'); -var srt2vtt = require('srt-to-vtt'); -var fs = require('fs'); // var BiggerSubtitleButton = videojs.extend(Button, { // /** @constructor */ @@ -82,94 +80,128 @@ class CustomTrackMenuItem extends MenuItem { options = options || {}; options.label = i18n.__('Custom...'); super(player, options); - - // let that = this; - // this.fileInput_ = $(''); - // $(this.el()).append(this.fileInput_); - // this.fileInput_.on('change', function () { - // that.player_.play(); - // if (this.value === '') { - // return; - // } - // that.loadSubtitle(this.value); - // this.value = null; //reset - // }); } /** * Seek with the button's configured offset */ handleClick() { - console.log('click!'); this.player_.pause(); - // this.fileInput_.trigger('click'); // redirect to fileInput click var input = document.createElement('input'); input.type = 'file'; input.accept = '.vtt, .srt, .ssa, .ass, .txt'; input.onchange = e => { - var file = e.target.files[0]; - console.log(file); - this.loadSubtitle(file); + let file = e.target.files[0]; + if (file.type === 'text/vtt') { + this.loadSubtitle(file); + return; + } + this.convert2vtt(file).then((file) => { + this.loadSubtitle(file); + }); }; input.click(); - // const now = this.player_.currentTime(); - // - // if (this.options_.direction === 'forward') { - // this.player_.currentTime(now + this.options_.seconds); - // } else if (this.options_.direction === 'back') { - // this.player_.currentTime(now - this.options_.seconds); - // } + } + + // TODO: find some npm module, which works with strings + srt2webvtt(data) { + // remove dos newlines + var srt = data.replace(/\r+/g, ''); + // trim white space start and end + srt = srt.replace(/^\s+|\s+$/g, ''); + + // get cues + var cuelist = srt.split('\n\n'); + var result = ''; + + if (cuelist.length > 0) { + result += 'WEBVTT\n\n'; + for (var i = 0; i < cuelist.length; i=i+1) { + result += this.convertSrtCue(cuelist[i]); + } + } + + return result; + } + + convertSrtCue(caption) { + // remove all html tags for security reasons + //srt = srt.replace(/<[a-zA-Z\/][^>]*>/g, ''); + + var cue = ''; + var s = caption.split(/\n/); + + // concatenate muilt-line string separated in array into one + while (s.length > 3) { + for (var i = 3; i < s.length; i++) { + s[2] += '\n' + s[i]; + } + s.splice(3, s.length - 3); + } + + var line = 0; + + // detect identifier + if (!s[0].match(/\d+:\d+:\d+/) && s[1].match(/\d+:\d+:\d+/)) { + cue += s[0].match(/\w+/) + '\n'; + line += 1; + } + + // get time strings + if (s[line].match(/\d+:\d+:\d+/)) { + // convert time string + var m = s[1].match(/(\d+):(\d+):(\d+)(?:,(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:,(\d+))?/); + if (m) { + cue += m[1]+':'+m[2]+':'+m[3]+'.'+m[4]+' --> ' + +m[5]+':'+m[6]+':'+m[7]+'.'+m[8]+'\n'; + line += 1; + } else { + // Unrecognized timestring + return ''; + } + } else { + // file format error or comment lines + return ''; + } + + // get cue text + if (s[line]) { + cue += s[line] + '\n\n'; + } + + return cue; + } + + async convert2vtt(file) { + let text = await file.text(); + + let vtt = this.srt2webvtt(text); + return new File( + [vtt], + '/temp.vtt', + {type: 'text/vtt'} + ); } loadSubtitle(file) { - //clean tracks - var tracks = this.player_.textTracks() || []; - for (var i = 0; i < tracks.length; ++i) { - if (tracks[i].id_.indexOf('vjs_subtitles_00') !== -1) { - $(tracks[i].el()).remove(); - tracks.splice(i, 1); - break; + // on call removeRemoteTextTrack this.player_ set to null (???) + let tracks = videojs('video_player').remoteTextTracks() || []; + for (let i = tracks.length - 1; i >= 0; --i) { + if (tracks[i].language === '00') { + videojs('video_player').removeRemoteTextTrack(tracks[i]); } } - window['x'] = file; - - file.text().then((x) => fs.writeFileSync(App.settings.tmpLocation + '/temp.srt', x)).then((stream) => { - fs.createReadStream(App.settings.tmpLocation + '/temp.srt') - .pipe(srt2vtt()) - .pipe(fs.createWriteStream(App.settings.tmpLocation + '/temp.vtt')); - }).then(() => { - let file = new Blob(new Uint8Array(fs.readFileSync(App.settings.tmpLocation + '/temp.vtt')), {type: 'text/vtt'}); - console.log(file); - const track = this.player_.addRemoteTextTrack({ - kind: 'subtitles', - language: '01', - label: 'str2vtt', - mode: 'showing', - src: URL.createObjectURL(file) - }, false); - console.log(track); - }); - //return; - - //file.stream().pipe(srt2vtt()).toBlob() - - const track = this.player_.addRemoteTextTrack({ + const track = videojs('video_player').addRemoteTextTrack({ kind: 'subtitles', language: '00', label: 'original', mode: 'showing', src: URL.createObjectURL(file) }, false); - // const track = this.player_.addTextTrack('subtitles', 'Loaded', App.Settings.language, { - // src: filePath - // }); - console.log(track); - //App.vent.trigger('customSubtitles:added', filePath); - //vjs.TextTrackMenuItem.prototype.onClick.call(this); // redirect to TextTrackMenuItem.onClick } } @@ -242,90 +274,3 @@ class CustomButton extends MenuButton { } videojs.registerComponent('customButton', CustomButton); - -// // Custom Subtitles Button/Menu -// videojs.registerPlugin('customSubtitles', function () { -// -// // Find subtitlesButton -// var subtitlesButton; -// this.controlBar.children().forEach(function (el) { -// if (el.name() === 'subtitlesButton') { -// subtitlesButton = el; -// } -// }); -// -// var CustomTrackMenuItem = vjs.TextTrackMenuItem.extend({ -// -// /*@ Constructor */ -// init: function (player, options) { -// options = options || {}; -// // fake 'empty' track -// options['track'] = { -// kind: function () { -// return 'subtitles'; -// }, -// player: player, -// label: function () { -// return i18n.__('Custom...'); -// }, -// dflt: function () { -// return false; -// }, -// mode: function () { -// return false; -// } -// }; -// -// this.fileInput_ = $(''); -// $(this.el()).append(this.fileInput_); -// -// var that = this; -// -// App.vent.on('videojs:drop_sub', function () { -// var subname = Settings.droppedSub; -// var subpath = path.join(App.settings.tmpLocation, subname); -// win.info('Subtitles dropped:', subname); -// that.loadSubtitle(subpath); -// }); -// -// this.fileInput_.on('change', function () { -// that.player_.play(); -// if (this.value === '') { -// return; -// } -// that.loadSubtitle(this.value); -// this.value = null; //reset -// }); -// -// vjs.TextTrackMenuItem.call(this, player, options); -// } -// }); -// -// CustomTrackMenuItem.prototype.onClick = function () { -// this.player_.pause(); -// this.fileInput_.trigger('click'); // redirect to fileInput click -// }; -// -// CustomTrackMenuItem.prototype.loadSubtitle = function (filePath) { -// -// //clean tracks -// var tracks = this.player_.textTracks() || []; -// for (var i = 0; i < tracks.length; ++i) { -// if (tracks[i].id_.indexOf('vjs_subtitles_00') !== -1) { -// $(tracks[i].el()).remove(); -// tracks.splice(i, 1); -// break; -// } -// } -// -// this.track = this.player_.addTextTrack('subtitles', i18n.__('Custom...'), '00', { -// src: filePath -// }); -// App.vent.trigger('customSubtitles:added', filePath); -// vjs.TextTrackMenuItem.prototype.onClick.call(this); // redirect to TextTrackMenuItem.onClick -// }; -// -// subtitlesButton.menu.addItem(new CustomTrackMenuItem(this)); -// subtitlesButton.show(); // Always show subtitles button -// -// });