UNPKG

ovenplayer

Version:

OvenPlayer is Open-Source HTML5 Player. OvenPlayer supports WebRTC Signaling from OvenMediaEngine for Sub-Second Latency Streaming.

351 lines (329 loc) 14.3 kB
import {getBrowserLanguage} from "utils/browser"; /* * sami-parser * The MIT License (MIT) * * Copyright (c) 2013 Constantine Kim <elegantcoder@gmail.com> * https://github.com/elegantcoder/sami-parser * */ const langCodes = ["ab","aa","af", "ak", "sq", "am", "ar", "an", "hy", "as", "av", "ae", "ay", "az", "bm", "ba", "eu", "be", "bn", "bh", "bi", "nb","bs","br","bg","my","es","ca","km","ch","ce","ny","ny","zh","za","cu","cu","cv","kw", "co","cr","hr","cs","da","dv","dv","nl","dz","en","eo","et","ee","fo","fj","fi","nl","fr","ff","gd","gl","lg","ka","de","ki","el","kl","gn","gu","ht","ht","ha","he","hz","hi","ho","hu","is","io","ig","id","ia","ie","iu","ik","ga", "it","ja","jv","kl","kn","kr","ks","kk","ki","rw","ky","kv","kg","ko","kj","ku","kj","ky","lo","la","lv","lb","li","li","li","ln","lt","lu","lb","mk","mg","ms","ml","dv","mt","gv","mi","mr","mh","ro","ro","mn","na","nv","nv","nd", "nr","ng","ne","nd","se","no","nb","nn","ii","ny","nn","ie","oc","oj","cu","cu","cu","or","om","os","os","pi","pa","ps","fa","pl","pt","pa","ps","qu","ro","rm","rn","ru","sm","sg","sa","sc","gd","sr","sn","ii","sd","si","si","sk", "sl","so","st","nr","es","su","sw","ss","sv","tl","ty","tg","ta","tt","te","th","bo","ti","to","ts","tn","tr","tk","tw","ug","uk","ur","ug","uz","ca","ve","vi","vo","wa","cy","fy","wo","xh","yi","yo","za","zu"]; const reOpenSync = /<sync/i; const reCloseSync = /<sync|<\/body|<\/sami/i; const reLineEnding = /\r\n?|\n/g; const reBrokenTag = /<[a-z]*[^>]*<[a-z]*/g; const reStartTime = /<sync[^>]+?start[^=]*=[^0-9]*([0-9]*)["^0-9"]*/i; const reBr = /<br[^>]*>/ig; const reStyle = /<style[^>]*>([\s\S]*?)<\/style[^>]*>/i; const reComment = /(<!--|-->)/g; const clone = function(obj) { var flags, key, newInstance; if ((obj == null) || typeof obj !== 'object') { return obj; } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof RegExp) { flags = ''; if (obj.global != null) { flags += 'g'; } if (obj.ignoreCase != null) { flags += 'i'; } if (obj.multiline != null) { flags += 'm'; } if (obj.sticky != null) { flags += 'y'; } return new RegExp(obj.source, flags); } newInstance = new obj.constructor(); for (key in obj) { newInstance[key] = clone(obj[key]); } return newInstance; }; const strip_tags = function (input, allowed) { // http://kevin.vanzonneveld.net // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + improved by: Luke Godfrey // + input by: Pul // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + bugfixed by: Onno Marsman // + input by: Alex // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + input by: Marc Palau // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + input by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + bugfixed by: Eric Nagel // + input by: Bobby Drake // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + bugfixed by: Tomasz Wesolowski // + input by: Evertjan Garretsen // + revised by: Rafał Kukawski (http://blog.kukawski.pl/) // * example 1: strip_tags('<p>Kevin</p> <br /><b>van</b> <i>Zonneveld</i>', '<i><b>'); // * returns 1: 'Kevin <b>van</b> <i>Zonneveld</i>' // * example 2: strip_tags('<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>', '<p>'); // * returns 2: '<p>Kevin van Zonneveld</p>' // * example 3: strip_tags("<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>", "<a>"); // * returns 3: '<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>' // * example 4: strip_tags('1 < 5 5 > 1'); // * returns 4: '1 < 5 5 > 1' // * example 5: strip_tags('1 <br/> 1'); // * returns 5: '1 1' // * example 6: strip_tags('1 <br/> 1', '<br>'); // * returns 6: '1 1' // * example 7: strip_tags('1 <br/> 1', '<br><br/>'); // * returns 7: '1 <br/> 1' allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>) var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi; return input.replace(commentsAndPhpTags, '').replace(tags, function($0, $1) { return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; }); }; const _sort = function(langItem) { return langItem.sort(function(a, b) { var res; if ((res = a.start - b.start) === 0) { return a.end - b.end; } else { return res; } }); }; const _mergeMultiLanguages = function(arr) { var content, dict, i, idx, key, lang, ret, val, _i, _len, _ref; dict = {}; i = arr.length; ret = []; for (i = _i = 0, _len = arr.length; _i < _len; i = ++_i) { val = arr[i]; key = val.startTime + ',' + val.endTime; if ((idx = dict[key]) !== void 0) { _ref = val.languages; for (lang in _ref) { content = _ref[lang]; ret[idx].languages[lang] = content; } } else { ret.push(val); dict[key] = ret.length - 1; } } return ret; }; const SmiParser = function(sami, options) { var definedLangs, duration, errors, getDefinedLangs, getLanguage, key, makeEndTime, parse, result, value, _ref, fixedLang; parse = function() { var element, error, innerText, isBroken, item, lang, langItem, lineNum, nextStartTagIdx, ret, startTagIdx, startTime, str, tempRet, _ref, _ref1, _ref2; error = function(error) { var e; e = new Error(error); e.line = lineNum; e.context = element; return errors.push(e); }; lineNum = 1; ret = []; tempRet = {}; str = sami; while (true) { startTagIdx = str.search(); if (nextStartTagIdx <= 0 || startTagIdx < 0) { break; } nextStartTagIdx = str.slice(startTagIdx + 1).search(reCloseSync) + 1; if (nextStartTagIdx > 0) { element = str.slice(startTagIdx, startTagIdx + nextStartTagIdx); } else { element = str.slice(startTagIdx); } lineNum += ((_ref = str.slice(0, startTagIdx).match(reLineEnding)) != null ? _ref.length : void 0) || 0; if (isBroken = reBrokenTag.test(element)) { error('ERROR_BROKEN_TAGS'); } str = str.slice(startTagIdx + nextStartTagIdx); startTime = +((_ref1 = element.match(reStartTime)) != null ? parseFloat(_ref1[1]/1000) : void 0); //HSLEE ms -> s 로 변경 if (startTime === null || startTime < 0) { error('ERROR_INVALID_TIME'); } // We don't need complex language. cus SMI doens't obey the rules... lang = getLanguage(element); //lang = "ko"; if (!lang) { // continue; error('ERROR_INVALID_LANGUAGE'); } lineNum += ((_ref2 = element.match(reLineEnding)) != null ? _ref2.length : void 0) || 0; element = element.replace(reLineEnding, ''); element = element.replace(reBr, "\n"); innerText = strip_tags(element).trim(); //HSLEE : 20180530 - 우린 랭기지 구분이 필요 없다. 있는거 그대로 보여줄뿐 item = { start: startTime, //languages: {}, text: "", contents: innerText }; if (lang) { //item.languages[lang] = innerText; item.text = innerText; } tempRet[lang] || (tempRet[lang] = []); //tempRet[lang].push(item); if(item.start){ tempRet[lang].push(item); } } //fixed by hslee 190130 //SMI was designed for multi language. But global standard (my guess) SRT, VTT doesn't support multi language. //This update is handling if SMI has multiple languages. fixedLang = fixedLang || getBrowserLanguage(); let convertedLanguageNames = Object.keys(tempRet); if(convertedLanguageNames && convertedLanguageNames.length > 0){ if(convertedLanguageNames.indexOf(fixedLang) > -1){ langItem = tempRet[fixedLang]; }else{ langItem = tempRet[convertedLanguageNames.filter(function(name){return name !== "undefined"})[0]]; } langItem = _sort(langItem); langItem = makeEndTime(langItem); ret = ret.concat(langItem); } //ret = _mergeMultiLanguages(ret); ret = _sort(ret); return ret; }; getLanguage = function(element) { var className, lang; if(!element){return ;} for (className in definedLangs) { lang = definedLangs[className]; if (lang.reClassName.test(element)) { return lang.lang; } } }; getDefinedLangs = function() { var className, declaration, e, error, lang, matched, parsed, rule, selector, _i, _len, _ref, _ref1, _results; try { matched = ((_ref = sami.match(reStyle)) != null ? _ref[1] : void 0) || ''; matched = matched.replace(reComment, ''); parsed = cssParse(matched); _ref1 = parsed.stylesheet.rules; _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { rule = _ref1[_i]; selector = rule.selectors[0]; if ((selector != null ? selector[0] : void 0) === '.') { _results.push((function() { var _j, _len1, _ref2, _results1; _ref2 = rule.declarations; _results1 = []; for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { declaration = _ref2[_j]; if (declaration.property.toLowerCase() === 'lang') { className = selector.slice(1); lang = declaration.value.slice(0, 2); if (~langCodes.indexOf(lang)) { _results1.push(definedLangs[className] = { lang: lang, reClassName: new RegExp("class[^=]*?=[\"'\S]*(" + className + ")['\"\S]?", 'i') }); } else { throw Error(); } } else { _results1.push(void 0); } } return _results1; })()); } else { _results.push(void 0); } } return _results; } catch (_error) { e = _error; errors.push(error = new Error('ERROR_INVALID_LANGUAGE_DEFINITION')); } }; makeEndTime = function(langItem) { var i, item, _ref; i = langItem.length; while (i--) { item = langItem[i]; if ((_ref = langItem[i - 1]) != null) { //HSLEE : 이왕이면 SRT 파서와 포맷을 맞추자 _ref.end = item.start; } if (!item.contents || item.contents === '&nbsp;') { langItem.splice(i, 1); } else { delete langItem[i].contents; if (!item.end) { item.end = item.start + duration; } } } return langItem; }; errors = []; definedLangs = { KRCC: { lang: 'ko', reClassName: new RegExp("class[^=]*?=[\"'\S]*(KRCC)['\"\S]?", 'i') }, KOCC: { lang: 'ko', reClassName: new RegExp("class[^=]*?=[\"'\S]*(KOCC)['\"\S]?", 'i') }, KR: { lang: 'ko', reClassName: new RegExp("class[^=]*?=[\"'\S]*(KR)['\"\S]?", 'i') }, ENCC: { lang: 'en', reClassName: new RegExp("class[^=]*?=[\"'\S]*(ENCC)['\"\S]?", 'i') }, EGCC: { lang: 'en', reClassName: new RegExp("class[^=]*?=[\"'\S]*(EGCC)['\"\S]?", 'i') }, EN: { lang: 'en', reClassName: new RegExp("class[^=]*?=[\"'\S]*(EN)['\"\S]?", 'i') }, JPCC: { lang: 'ja', reClassName: new RegExp("class[^=]*?=[\"'\S]*(JPCC)['\"\S]?", 'i') } }; if (options != null ? options.definedLangs : void 0) { _ref = options.definedLangs; for (key in _ref) { value = _ref[key]; definedLangs[key] = value; } } duration = (options != null ? options.duration : void 0) || 10; //HSLEE ms -> s 로 변경 fixedLang = options.fixedLang; sami = sami.trim(); //getDefinedLangs(); result = parse(); return { result: result, errors: errors }; }; export default SmiParser;