Skip to content

Instantly share code, notes, and snippets.

@taka-wang
Created February 3, 2024 09:49
Show Gist options
  • Save taka-wang/d76124b34f63e1b8152e6a845132e517 to your computer and use it in GitHub Desktop.
Save taka-wang/d76124b34f63e1b8152e6a845132e517 to your computer and use it in GitHub Desktop.
Load external subtitle for youtube
function loadSRT(url, callback) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
var subtitles = parseSRT(httpRequest.responseText);
callback(subtitles);
} else {
console.error(
'Failed to load SRT file. HTTP status:',
httpRequest.status,
);
}
}
};
httpRequest.open('GET', url, true);
httpRequest.send();
}
function parseSRT(srtText) {
var subtitles = parser.fromSrt(srtText, true);
for (var i in subtitles) {
subtitles[i] = {
start: subtitles[i].startTime / 1000,
end: subtitles[i].endTime / 1000,
text: subtitles[i].text,
};
}
return subtitles;
}
function initializeSubtitles(videoElement, subtitles) {
new YoutubeExternalSubtitle.Subtitle(videoElement, subtitles);
}
function loadSRT(t,e){var n=new XMLHttpRequest;n.onreadystatechange=function(){n.readyState===XMLHttpRequest.DONE&&(200===n.status?e(parseSRT(n.responseText)):console.error("Failed to load SRT file. HTTP status:",n.status))},n.open("GET",t,!0),n.send()}function parseSRT(t){var e=parser.fromSrt(t,!0);for(var n in e)e[n]={start:e[n].startTime/1e3,end:e[n].endTime/1e3,text:e[n].text};return e}function initializeSubtitles(t,e){new YoutubeExternalSubtitle.Subtitle(t,e)}
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).YoutubeExternalSubtitle=e()}(this,(function(){"use strict";var t=function(){return(t=Object.assign||function(t){for(var e,n=1,i=arguments.length;n<i;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)},e=new(function(){function t(){this.window=null,this.document=null,this.YT=null,this.initService=null}return t.prototype.setWindow=function(t){this.window=t},t.prototype.getWindow=function(){return this.window},t.prototype.setDocument=function(t){this.document=t},t.prototype.getDocument=function(){return this.document},t.prototype.setYT=function(t){this.YT=t},t.prototype.getYT=function(){return this.YT},t.prototype.setInitService=function(t){this.initService=t},t.prototype.getInitService=function(){return this.initService},t}()),n="youtube-external-subtitle-style",i="youtube-external-subtitle",r="fullscreen",o="fullscreen-ignore",l=function(t){return e.getInitService().getSubtitles().filter((function(e){return e.isInContainer(t)}))},s=function(){for(var t=e.getDocument(),n=function(t){return t.fullscreenElement||t.webkitFullscreenElement||t.webkitCurrentFullScreenElement||t.mozFullScreenElement||t.msFullscreenElement}(t),i=!!n,r=function(t){if(!t)return null;if(t.youtubeExternalSubtitle)return t.youtubeExternalSubtitle;var e=l(t);return e.length>0?e[0]:null}(n),o=0,s=l(t);o<s.length;o++){var u=s[o];u.setIsFullscreenActive(i?r===u:null)}},u=function(){function t(){this.subtitles=[]}return t.prototype.getSubtitles=function(){return this.subtitles},t.prototype.addSubtitle=function(t){this.subtitles.push(t)},t.prototype.removeSubtitle=function(t){var e=this.subtitles.indexOf(t);-1!==e&&this.subtitles.splice(e,1)},t.prototype.grantIframeApi=function(t){if(null===e.getYT()){var n=e.getWindow(),i=e.getDocument();!function(t,e){if(t())e();else var n=setInterval((function(){t()&&(clearInterval(n),e())}),100)}((function(){return function(t){return!(!t.YT||!t.YT.Player)}(n)}),(function(){e.setYT(n.YT),t()})),function(t){(function(t){for(var e=t.getElementsByTagName("script"),n=0;n<e.length;n++){var i=e[n].src;if(i&&-1!==i.indexOf("youtube.com/iframe_api"))return!0}return!1})(t)||function(t){var e=t.createElement("script");e.src="https://www.youtube.com/iframe_api";var n=t.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)}(t)}(i)}else t()},t.prototype.grantGlobalStyles=function(){var t=e.getDocument();(function(t){return!!t.getElementById(n)})(t)||function(t){var e=t.createElement("style");e.id=n,e.type="text/css",e.innerHTML="\n ."+i+" { position: absolute; display: none; z-index: 0; pointer-events: none; color: #fff; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-weight: normal; font-size: 17px; text-align: center; }\n ."+i+" span { background: #000; background: rgba(0, 0, 0, 0.9); padding: 1px 4px; display: inline-block; margin-bottom: 2px; }\n ."+i+"."+o+" { display: none !important; }\n ."+i+"."+r+" { z-index: 3000000000; }\n ";var l=t.getElementsByTagName("head")[0]||t.documentElement;l.insertBefore(e,l.firstChild),t.addEventListener("fullscreenchange",s),t.addEventListener("webkitfullscreenchange",s),t.addEventListener("mozfullscreenchange",s),t.addEventListener("MSFullscreenChange",s)}(t)},t}(),a=function(t){return Math.floor(t/10)},c=function(t,e){for(var n=[],i=a(e),r=a(t);r<=i;r++)n.push(r);return n},f=function(t,e){var n=t.indexOf("#"),i="";-1!==n&&(i=t.substr(n),t=t.substr(0,n));var r=t.indexOf("?"),o="";-1!==r&&(o=t.substr(r),t=t.substr(0,r));for(var l=0,s=Object.keys(e);l<s.length;l++){var u=s[l];o+=(""===o?"?":"&")+u+"="+e[u]}return""+t+o+i},h=function(t,e,n,l,s){if(t.className=function(t){var e=[i];return null!==t&&e.push(t?r:o),e.join(" ")}(n),t.innerHTML=function(t){return"<span>"+(null===t?"":t).replace(/(?:\r\n|\r|\n)/g,"</span><br /><span>")+"</span>"}(l),t.style.display=null===l?"":"block",e){var u=function(t,e){var n=t.offsetHeight;return{x:t.offsetLeft-t.scrollLeft+t.clientLeft,y:t.offsetTop-t.scrollTop+t.clientTop,width:t.offsetWidth,height:n,bottomPadding:n<200&&!e?20:60}}(e.getIframe(),s);t.style.visibility="hidden",t.style.top=u.y+"px",t.style.left=u.x+"px",t.style.maxWidth=u.width-20+"px",t.style.fontSize=u.height/260+"em",t.style.top=u.y+u.height-u.bottomPadding-t.offsetHeight+"px",t.style.left=u.x+(u.width-t.offsetWidth)/2+"px",t.style.visibility=""}},d=function(){function n(t,n,i){var r=this;if(void 0===n&&(n=[]),void 0===i&&(i=null),this.cache=null,this.timeChangeInterval=0,this.controlsHideTimeout=0,this.player=null,this.videoId=null,this.element=null,this.renderMethod=null,this.state={text:null,isFullscreenActive:null,controlsVisible:!0},this.onTimeChange=function(){var t=function(t,e){if(!e)return null;var n=e[a(t)];if(!n)return null;for(var i=0,r=n;i<r.length;i++){var o=r[i];if(t>=o.start&&t<=o.end)return o}return null}(r.player.getCurrentTime(),r.cache);r.setState({text:t?t.text:null})},this.onControlsHide=function(){r.setState({controlsVisible:!1})},this.onPlayerReady=function(){r.videoId=r.getCurrentVideoId()},this.onPlayerStateChange=function(t){if(r.videoId===r.getCurrentVideoId()){var n=e.getYT();t.data===n.PlayerState.PLAYING?r.start():t.data===n.PlayerState.PAUSED?r.stop():t.data===n.PlayerState.ENDED&&(r.stop(),r.setState({text:null}))}},t.youtubeExternalSubtitle)throw new Error("YoutubeExternalSubtitle: subtitle is already added for this element");t.youtubeExternalSubtitle=this;var o=function(t){var e=t;return-1===e.indexOf("enablejsapi=1")&&(e=f(e,{enablejsapi:"1"})),-1===e.indexOf("html5=1")&&(e=f(e,{html5:"1"})),-1===e.indexOf("playsinline=1")&&(e=f(e,{playsinline:"1"})),-1===e.indexOf("fs=")&&(e=f(e,{fs:"0"})),e}(t.src);t.src!==o&&(t.src=o),this.load(n),this.element=function(t,n){var i=e.getDocument().createElement("div");return i.youtubeExternalSubtitle=n,t.parentNode.insertBefore(i,t.nextSibling),i}(t,this),this.renderMethod=null===i?h:i;var l=e.getInitService();l.grantGlobalStyles(),l.addSubtitle(this),this.render(),l.grantIframeApi((function(){var n=e.getYT();r.player=new n.Player(t),r.player.addEventListener("onReady",r.onPlayerReady),r.player.addEventListener("onStateChange",r.onPlayerStateChange)}))}return n.prototype.load=function(t){this.cache=function(t){for(var e={},n=0,i=t;n<i.length;n++)for(var r=i[n],o=0,l=c(r.start,r.end);o<l.length;o++){var s=l[o];e[s]||(e[s]=[]),e[s].push(r)}return e}(t)},n.prototype.setIsFullscreenActive=function(t){this.setState({isFullscreenActive:t})},n.prototype.destroy=function(){this.stop(),this.element.parentNode.removeChild(this.element),this.player.getIframe().youtubeExternalSubtitle=null,this.player.removeEventListener("onReady",this.onPlayerReady),this.player.removeEventListener("onStateChange",this.onPlayerStateChange),e.getInitService().removeSubtitle(this)},n.prototype.render=function(){this.renderMethod(this.element,this.player,this.state.isFullscreenActive,this.state.text,this.state.controlsVisible)},n.prototype.isInContainer=function(t){return t.contains(this.element)||t===this.element},n.prototype.getYTPlayer=function(){return this.player},n.prototype.setState=function(e){var n=this.state,i=t(t({},n),e);(function(t,e){for(var n=0,i=Object.keys(e);n<i.length;n++){var r=i[n];if(t[r]!==e[r])return!0}return!1})(n,i)&&(this.state=i,this.render())},n.prototype.start=function(){this.stop();var t=e.getWindow();this.timeChangeInterval=t.setInterval(this.onTimeChange,500),this.controlsHideTimeout=t.setTimeout(this.onControlsHide,3e3),this.onTimeChange()},n.prototype.stop=function(){var t=e.getWindow();t.clearInterval(this.timeChangeInterval),t.clearTimeout(this.controlsHideTimeout),this.setState({controlsVisible:!0})},n.prototype.getCurrentVideoId=function(){return this.player.getVideoData().video_id},n}();return function(t){e.setWindow(t),e.setDocument(t.document),e.setInitService(new u)}(window),{Subtitle:d}}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment