From ce528c978313b26a7060fdac5b2aa26153c01194 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 10:34:52 -0600 Subject: [PATCH 01/19] Update sorting for subscriptions --- src/invidious.cr | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index e0e124b8..f5c97c13 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1909,12 +1909,6 @@ get "/feed/subscriptions" do |env| offset = (page - 1) * max_results end - if preferences.sort == "published - reverse" - sort = "" - else - sort = "DESC" - end - notifications = PG_DB.query_one("SELECT notifications FROM users WHERE email = $1", user.email, as: Array(String)) view_name = "subscriptions_#{sha256(user.email)[0..7]}" @@ -1925,7 +1919,7 @@ get "/feed/subscriptions" do |env| args = arg_array(notifications) notifications = PG_DB.query_all("SELECT * FROM channel_videos WHERE id IN (#{args}) - ORDER BY published #{sort}", notifications, as: ChannelVideo) + ORDER BY published DESC", notifications, as: ChannelVideo) videos = [] of ChannelVideo notifications.sort_by! { |video| video.published }.reverse! @@ -1953,7 +1947,7 @@ get "/feed/subscriptions" do |env| end videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM #{view_name} WHERE \ NOT id = ANY (#{values}) \ - ORDER BY ucid, published #{sort}", as: ChannelVideo) + ORDER BY ucid, published DESC", as: ChannelVideo) else # Show latest video from each channel @@ -1973,12 +1967,12 @@ get "/feed/subscriptions" do |env| end videos = PG_DB.query_all("SELECT * FROM #{view_name} WHERE \ NOT id = ANY (#{values}) \ - ORDER BY published #{sort} LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) + ORDER BY published DESC LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) else # Sort subscriptions as normal videos = PG_DB.query_all("SELECT * FROM #{view_name} \ - ORDER BY published #{sort} LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) + ORDER BY published DESC LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) end end @@ -2197,12 +2191,6 @@ get "/feed/private" do |env| sort = env.params.query["sort"]? sort ||= "published" - if sort == "published - reverse" - desc = "" - else - desc = "DESC" - end - view_name = "subscriptions_#{sha256(user.email)[0..7]}" if latest_only @@ -2212,7 +2200,7 @@ get "/feed/private" do |env| videos.sort_by! { |video| video.published }.reverse! else videos = PG_DB.query_all("SELECT * FROM #{view_name} \ - ORDER BY published #{desc} LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) + ORDER BY published DESC LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) end case sort From eaf55bf12c6de23d88b71ee17cc5bddf370350ff Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 10:35:18 -0600 Subject: [PATCH 02/19] Fix styling for watch indicator --- assets/css/lighttheme.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/lighttheme.css b/assets/css/lighttheme.css index 7f2a5226..b28ef0ee 100644 --- a/assets/css/lighttheme.css +++ b/assets/css/lighttheme.css @@ -9,7 +9,7 @@ a { } /* All links that do not fit with the default color goes here */ -a > .icon, +a:not([data-id]) > .icon, .pure-u-md-1-5 > .h-box > a[href^="/watch?"], .playlist-restricted > ol > li > a { color: #303030; From 79c10407968f71737c9cc508e8bb5a3e0fa009c8 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 10:36:01 -0600 Subject: [PATCH 03/19] Remove sourceMap link for JS source --- assets/js/dash.mediaplayer.min.js | 3 +-- assets/js/silvermine-videojs-quality-selector.min.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/js/dash.mediaplayer.min.js b/assets/js/dash.mediaplayer.min.js index bb214405..22207e41 100644 --- a/assets/js/dash.mediaplayer.min.js +++ b/assets/js/dash.mediaplayer.min.js @@ -25,5 +25,4 @@ function V(){sa.info("Native video element event: pause"),qa.trigger(o.default.P ;var f=a(98),g=d(f),h=a(163),i=d(h),j=a(183),k=a(46),l=d(k),m=a(50),n=d(m),o=a(47),p=d(o),q=a(45),r=d(q),s=a(158),t=d(s),u=1,v=5e3;e.__dashjs_factory_name="TimeSyncController";var w=p.default.getSingletonFactory(e);w.TIME_SYNC_FAILED_ERROR_CODE=u,w.HTTP_TIMEOUT_MS=v,p.default.updateSingletonFactory(e.__dashjs_factory_name,w),c.default=w,b.exports=c.default},{158:158,163:163,183:183,45:45,46:46,47:47,50:50,98:98}],112:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){A.on(k.default.XLINK_ELEMENT_LOADED,j,C),H=(0,g.default)(z).create({errHandler:a.errHandler,metricsModel:a.metricsModel,mediaPlayerModel:a.mediaPlayerModel,requestModifier:a.requestModifier})}function c(a){a&&(D=a)}function d(a){a&&(E=a)}function e(a){var b=void 0;G=new o.default({escapeMode:!1,attributePrefix:"",arrayAccessForm:"property",emptyNodeForm:"object",stripWhitespaces:!1,enableToStringFunc:!1,ignoreRoot:!0,matchers:D}),F=a,b=m(F.Period_asArray,F,t,r),h(b,t,r)}function f(){A.off(k.default.XLINK_ELEMENT_LOADED,j,C),H&&(H.reset(),H=null)}function h(a,b,c){var d={},e=void 0,f=void 0;d.elements=a,d.type=b,d.resolveType=c,0===d.elements.length&&l(d);for(var g=0;g")+2),d=b.resolvedContent.substr(0,e)+""+b.resolvedContent.substr(e)+"",b.resolvedContent=G.xml_str2json(d)}x(c)&&l(c)}function l(a){var b=[],c=void 0,d=void 0;if(n(a),a.resolveType===s&&A.trigger(k.default.XLINK_READY,{manifest:F}),a.resolveType===r)switch(a.type){case t:for(c=0;c=0;g--)f=a[g],f.hasOwnProperty("xlink:href")&&f["xlink:href"]===w&&a.splice(g,1);for(g=0;g=0;f--){if(c=a.elements[f],d=c.type+"_asArray",!c.resolvedContent||y())delete c.originalContent["xlink:actuate"],delete c.originalContent["xlink:href"],b.push(c.originalContent);else if(c.resolvedContent)for(g=0;g0&&E.run(F)}function p(a,b,c,d,e,f){return{url:a,parentElement:b,type:c,index:d,resolveType:e,originalContent:f,resolvedContent:null,resolved:!1}}function x(a){var b=void 0,c=void 0;for(b=0;b=a-c})}function l(a){N=N.filter(function(b){return isNaN(b.startTime)||void 0!==a&&b.startTime+b.duration=b-d||isNaN(c.duration)||c.startTime+c.duration<=a+d}))}function r(a,b){if(!a||0===a.length)return void k();for(var c=0,d=0,e=a.length;d0&&n(c,b)}function t(){J.abort(),O=[]}function u(a){switch(a.action){case m.default.ACTION_COMPLETE:N.push(a),z(a,q),L.debug("executeRequest trigger STREAM_COMPLETED"),H.trigger(i.default.STREAM_COMPLETED,{request:a,fragmentModel:this});break;case m.default.ACTION_DOWNLOAD:z(a,p),O.push(a),v(a);break;default:L.warn("Unknown request action.")}}function v(a){H.trigger(i.default.FRAGMENT_LOADING_STARTED,{sender:K,request:a}),J.load(a)}function w(a,b,c){for(var d=a.length-1,e=d;e>=0;e--){var f=a[e],g=f.startTime,h=g+f.duration;if(c=isNaN(c)?j(f):c,!isNaN(g)&&!isNaN(h)&&b+c>=g&&b-cR&&d[b].shift()}function s(a,b,c,d,e,f){var g=new m.default;return g.tcpid=b,g.dest=c,g.topen=d,g.tclose=e,g.tconnect=f,B(a,i.default.TCP_CONNECTION,g),g}function u(a,b,c,d){var e=new n.HTTPRequestTrace;return e.s=b,e.d=c,e.b=d,a.trace.push(e),a.interval||(a.interval=0),a.interval+=c,e}function w(a,b,c,d,e,f,g,h,j,k,l,m,o,p){var q=new n.HTTPRequest;return e&&e!==d&&(w(a,null,c,d,null,null,g,h,null,null,null,m,null,null),q.actualurl=e),q.tcpid=b,q.type=c,q.url=d,q.range=g,q.trequest=h,q.tresponse=j,q.responsecode=l,q._tfinish=k,q._stream=a,q._mediaduration=m,q._responseHeaders=o,q._serviceLocation=f,p?p.forEach(function(a){u(q,a.s,a.d,a.b)}):(delete q.interval,delete q.trace),B(a,i.default.HTTP_REQUEST,q),q}function z(a,b,c,d,e){var f=new p.default;return f.t=b,f.mt=c,f.to=d,e?f.lto=e:delete f.lto,B(a,i.default.TRACK_SWITCH,f),f}function B(a,b,c){q(a,b,c),f(a,b,c)}function D(a,b,c){var d=new r.default;return d.t=b,d.level=c,B(a,i.default.BUFFER_LEVEL,d),d}function F(a,b,c){var d=new t.default;return d.target=c,d.state=b,B(a,i.default.BUFFER_STATE,d),d}function H(a,b,c,d){var e=new v.default;return e.time=b,e.range=d,e.manifestInfo=c,B(a,i.default.DVR_INFO,e),e}function I(a,b){var c=new x.default,d=o(a).DroppedFrames;return c.time=b.creationTime,c.droppedFrames=b.droppedVideoFrames,d.length>0&&d[d.length-1]==c?d[d.length-1]:(B(a,i.default.DROPPED_FRAMES,c),c)}function J(a,b,c,d,e,f,g,h,j){var k=new A.default;return k.mediaType=a,k.t=b,k.type=c,k.startTime=d,k.availabilityStartTime=e,k.duration=f,k.quality=g,k.range=h,k.state=j,B(a,i.default.SCHEDULING_INFO,k),k}function K(a,b,c){var d=new E.default;d.loadingRequests=b,d.executedRequests=c,o(a).RequestsQueue=d,f(a,i.default.REQUESTS_QUEUE,d)}function L(a,b,c,d,e,h,j,k,l,m){var n=new y.ManifestUpdate;return n.mediaType=a,n.type=b,n.requestTime=c,n.fetchTime=d,n.availabilityStartTime=e,n.presentationStartTime=h,n.clientTimeOffset=j,n.currentTime=k,n.buffered=l,n.latency=m,q(g.default.STREAM,i.default.MANIFEST_UPDATE,n),f(a,i.default.MANIFEST_UPDATE,n),n}function M(a,b){if(a){for(var c in b)a[c]=b[c];e(a.mediaType,i.default.MANIFEST_UPDATE,a)}}function N(a,b,c,d,f){if(a){var g=new y.ManifestUpdateStreamInfo;return g.id=b,g.index=c,g.start=d,g.duration=f,a.streamInfo.push(g),e(a.mediaType,i.default.MANIFEST_UPDATE_STREAM_INFO,a),g}return null}function O(a,b,c,d,f,g,h,j){if(a){var k=new y.ManifestUpdateRepresentationInfo;return k.id=b,k.index=c,k.streamIndex=d,k.mediaType=f,k.startNumber=h,k.fragmentInfoType=j,k.presentationTimeOffset=g,a.representationInfo.push(k),e(a.mediaType,i.default.MANIFEST_UPDATE_TRACK_INFO,a),k}return null}function P(a){var b=g.default.STREAM;return a.trace&&Array.isArray(a.trace)?a.trace.forEach(function(a){a.hasOwnProperty("subreplevel")&&!a.subreplevel&&delete a.subreplevel}):delete a.trace,B(b,i.default.PLAY_LIST,a),a}function Q(a){return B(g.default.STREAM,i.default.DVB_ERRORS,a),a}var R=1e3,S=this.context,T=(0,C.default)(S).getInstance(),U=void 0,V=void 0,W=void 0;return U={clearCurrentMetricsForType:h,clearAllCurrentMetrics:j,getReadOnlyMetricsFor:l,getMetricsFor:o,addTcpConnection:s,addHttpRequest:w,addRepresentationSwitch:z,addBufferLevel:D,addBufferState:F,addDVRInfo:H,addDroppedFrames:I,addSchedulingInfo:J,addRequestsQueue:K,addManifestUpdate:L,updateManifestUpdateInfo:M,addManifestUpdateStreamInfo:N,addManifestUpdateRepresentationInfo:O,addPlayList:P,addDVBErrors:Q,setConfig:b},a(),U}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(99),i=d(h),j=a(171),k=d(j),l=a(189),m=d(l),n=a(183),o=a(186),p=d(o),q=a(179),r=d(q),s=a(180),t=d(s),u=a(181),v=d(u),w=a(182),x=d(w),y=a(184),z=a(188),A=d(z),B=a(46),C=d(B),D=a(187),E=d(D),F=a(50),G=d(F),H=a(47),I=d(H);e.__dashjs_factory_name="MetricsModel",c.default=I.default.getSingletonFactory(e),b.exports=c.default},{171:171,179:179,180:180,181:181,182:182,183:183,184:184,186:186,187:187,188:188,189:189,46:46,47:47,50:50,98:98,99:99}],118:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){if(c=new g.default,!a)return null;var b=a.indexOf("#");if(-1!==b)for(var d=a.substr(b+1).split("&"),e=0,f=d.length;e0?Y.addEventListener("canplay",d):Y.playbackRate=a)}function f(a,b){if(Y){if(Y.currentTime==a)return;try{a=b?g(a):a,Y.currentTime=a}catch(c){0===Y.readyState&&c.code===c.INVALID_STATE_ERR&&setTimeout(function(){Y.currentTime=a},400)}}}function g(a){var b=K(),c=a,d=9999999999;if(b)for(var e=0;e=f&&a<=g)return a;h0}function u(a){var b=void 0;null===a||Y.seeking||-1!==da.indexOf(a)||(da.push(a),Y&&1===da.length&&(b=document.createEvent("Event"),b.initEvent("waiting",!0,!1),_=Y.playbackRate,e(0),Y.dispatchEvent(b)))}function v(a){var b=da.indexOf(a),c=void 0;null!==a&&(-1!==b&&da.splice(b,1),Y&&!1===t()&&0===Y.playbackRate&&(e(_||1),Y.paused||(c=document.createEvent("Event"),c.initEvent("playing",!0,!1),Y.dispatchEvent(c))))}function w(a,b){b?u(a):v(a)}function x(){if(Y&&t()&&0===Y.playbackRate){var a=document.createEvent("Event");a.initEvent("waiting",!0,!1),Y.dispatchEvent(a)}}function y(){if(!Y)return null;var a="webkitDroppedFrameCount"in Y&&"webkitDecodedFrameCount"in Y,b="getVideoPlaybackQuality"in Y,c=null;return b?c=Y.getVideoPlaybackQuality():a&&(c={droppedVideoFrames:Y.webkitDroppedFrameCount,totalVideoFrames:Y.webkitDroppedFrameCount+Y.webkitDecodedFrameCount,creationTime:new Date}),c}function z(){if(Y){Y.autoplay=!0;var a=Y.play();a&&"undefined"!=typeof Promise&&a instanceof Promise&&a.catch(function(a){"NotAllowedError"===a.name&&ca.trigger(k.default.PLAYBACK_NOT_ALLOWED),X.warn("Caught pending play exception - continuing ("+a+")")})}}function A(){return Y?Y.paused:null}function B(){Y&&(Y.pause(),Y.autoplay=!1)}function C(){return Y?Y.seeking:null}function D(){return Y?Y.currentTime:null}function E(){return Y?Y.playbackRate:null}function F(){return Y?Y.played:null}function G(){return Y?Y.ended:null}function H(a,b){Y&&Y.addEventListener(a,b)}function I(a,b){Y&&Y.removeEventListener(a,b)}function J(){return Y?Y.readyState:NaN}function K(){return Y?Y.buffered:null}function L(){return Y?Y.clientWidth:NaN}function M(){return Y?Y.clientHeight:NaN}function N(){return Y?Y.videoWidth:NaN}function O(){return Y?Y.videoHeight:NaN}function P(){return Y&&Y.parentNode?Y.getBoundingClientRect().top-Y.parentNode.getBoundingClientRect().top:NaN}function Q(){return Y&&Y.parentNode?Y.getBoundingClientRect().left-Y.parentNode.getBoundingClientRect().left:NaN}function R(){return Y?Y.textTracks:[]}function S(a,b,c,d,e){if(Y)for(var f=0;f0){q=d(q,g),o+=g.length,s.push({ts:Date.now(),bytes:g.length});var h=(0,i.default)().getInstance().findLastTopIsoBoxCompleted(["moov","mdat"],q,r);if(h.found){var j=h.lastCompletedOffset+h.size,k=void 0;j===q.length?(k=q,q=new Uint8Array):(k=new Uint8Array(q.subarray(0,j)),q=q.subarray(j)),a.progress({data:k.buffer,lengthComputable:!1,noTrace:!0}),r=0}else r=h.lastCompletedOffset,p||(a.progress({lengthComputable:!1,noTrace:!0}),p=!0)}c(a,b)})}).catch(function(b){a.onerror&&a.onerror(b)})}function c(a,b){a.reader.read().then(b).catch(function(b){a.onerror&&200===a.response.status&&a.onerror(b)})}function d(a,b){if(0===a.length)return b;var c=new Uint8Array(a.length+b.length);return c.set(a),c.set(b,a.length),c}function e(a){if(a.abortController)a.abortController.abort();else if(a.reader)try{a.reader.cancel()}catch(b){}}function f(a,b){if(a=a.filter(function(c){return c.bytes>b/4/a.length}),a.length>1){var c=function(){var b=0,c=(a[a.length-1].ts-a[0].ts)/a.length;return a.forEach(function(d,e){var f=a[e+1];if(f){var g=f.ts-d.ts;b+=g0?(b--,t.push(setTimeout(function(){c(a,b)},m.getRetryIntervalForType(d.type)))):(i.downloadError(u[d.type],d.url,d),a.error&&a.error(d,"error",x.response.statusText),a.complete&&a.complete(d,x.response.statusText))))},A=function(b){var c=new Date;f&&(f=!1,(!b.lengthComputable||b.lengthComputable&&b.total!==b.loaded)&&(d.firstByteDate=c)),b.lengthComputable&&(d.bytesLoaded=b.loaded,d.bytesTotal=b.total),b.noTrace||(e.push({s:v,d:b.time?b.time:c.getTime()-v.getTime(),b:[b.loaded?b.loaded-w:0]}),v=c,w=b.loaded),a.progress&&b&&a.progress(b)},B=function(){x.response.status>=200&&x.response.status<=299&&(y(!0), a.success&&a.success(x.response.response,x.response.statusText,x.response.responseURL),a.complete&&a.complete(d,x.response.statusText))},C=function(){a.abort&&a.abort(d)},D=void 0;D=p&&window.fetch&&"arraybuffer"===d.responseType?(0,j.default)(g).create({requestModifier:n}):(0,h.default)(g).create({requestModifier:n});var E=n.modifyRequestURL(d.url),F=d.checkExistenceOnly?k.HTTPRequest.HEAD:k.HTTPRequest.GET,G=m.getXHRWithCredentialsForType(d.type);x={url:E,method:F,withCredentials:G,request:d,onload:B,onend:z,onerror:z,progress:A,onabort:C,loader:D};var H=(new Date).getTime();isNaN(d.delayLoadingTime)||H>=d.delayLoadingTime?(r.push(x),D.load(x)):function(){var a={httpRequest:x};s.push(a),a.delayTimeout=setTimeout(function(){if(-1!==s.indexOf(a)){s.splice(s.indexOf(a),1);try{q=new Date,v=q,r.push(a.httpRequest),D.load(a.httpRequest)}catch(b){a.httpRequest.onerror()}}},d.delayLoadingTime-H)}()}function d(a){a.request&&c(a,m.getRetryAttemptsForType(a.request.type))}function f(){t.forEach(function(a){return clearTimeout(a)}),t=[],s.forEach(function(a){return clearTimeout(a.delayTimeout)}),s=[],r.forEach(function(a){a.onloadend=a.onerror=a.onprogress=void 0,a.loader.abort(a),a.onabort()}),r=[]}a=a||{};var g=this.context,i=a.errHandler,l=a.metricsModel,m=a.mediaPlayerModel,n=a.requestModifier,p=a.useFetch||!1,q=void 0,r=void 0,s=void 0,t=void 0,u=void 0;return q={load:d,abort:f},b(),q}Object.defineProperty(c,"__esModule",{value:!0});var g=a(122),h=d(g),i=a(120),j=d(i),k=a(183),l=a(47),m=d(l),n=a(151),o=d(n);f.__dashjs_factory_name="HTTPLoader";var p=m.default.getClassFactory(f);c.default=p,b.exports=c.default},{120:120,122:122,151:151,183:183,47:47}],122:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(a){var b=new Date,c=a.request,e=new XMLHttpRequest;e.open(a.method,a.url,!0),c.responseType&&(e.responseType=c.responseType),c.range&&e.setRequestHeader("Range","bytes="+c.range),c.requestStartDate||(c.requestStartDate=b),d&&(e=d.modifyRequestHeader(e)),e.withCredentials=a.withCredentials,e.onload=a.onload,e.onloadend=a.onend,e.onerror=a.onerror,e.onprogress=a.progress,e.onabort=a.onabort,e.send(),a.response=e}function c(a){var b=a.response;b.onloadend=b.onerror=b.onprogress=void 0,b.abort()}a=a||{};var d=a.requestModifier;return{load:b,abort:c}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="XHRLoader";var h=g.default.getClassFactory(e);c.default=h,b.exports=c.default},{47:47}],123:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){var c=b&&b.droppedVideoFrames?b.droppedVideoFrames:0,g=b&&b.totalVideoFrames?b.totalVideoFrames:0,h=c-e;e=c;var i=g-f;f=g,isNaN(a)||(d[a]?(d[a].droppedVideoFrames+=h,d[a].totalVideoFrames+=i):d[a]={droppedVideoFrames:h,totalVideoFrames:i})}function b(){return d}function c(a){d=[],e=a.droppedVideoFrames,f=a.totalVideoFrames}var d=[],e=0,f=0;return{push:a,getFrameHistory:b,reset:c}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="DroppedFramesHistory";var h=g.default.getClassFactory(e);c.default=h,b.exports=c.default},{47:47}],124:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){return n.mediaInfo.type}function c(){return n.mediaInfo.streamInfo}function d(){return n.mediaInfo}function e(){return n}function f(){return m}function g(){return l}function h(){return o}function i(){return p}function j(){return q}function k(){return r}a=a||{};var l=a.abrController,m=a.streamProcessor,n=a.streamProcessor.getCurrentRepresentationInfo(),o=a.switchHistory,p=a.droppedFramesHistory,q=a.currentRequest,r=a.useBufferOccupancyABR;return{getMediaType:b,getMediaInfo:d,getDroppedFramesHistory:i,getCurrentRequest:j,getSwitchHistory:h,getStreamInfo:c,getStreamProcessor:f,getAbrController:g,getRepresentationInfo:e,useBufferOccupancyABR:k}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="RulesContext",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],125:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a,b,c){function d(a){var b=i.DEFAULT;return a!==i.DEFAULT&&a!==i.STRONG&&a!==i.WEAK||(b=a),b}var e=void 0,f=void 0,g=void 0;return e=void 0===a?h:a,f=d(c),g=void 0===b?null:b,{quality:e,reason:g,priority:f}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=-1,i={DEFAULT:.5,STRONG:1,WEAK:0};e.__dashjs_factory_name="SwitchRequest";var j=g.default.getClassFactory(e);j.NO_CHANGE=h,j.PRIORITY=i,g.default.updateClassFactory(e.__dashjs_factory_name,j),c.default=j,b.exports=c.default},{47:47}],126:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){a.newValue===i.default.NO_CHANGE&&(a.newValue=a.oldValue),d[a.oldValue]||(d[a.oldValue]={noDrops:0,drops:0,dropSize:0});var b=a.newValue-a.oldValue,c=b<0?1:0,f=c?-b:0,g=c?0:1;if(d[a.oldValue].drops+=c,d[a.oldValue].dropSize+=f,d[a.oldValue].noDrops+=g,e.push({idx:a.oldValue,noDrop:g,drop:c,dropSize:f}),e.length>j){var h=e.shift();d[h.idx].drops-=h.drop,d[h.idx].dropSize-=h.dropSize,d[h.idx].noDrops-=h.noDrop}}function b(){return d}function c(){d=[],e=[]}var d=[],e=[];return{push:a,getSwitchRequests:b,reset:c}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(125),i=d(h),j=8;e.__dashjs_factory_name="SwitchRequestHistory";var k=g.default.getClassFactory(e);c.default=k,b.exports=c.default},{125:125,47:47}],127:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){F={throughputHalfLife:{fast:x,slow:w},latencyHalfLife:{fast:z,slow:y}},p()}function c(a,b,c){return a===g.default.VIDEO?c0&&!B[a].hasCachedEntries)return;B[a].hasCachedEntries=!0}else B[a]&&B[a].hasCachedEntries&&o(a);B[a].push(k),B[a].length>q&&B[a].shift(),C[a].push(f),C[a].length>q&&C[a].shift(),e(D[a],k,.001*g,F.throughputHalfLife),e(E[a],f,1,F.latencyHalfLife)}}function e(a,b,c,d){var e=Math.pow(.5,c/d.fast);a.fastEstimate=(1-e)*b+e*a.fastEstimate;var f=Math.pow(.5,c/d.slow);a.slowEstimate=(1-f)*b+f*a.slowEstimate,a.totalWeight+=c}function f(a,b,c){var d=void 0,e=void 0;if(a?(d=B[b],e=c?r:s):(d=C[b],e=t),d){if(e>=d.length)e=d.length;else if(a)for(var f=1;f=v||g<=1/u)&&(e+=1)===d.length)break}}else e=0;return e}function h(a,b,c){return A.getMovingAverageMethod()!==g.default.MOVING_AVERAGE_SLIDING_WINDOW?j(a,b):i(a,b,c)}function i(a,b,c){var d=f(a,b,c),e=a?B:C,g=e[b];return 0!==d&&g&&0!==g.length?(g=g.slice(-d),g.reduce(function(a,b){return a+b})/g.length):NaN}function j(a,b){var c=a?F.throughputHalfLife:F.latencyHalfLife,d=a?D[b]:E[b];if(!d||d.totalWeight<=0)return NaN;var e=d.fastEstimate/(1-Math.pow(.5,d.totalWeight/c.fast)),f=d.slowEstimate/(1-Math.pow(.5,d.totalWeight/c.slow));return a?Math.min(e,f):Math.max(e,f)}function k(a,b){return h(!0,a,b)}function l(a,b){var c=k(a,b);return isNaN(c)||(c*=A.getBandwidthSafetyFactor()),c}function m(a){return h(!1,a)}function n(a){B[a]=B[a]||[],C[a]=C[a]||[],D[a]=D[a]||{fastEstimate:0,slowEstimate:0,totalWeight:0},E[a]=E[a]||{fastEstimate:0,slowEstimate:0,totalWeight:0}}function o(a){delete B[a],delete C[a],delete D[a],delete E[a],n(a)}function p(){B={},C={},D={},E={}}a=a||{};var q=20,r=3,s=4,t=4,u=1.3,v=1.3,w=8,x=3,y=2,z=1,A=a.mediaPlayerModel,B=void 0,C=void 0,D=void 0,E=void 0,F=void 0,G={push:d,getAverageThroughput:k,getSafeAverageThroughput:l,getAverageLatency:m,reset:p};return b(),G}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="ThroughputHistory",c.default=i.default.getClassFactory(e),b.exports=c.default},{47:47,98:98}],128:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){r=[],s=[],l.getUseDefaultABRRules()&&(r.push((0,q.default)(j).create({metricsModel:n,dashMetrics:p,mediaPlayerModel:l})),r.push((0,g.default)(j).create({metricsModel:n,dashMetrics:p})),r.push((0,i.default)(j).create({metricsModel:n,dashMetrics:p})),r.push((0,o.default)(j).create()),r.push((0,m.default)(j).create()),s.push((0,k.default)(j).create({metricsModel:n,dashMetrics:p,mediaPlayerModel:l}))),l.getABRCustomRules().forEach(function(a){a.type===v&&r.push(a.rule(j).create()),a.type===w&&s.push(a.rule(j).create())})}function c(a){return a.filter(function(a){return a.quality>u.default.NO_CHANGE})}function d(a){var b={},c=void 0,d=void 0,e=void 0,f=void 0,g=void 0;if(0!==a.length){for(b[u.default.PRIORITY.STRONG]=u.default.NO_CHANGE,b[u.default.PRIORITY.WEAK]=u.default.NO_CHANGE,b[u.default.PRIORITY.DEFAULT]=u.default.NO_CHANGE,c=0,d=a.length;cu.default.NO_CHANGE?Math.min(b[e.priority],e.quality):e.quality);return b[u.default.PRIORITY.WEAK]!==u.default.NO_CHANGE&&(f=b[u.default.PRIORITY.WEAK]),b[u.default.PRIORITY.DEFAULT]!==u.default.NO_CHANGE&&(f=b[u.default.PRIORITY.DEFAULT]),b[u.default.PRIORITY.STRONG]!==u.default.NO_CHANGE&&(f=b[u.default.PRIORITY.STRONG]),f!==u.default.NO_CHANGE&&(g=f),(0,u.default)(j).create(g)}}function e(a){return d(c(r.map(function(b){return b.getMaxIndex(a)})))||(0,u.default)(j).create()}function f(a){return d(c(s.map(function(b){return b.shouldAbandon(a)})))||(0,u.default)(j).create()}function h(){[r,s].forEach(function(a){a&&a.length&&a.forEach(function(a){return a.reset&&a.reset()})}),r=[],s=[]}a=a||{};var j=this.context,l=a.mediaPlayerModel,n=a.metricsModel,p=a.dashMetrics,r=void 0,s=void 0;return{initialize:b,reset:h,getMaxQuality:e,shouldAbandonFragment:f}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(134),g=d(f),h=a(132),i=d(h),j=a(129),k=d(j),l=a(131),m=d(l),n=a(133),o=d(n),p=a(130),q=d(p),r=a(47),s=d(r),t=a(125),u=d(t),v="qualitySwitchRules",w="abandonFragmentRules";e.__dashjs_factory_name="ABRRulesCollection";var x=s.default.getClassFactory(e);x.QUALITY_SWITCH_RULES=v,x.ABANDON_FRAGMENT_RULES=w,s.default.updateSingletonFactory(e.__dashjs_factory_name,x),c.default=x,b.exports=c.default},{125:125,129:129,130:130,131:131,132:132,133:133,134:134,47:47}],129:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){r=(0,k.default)(m).getInstance().getLogger(q),h()}function c(a,b){s[a]=s[a]||{},s[a][b]=s[a][b]||{}}function d(a,b){u[a]=u[a]||[],u[a].push(b)}function f(a){var b=(0,g.default)(m).create(g.default.NO_CHANGE,{name:e.__dashjs_factory_name});if(!(a&&a.hasOwnProperty("getMediaInfo")&&a.hasOwnProperty("getMediaType")&&a.hasOwnProperty("getCurrentRequest")&&a.hasOwnProperty("getRepresentationInfo")&&a.hasOwnProperty("getAbrController")))return b;var f=a.getMediaInfo(),h=a.getMediaType(),k=a.getCurrentRequest();if(!isNaN(k.index)){c(h,k.index);var q=n.getStableBufferTime();if(p.getCurrentBufferLevel(o.getReadOnlyMetricsFor(h))>q)return b;var v=s[h][k.index];if(null===v||null===k.firstByteDate||t.hasOwnProperty(v.id))return b;if(void 0===v.firstByteTime&&(u[h]=[],v.firstByteTime=k.firstByteDate.getTime(),v.segmentDuration=k.duration,v.bytesTotal=k.bytesTotal,v.id=k.index),v.bytesLoaded=k.bytesLoaded,v.elapsedTime=(new Date).getTime()-v.firstByteTime,v.bytesLoaded>0&&v.elapsedTime>0&&d(h,Math.round(8*v.bytesLoaded/v.elapsedTime)),u[h].length>=l&&v.elapsedTime>j&&v.bytesLoadedv.bytesTotal*z[A].bitrate/z[x.getQualityFor(h,f.streamInfo)].bitrate&&(b.quality=A,b.reason.throughput=v.measuredBandwidthInKbps,b.reason.fragmentID=v.id,t[v.id]=v,r.debug("( ",h,"frag id",v.id,") is asking to abandon and switch to quality to ",A," measured bandwidth was",v.measuredBandwidthInKbps),delete s[h][v.id])}}else v.bytesLoaded===v.bytesTotal&&delete s[h][v.id]}return b}function h(){s={},t={},u=[]}a=a||{};var i=1.8,j=500,l=5,m=this.context,n=a.mediaPlayerModel,o=a.metricsModel,p=a.dashMetrics,q=void 0,r=void 0,s=void 0,t=void 0,u=void 0;return q={shouldAbandon:f,reset:h},b(),q}Object.defineProperty(c,"__esModule",{value:!0});var f=a(125),g=d(f),h=a(47),i=d(h),j=a(45),k=d(j);e.__dashjs_factory_name="AbandonRequestsRule",c.default=i.default.getClassFactory(e),b.exports=c.default},{125:125,45:45,47:47}],130:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){P=(0,r.default)(J).getInstance().getLogger(O),H(),N.on(p.default.BUFFER_EMPTY,y,O),N.on(p.default.PLAYBACK_SEEKING,z,O),N.on(p.default.PERIOD_SWITCH_STARTED,A,O),N.on(p.default.MEDIA_FRAGMENT_LOADED,B,O),N.on(p.default.METRIC_ADDED,C,O),N.on(p.default.QUALITY_CHANGE_REQUESTED,E,O),N.on(p.default.FRAGMENT_LOADING_ABANDONED,F,O)}function c(a){return a.map(function(a){return Math.log(a)})}function d(a,b,c){var d=c.reduce(function(a,b,d){return b>c[a]?d:a},0);if(0===d)return null;var e=Math.max(a,v+w*b.length),f=(c[d]-1)/(e/v-1);return{gp:f,Vp:v/f}}function e(a){var b={},e=a.getMediaInfo(),g=e.bitrateList.map(function(a){return a.bandwidth}),h=c(g);h=h.map(function(a){return a-h[0]+1});var i=M.getStableBufferTime(),j=d(i,g,h);return j?(b.state=t,b.bitrates=g,b.utilities=h,b.stableBufferTime=i,b.Vp=j.Vp,b.gp=j.gp,b.lastQuality=0,f(b)):b.state=s,b}function f(a){a.placeholderBuffer=0,a.mostAdvancedSegmentStart=NaN,a.lastSegmentWasReplacement=!1,a.lastSegmentStart=NaN,a.lastSegmentDurationS=NaN,a.lastSegmentRequestTimeMs=NaN,a.lastSegmentFinishTimeMs=NaN}function h(a,b){var c=M.getStableBufferTime();if(a.stableBufferTime!==c){var e=d(c,a.bitrates,a.utilities);if(e.Vp!==a.Vp||e.gp!==a.gp){var f=K.getCurrentBufferLevel(L.getReadOnlyMetricsFor(b)),g=f+a.placeholderBuffer;g-=v,g*=e.Vp/a.Vp,g+=v,a.stableBufferTime=c,a.Vp=e.Vp,a.gp=e.gp,a.placeholderBuffer=Math.max(0,g-f)}}}function j(a){var b=a.getMediaType(),c=Q[b];return c?c.state!==s&&h(c,b):(c=e(a),Q[b]=c),c}function k(a,b){for(var c=a.bitrates.length,d=NaN,e=NaN,f=0;f=e)&&(e=g,d=f)}return d}function m(a,b){return a.Vp*(a.utilities[b]+a.gp)}function o(a,b){for(var c=a.bitrates[b],d=a.utilities[b],e=0,f=b-1;f>=0;--f)if(a.utilities[f]b.mostAdvancedSegmentStart?(b.mostAdvancedSegmentStart=c,b.lastSegmentWasReplacement=!1):b.lastSegmentWasReplacement=!0,b.lastSegmentStart=c,b.lastSegmentDurationS=a.chunk.duration,b.lastQuality=a.chunk.quality,D(b,a.chunk.mediaInfo.type)}}}function C(a){if(a&&a.metric===g.default.HTTP_REQUEST&&a.value&&a.value.type===l.HTTPRequest.MEDIA_SEGMENT_TYPE&&a.value.trace&&a.value.trace.length){var b=Q[a.mediaType];b&&b.state!==s&&(b.lastSegmentRequestTimeMs=a.value.trequest.getTime(),b.lastSegmentFinishTimeMs=a.value._tfinish.getTime(),D(b,a.mediaType))}}function D(a,b){if(!isNaN(a.lastSegmentStart)&&!isNaN(a.lastSegmentRequestTimeMs)&&!isNaN(a.placeholderBuffer)){if(a.placeholderBuffer*=x,!isNaN(a.lastSegmentFinishTimeMs)){var c=K.getCurrentBufferLevel(L.getReadOnlyMetricsFor(b)),d=c+.001*(a.lastSegmentFinishTimeMs-a.lastSegmentRequestTimeMs),e=m(a,a.lastQuality),f=Math.max(0,e-d);a.placeholderBuffer=Math.min(f,a.placeholderBuffer)}a.lastSegmentWasReplacement&&!isNaN(a.lastSegmentDurationS)&&(a.placeholderBuffer+=a.lastSegmentDurationS),a.lastSegmentStart=NaN,a.lastSegmentRequestTimeMs=NaN}}function E(a){if(a){var b=Q[a.mediaType];b&&b.state!==s&&(b.abrQuality=a.newQuality)}}function F(a){if(a){var b=Q[a.mediaType];if(b&&b.state!==s){var c=K.getCurrentBufferLevel(L.getReadOnlyMetricsFor(a.mediaType)),d=void 0;d=b.abrQuality>0?o(b,b.abrQuality):v;var e=Math.max(0,d-c);b.placeholderBuffer=Math.min(b.placeholderBuffer,e)}}}function G(a){var b=a.getMediaInfo(),c=a.getMediaType(),d=L.getReadOnlyMetricsFor(c),e=a.getStreamProcessor(),g=a.getStreamInfo(),h=a.getAbrController(),l=h.getThroughputHistory(),n=g?g.id:null,p=g&&g.manifestInfo&&g.manifestInfo.isDynamic,r=a.useBufferOccupancyABR(),v=(0,i.default)(J).create();if(v.reason=v.reason||{},!r)return v;e.getScheduleController().setTimeToLoadDelay(0);var w=j(a);if(w.state===s)return v;var x=K.getCurrentBufferLevel(d),y=l.getAverageThroughput(c,p),z=l.getSafeAverageThroughput(c,p),A=l.getAverageLatency(c),B=void 0;if(v.reason.state=w.state,v.reason.throughput=y,v.reason.latency=A,isNaN(y))return v;switch(w.state){case t:B=h.getQualityForBitrate(b,z,A),v.quality=B,v.reason.throughput=z,w.placeholderBuffer=Math.max(0,o(w,B)-x),w.lastQuality=B,!isNaN(w.lastSegmentDurationS)&&x>=w.lastSegmentDurationS&&(w.state=u);break;case u:q(w,c),B=k(w,x+w.placeholderBuffer);var C=h.getQualityForBitrate(b,z,A);B>w.lastQuality&&B>C&&(B=Math.max(C,w.lastQuality));var D=Math.max(0,x+w.placeholderBuffer-m(w,B));D<=w.placeholderBuffer?(w.placeholderBuffer-=D,D=0):(D-=w.placeholderBuffer,w.placeholderBuffer=0,Bg&&h/j>f)){k=l-1,e.debug("index: "+k+" Dropped Frames: "+h+" Total Frames: "+j);break}return(0,i.default)(c).create(k,{droppedFrames:h})}return(0,i.default)(c).create()}var c=this.context,d=void 0,e=void 0,f=.15,g=375;return d={getMaxIndex:b},a(),d}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(125),i=d(h),j=a(45),k=d(j);e.__dashjs_factory_name="DroppedFramesRule",c.default=g.default.getClassFactory(e),b.exports=c.default},{125:125,45:45,47:47}],132:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){t=(0,o.default)(m).getInstance().getLogger(s),f(),n.on(k.default.PLAYBACK_SEEKING,h,s)}function c(){if(!(p&&p.hasOwnProperty("getReadOnlyMetricsFor")&&r&&r.hasOwnProperty("getCurrentBufferLevel")))throw new Error("Missing config parameter(s)")}function d(a){var b=(0,q.default)(m).create();if(!a||!a.hasOwnProperty("getMediaType"))return b;c();var d=a.getMediaType(),f=p.getReadOnlyMetricsFor(d),h=f.BufferState.length>0?f.BufferState[f.BufferState.length-1]:null,i=a.getRepresentationInfo(),j=i.fragmentDuration;if(!h||!e(d,h)||!j)return b;if(h.state===g.default.BUFFER_EMPTY)t.info("Switch to index 0; buffer is empty."),b.quality=0,b.reason="InsufficientBufferRule: Buffer is empty";else{var k=a.getMediaInfo(),n=a.getAbrController(),o=n.getThroughputHistory(),s=r.getCurrentBufferLevel(f),u=o.getAverageThroughput(d),v=o.getAverageLatency(d),w=u*(s/j)*l;b.quality=n.getQualityForBitrate(k,w,v),b.reason="InsufficientBufferRule: being conservative to avoid immediate rebuffering"}return b}function e(a,b){u[a]=u[a]||{};var c=!1;return u[a].firstBufferLoadedEvent?c=!0:b&&b.state===g.default.BUFFER_LOADED&&(u[a].firstBufferLoadedEvent=!0,c=!0),c}function f(){u={}}function h(){f()}function j(){f(),n.off(k.default.PLAYBACK_SEEKING,h,s)}a=a||{};var l=.5,m=this.context,n=(0,i.default)(m).getInstance(),p=a.metricsModel,r=a.dashMetrics,s=void 0,t=void 0,u=void 0;return s={getMaxIndex:d,reset:j},b(),s}Object.defineProperty(c,"__esModule",{value:!0});var f=a(103),g=d(f),h=a(46),i=d(h),j=a(50),k=d(j),l=a(47),m=d(l),n=a(45),o=d(n),p=a(125),q=d(p);e.__dashjs_factory_name="InsufficientBufferRule",c.default=m.default.getClassFactory(e),b.exports=c.default},{103:103,125:125,45:45,46:46,47:47,50:50}],133:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){e=(0,i.default)(c).getInstance().getLogger(d)}function b(a){for(var b=a?a.getSwitchHistory():null,d=b?b.getSwitchRequests():[],h=0,i=0,j=0,l=(0,k.default)(c).create(),m=0;m=g&&h/i>f)){l.quality=m>0&&d[m].drops>0?m-1:m,l.reason={index:l.quality,drops:h,noDrops:i,dropSize:j},e.info("Switch history rule index: "+l.quality+" samples: "+(h+i)+" drops: "+h);break}return l}var c=this.context,d=void 0,e=void 0,f=.075,g=6;return d={getMaxIndex:b},a(),d}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(45),i=d(h),j=a(125),k=d(j);e.__dashjs_factory_name="SwitchHistoryRule",c.default=g.default.getClassFactory(e),b.exports=c.default},{125:125,45:45,47:47}],134:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){k=(0,m.default)(f).getInstance().getLogger(j)}function c(){if(!h||!h.hasOwnProperty("getReadOnlyMetricsFor"))throw new Error("Missing config parameter(s)")}function d(a){var b=(0,o.default)(f).create();if(!(a&&a.hasOwnProperty("getMediaInfo")&&a.hasOwnProperty("getMediaType")&&a.hasOwnProperty("useBufferOccupancyABR")&&a.hasOwnProperty("getAbrController")&&a.hasOwnProperty("getStreamProcessor")))return b;c();var d=a.getMediaInfo(),e=a.getMediaType(),j=h.getReadOnlyMetricsFor(e),l=a.getStreamProcessor(),m=a.getAbrController(),n=a.getStreamInfo(),p=n&&n.manifestInfo?n.manifestInfo.isDynamic:null,q=m.getThroughputHistory(),r=q.getSafeAverageThroughput(e,p),s=q.getAverageLatency(e),t=j.BufferState.length>0?j.BufferState[j.BufferState.length-1]:null,u=a.useBufferOccupancyABR();return!j||isNaN(r)||!t||u?b:(m.getAbandonmentStateFor(e)!==i.default.ABANDON_LOAD&&(t.state===g.default.BUFFER_LOADED||p)&&(b.quality=m.getQualityForBitrate(d,r,s),l.getScheduleController().setTimeToLoadDelay(0),k.info("requesting switch to index: ",b.quality,"type: ",e,"Average throughput",Math.round(r),"kbps"),b.reason={throughput:r,latency:s}),b)}function e(){}a=a||{};var f=this.context,h=a.metricsModel,j=void 0,k=void 0;return j={getMaxIndex:d,reset:e},b(),j}Object.defineProperty(c,"__esModule",{value:!0});var f=a(103),g=d(f),h=a(100),i=d(h),j=a(47),k=d(j),l=a(45),m=d(l),n=a(125),o=d(n);e.__dashjs_factory_name="ThroughputRule",c.default=k.default.getClassFactory(e),b.exports=c.default},{100:100,103:103,125:125,45:45,47:47}],135:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){}function c(a,b){return e.getCurrentBufferLevel(f.getReadOnlyMetricsFor(a.getType()))=h.getLongFormContentDurationThreshold();c=n?h.getBufferTimeAtTopQualityLongForm():h.getBufferTimeAtTopQuality()}else c=h.getStableBufferTime()}return c}a=a||{};var e=a.dashMetrics,f=a.metricsModel,h=a.mediaPlayerModel,i=a.textController,j=a.abrController,k={execute:c,getBufferTarget:d};return b(),k}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="BufferLevelRule",c.default=i.default.getClassFactory(e),b.exports=c.default},{47:47,98:98}],136:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){j=(0,i.default)(d).getInstance().getLogger(h)}function c(a,b){if(!a)return null;var c=a.getCurrentRepresentationInfo(),d=c.mediaInfo,h=d.type,i=a.getScheduleController(),k=i.getSeekTarget(),l=!isNaN(k),n=a.getBufferController(),o=a.getPlaybackController().getTime(),p=l?k:e.getIndexHandlerTime(a),q=!1,r=void 0;if(l&&i.setSeekTarget(NaN),isNaN(p)||h===g.default.FRAGMENTED_TEXT&&!f.isTextEnabled())return null;if(n){var s=n.getRangeAt(p),t=n.getRangeAt(o),u=n.getBuffer().getAllBufferRanges(),v=u?u.length:0;null===s&&null===t||l||((!s||t&&t.start!=s.start&&t.end!=s.end)&&(v>1&&(a.getFragmentModel().removeExecutedRequestsAfterTime(t.end),q=!0),s=t),j.debug("Prior to making a request for time, NextFragmentRequestRule is aligning index handler's currentTime with bufferedRange.end for",h,".",p,"was changed to",s.end),p=s.end)}if(b)p=b.startTime+b.duration/2,r=e.getFragmentRequestForTime(a,c,p,{timeThreshold:0,ignoreIsFinished:!0});else{for(r=e.getFragmentRequestForTime(a,c,p,{keepIdx:!l&&!q});r&&r.action!==m.default.ACTION_COMPLETE&&a.getFragmentModel().isFragmentLoaded(r);)r=e.getNextFragmentRequest(a,c);r&&(isNaN(r.startTime+r.duration)||e.setIndexHandlerTime(a,r.startTime+r.duration),r.delayLoadingTime=(new Date).getTime()+i.getTimeToLoadDelay(),i.setTimeToLoadDelay(0))}return r}a=a||{};var d=this.context,e=a.adapter,f=a.textController,h=void 0,j=void 0;return h={execute:c},b(),h}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(45),i=d(h),j=a(47),k=d(j),l=a(165),m=d(l);e.__dashjs_factory_name="NextFragmentRequestRule",c.default=k.default.getClassFactory(e),b.exports=c.default},{165:165,45:45,47:47,98:98}],137:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){for(var b="",c=0;c0&&(o.spans.push({name:p,line:w,row:t}),w="");var C="style_cea608_"+B.foreground+"_"+B.background;B.underline&&(C+="_underline"),B.italics&&(C+="_italics"),r.hasOwnProperty(C)||(r[C]=JSON.parse(JSON.stringify(B))),x=B,p=C}w+=A.uchar}w.trim().length>0&&o.spans.push({name:p,line:w,row:t}),m=!0,n=y}else m=!1,n=-1,k&&(k.p.push(o),o={start:g,end:i,spans:[]},k.y2=t,k.name="region_"+k.x+"_"+k.y1+"_"+k.y2,!1===q.hasOwnProperty(k.name)?(s.push(k),q[k.name]=k):(l=q[k.name],l.p.contat(k.p)),k=null)}k&&(k.p.push(o),k.y2=t+1,k.name="region_"+k.x+"_"+k.y1+"_"+k.y2,!1===q.hasOwnProperty(k.name)?(s.push(k),q[k.name]=k):(l=q[k.name],l.p.contat(k.p)),k=null);var D=[];for(t=0;t0){if(0!==u&&M!=N.row){var O=document.createElement("br");O.className="lineBreak",J.appendChild(O)}var P=!1;M===N.row&&(P=!0),M=N.row;var Q=r[N.name],R=document.createElement("span");R.className="spanPadding "+N.name+" customSpanColor",R.style.cssText=d(c,Q),0!==u&&P?u===L.spans.length-1?R.textContent=f(N.line):R.textContent=N.line:L.spans.length>1&&u0?d[b.roles[0]]:d.caption;return a=a===d.caption||a===d.subtitle?a:d.caption},f=function(){var a=!1;return b.codec&&b.codec.search(g.default.STPP)>=0&&(a=!0),b.mimeType&&b.mimeType.search(g.default.TTML)>=0&&(a=!0),a};c.captionData=a,c.lang=b.lang,c.label=b.id?b.id:b.index,c.index=b.index,c.isTTML=f(),c.defaultTrack=C(b),c.isFragmented=!O.getIsTextTrack(b.mimeType),c.isEmbedded=!!b.isEmbedded,c.kind=e(),c.roles=b.roles,c.accessibility=b.accessibility;var h=(V?V.length:0)+da.length;W.addTextTrack(c,h)}function y(a,b){var c=void 0,d=void 0,e=void 0,f=void 0,i=void 0,j=void 0,k=void 0,l=b.mediaInfo,m=l.type,n=l.mimeType,o=l.codec||n;if(!o)return void L.error("No text type defined");if(m===g.default.FRAGMENTED_TEXT)if(Y)if(j=U.getSamplesInfo(a),d=j.sampleList,!ba&&d.length>0&&(ba=d[0].cts-b.start*Z),o.search(g.default.STPP)>=0)for(R=null!==R?R:E(o),e=0;e0&&W.addCaptions(ca,0,D)}else Y=!0,Z=U.getMediaTimescaleFromMoov(a);else if(m===g.default.TEXT){var s=new DataView(a,0,a.byteLength);k=z.default.Utils.dataViewToString(s,g.default.UTF8);try{c=E(o).parse(k,0),W.addCaptions(W.getCurrentTrackIdx(),0,c)}catch(ka){N.timedTextError(ka,"parse",k)}}else if(m===g.default.VIDEO)if(b.segmentType===h.HTTPRequest.INIT_SEGMENT_TYPE){if(0===fa)for(fa=U.getMediaTimescaleFromMoov(a),e=0;e0&&(f.cts!==h?i=0:i+=1,c.fields[l].push([f.cts+ka*fa,k[l],i]),h=f.cts);return c.fields.forEach(function(a){a.sort(function(a,b){return a[0]===b[0]?a[2]-b[2]:a[0]-b[0]})}),c}function C(a){var b=!1;return da.length>1&&a.isEmbedded?b=a.id&&a.id===g.default.CC1:1===da.length?a.id&&"CC"===a.id.substring(0,2)&&(b=!0):0===da.length&&(b=a.index===V[0].index),b}function E(a){var b=void 0;return a.search(g.default.VTT)>=0?b=S:(a.search(g.default.TTML)>=0||a.search(g.default.STPP)>=0)&&(b=T),b}function G(a,b){void 0===a&&a===b&&(a=this.buffered.start(0),b=this.buffered.end(this.buffered.length-1)),this.buffered.remove(a,b)}var H=this.context,I=(0,D.default)(H).getInstance(),J=!1,K=void 0,L=void 0,M=void 0,N=void 0,O=void 0,P=void 0,Q=void 0,R=void 0,S=void 0,T=void 0,U=void 0,V=void 0,W=void 0,X=void 0,Y=void 0,Z=void 0,$=void 0,_=void 0,aa=void 0,ba=void 0,ca=void 0,da=void 0,ea=void 0,fa=void 0,ga=void 0,ha=void 0,ia=void 0,ja=void 0,ka=void 0;return K={initialize:d,append:y,abort:f,addEmbeddedTrack:q,resetEmbedded:o,setConfig:r,getConfig:s,setCurrentFragmentedTrackIdx:u,remove:G,reset:i},a(),K}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(183),i=a(175),j=d(i),k=a(71),l=d(k),m=a(146),n=d(m),o=a(148),p=d(o),q=a(47),r=d(q),s=a(45),t=d(s),u=a(142),v=d(u),w=a(137),x=d(w),y=a(5),z=d(y),A=a(2),B=d(A),C=a(46),D=d(C),E=a(50),F=d(E);e.__dashjs_factory_name="TextSourceBuffer",c.default=r.default.getSingletonFactory(e),b.exports=c.default},{137:137,142:142,146:146,148:148,175:175,183:183,2:2,45:45,46:46,47:47,5:5,50:50,71:71,98:98}],142:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){H=(0,o.default)(E).getInstance().getLogger(G)}function b(){"undefined"!=typeof window&&"undefined"!=typeof navigator&&(I=window.VTTCue||window.TextTrackCue,K=[],L=[],M=-1,N=0,O=0,P=0,Q=0,R=null,S=null,U=!1,W=2147483647,V=null,void 0!==document.fullscreenElement?T="fullscreenElement":void 0!==document.webkitIsFullScreen?T="webkitIsFullScreen":document.msFullscreenElement?T="msFullscreenElement":document.mozFullScreen&&(T="mozFullScreen"))}function c(a){var b=K[a].kind,c=void 0!==K[a].label?K[a].label:K[a].lang,d=K[a].lang,e=K[a].isTTML,f=K[a].isEmbedded,g=J.addTextTrack(b,c,d);return g.isEmbedded=f,g.isTTML=e,g}function d(a){U=a,R&&!document[T]&&(R.style.zIndex=a?W:null)}function e(a,b){if(K.length===b)return void H.error("Trying to add too many tracks.");if(K.push(a),K.length===b){K.sort(function(a,b){return a.index-b.index}),R=J.getTTMLRenderingDiv();for(var d=-1,e=0;e=0)for(var i=0;ih?(j=b,i=j/d*c):(i=a,j=i/c*d);var k=0,l=0,m=0,n=0;return i/j>e?(n=j,m=j*e):(m=i,n=i/e),k=(a-m)/2,l=(b-n)/2,f?{x:k+.1*m,y:l+.1*n,w:.8*m,h:.8*n}:{x:k,y:l,w:m,h:n}}function h(a,b){var c=J.getClientWidth(),d=J.getClientHeight(),e=J.getVideoWidth(),g=J.getVideoHeight(),h=J.getVideoRelativeOffsetTop(),i=J.getVideoRelativeOffsetLeft(),j=e/g,l=!1;a.isFromCEA608&&(j=3.5/3,l=!0);var m=f.call(this,c,d,e,g,j,l),n=m.w,o=m.h,p=m.x,q=m.y;if(n!=P||o!=Q||p!=N||q!=O||b){if(N=p+i,O=q+h,P=n,Q=o,R){var r=R.style;r.left=N+"px",r.top=O+"px",r.width=P+"px",r.height=Q+"px",r.zIndex=T&&document[T]||U?W:null,F.trigger(k.default.CAPTION_CONTAINER_RESIZE,{})}var s=a.activeCues;if(s)for(var t=s.length,u=0;u=0&&K[a]?J.getTextTrack(K[a].kind,K[a].label,K[a].lang,K[a].isTTML,K[a].isEmbedded):null}function q(){return M}function r(a){for(var b=-1,c=0;c=0;d--)a.removeCue(b[d])}function v(a){var b=n(a);b&&u(b)}function w(){for(var a=L?L.length:0,b=0;b0&&e.forEach(function(a){a.segmentInfoType===i.default.SEGMENT_TEMPLATE&&a.segmentDuration>0&&a.media&&d(a)}),x.length>0&&(x.sort(function(a,b){return a.bitrate-b.bitrate}),y=x.length-1)}}}}}function d(a){var b=new m.default;b.id=a.id,b.bitrate=a.bandwidth,b.width=a.width,b.height=a.height,b.tilesHor=1,b.tilesVert=1,b.startNumber=a.startNumber,b.segmentDuration=a.segmentDuration,b.timescale=a.timescale,b.templateUrl=e(a),a.essentialProperties&&a.essentialProperties.forEach(function(a){if(a.schemeIdUri===q&&a.value){var c=a.value.split("x");2!==c.length||isNaN(c[0])||isNaN(c[1])||(b.tilesHor=parseInt(c[0],10),b.tilesVert=parseInt(c[1],10))}}),b.tilesHor>0&&b.tilesVert>0&&(b.widthPerTile=b.width/b.tilesHor,b.heightPerTile=b.height/b.tilesVert,x.push(b))}function e(a){var b=v.isRelative(a.media)?v.resolve(a.media,t.resolve(a.path).url):a.media;return b?(0,p.replaceIDForTemplate)(b,a.id):""}function f(){return x}function h(){return y}function j(){return y<0?null:x[y]}function k(a){x&&0!==x.length&&(a>=x.length&&(a=x.length-1),y=a)}function l(){x=[],y=-1}var n=this.context,r=a.dashManifestModel,s=a.adapter,t=a.baseURLController,u=a.stream,v=(0,o.default)(n).getInstance(),w=void 0,x=void 0,y=void 0;return w={initialize:b,getTracks:f,reset:l,setTrackByIndex:k,getCurrentTrack:j,getCurrentTrackIndex:h},b(),w}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(57),i=d(h),j=a(47),k=d(j),l=a(177),m=d(l),n=a(158),o=d(n),p=a(75),q="http://dashif.org/thumbnail_tile";e.__dashjs_factory_name="ThumbnailTracks",c.default=k.default.getClassFactory(e),b.exports=c.default},{158:158,177:177,47:47,57:57,75:75,98:98}],145:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){p=(0,k.default)(h).create({updateEventName:i.default.SERVICE_LOCATION_BLACKLIST_CHANGED,addBlacklistEventName:i.default.SERVICE_LOCATION_BLACKLIST_ADD}),q=(0,o.default)(h).create({blacklistController:p}),t=(0,m.default)(h).create({blacklistController:p}),u=q}function b(a){a.selector&&(u=a.selector),a.dashManifestModel&&(l=a.dashManifestModel)}function c(){if(!l||!l.hasOwnProperty("getIsDVB"))throw new Error("Missing config parameter(s)")}function d(a){c(),u=l.getIsDVB(a)?t:q}function e(a){var b=a.baseUrls,c=a.selectedIdx;if(!isNaN(c))return b[c];var d=u.select(b);return d?(a.selectedIdx=b.indexOf(d),d):(j.trigger(i.default.URL_RESOLUTION_FAILED,{error:new Error(r,s)}),void(u===q&&f()))}function f(){p.reset()}var h=this.context,j=(0,g.default)(h).getInstance(),l=void 0,n=void 0,p=void 0,q=void 0,t=void 0,u=void 0;return n={chooseSelectorFromManifest:d,select:e,reset:f,setConfig:b},a(),n}Object.defineProperty(c,"__esModule",{value:!0});var f=a(46),g=d(f),h=a(50),i=d(h),j=a(102),k=d(j),l=a(161),m=d(l),n=a(160),o=d(n),p=a(47),q=d(p),r=1,s="Failed to resolve a valid URL";e.__dashjs_factory_name="BaseURLSelector";var t=q.default.getClassFactory(e);t.URL_RESOLUTION_FAILED_GENERIC_ERROR_CODE=r,t.URL_RESOLUTION_FAILED_GENERIC_ERROR_MESSAGE=s,q.default.updateClassFactory(e.__dashjs_factory_name,t),c.default=t,b.exports=c.default},{102:102,160:160,161:161,46:46,47:47,50:50}],146:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){if(!a)return null;void 0===a.fileStart&&(a.fileStart=0);var b=k.default.parseBuffer(a),c=(0,g.default)(e).create();return c.setData(b),c}function b(a,b,e){if(void 0===e&&(e=0),!b||e+8>=b.byteLength)return new m.default(0,!1);for(var f=b instanceof ArrayBuffer?new Uint8Array(b):b,g=void 0,h=0;e=0?g=new m.default(e,!0,i):h=e+i),e+=i}return g||new m.default(h,!1)}function c(a,b){return a[b+3]>>>0|a[b+2]<<8>>>0|a[b+1]<<16>>>0|a[b]<<24>>>0}function d(a,b){return String.fromCharCode(a[b++])+String.fromCharCode(a[b++])+String.fromCharCode(a[b++])+String.fromCharCode(a[b])}var e=this.context;return{parse:a,findLastTopIsoBoxCompleted:b}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(153),g=d(f),h=a(47),i=d(h),j=a(5),k=d(j),l=a(168),m=d(l);e.__dashjs_factory_name="BoxParser",c.default=i.default.getSingletonFactory(e),b.exports=c.default},{153:153,168:168,47:47,5:5}],147:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){g=!1}function b(){var a="WebKitMediaSource"in window,b="MediaSource"in window;return a||b}function c(){return g}function d(a){g=a}function e(a){return!!("MediaSource"in window&&MediaSource.isTypeSupported(a))||!!("WebKitMediaSource"in window&&WebKitMediaSource.isTypeSupported(a))}var f=void 0,g=void 0;return f={supportsMediaSource:b,supportsEncryptedMedia:c,supportsCodec:e,setEncryptedMediaSupported:d},a(),f}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="Capabilities",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47}],148:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){var c=0;for(c=0;cthis.customTimeRangeArray[c].start;c++);for(this.customTimeRangeArray.splice(c,0,{start:a,end:b}),c=0;c=this.customTimeRangeArray[c].end)this.customTimeRangeArray.splice(c,1),c--;else{if(a>this.customTimeRangeArray[c].start&&bthis.customTimeRangeArray[c].start&&athis.customTimeRangeArray[c].start&&b=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].start}function g(a){return e(a),a>=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].end}return{customTimeRangeArray:[],length:0,add:a,clear:b,remove:c,mergeRanges:d,start:f,end:g}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="CustomTimeRanges",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],149:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){w=(0,i.default)(t).getInstance().getLogger(v),d()}function c(a){if(void 0!==x)return x;x=!1;var b="1",c="1",d=void 0;try{"undefined"!=typeof window&&(d=window[a])}catch(e){return w.warn("DOMStorage access denied: "+e.message),x}if(!d||a!==m&&a!==n)return x;try{d.setItem(b,c),d.removeItem(b),x=!0}catch(e){w.warn("DOMStorage is supported, but cannot be used: "+e.message)}return x}function d(){c(m)&&j.forEach(function(a){var b=localStorage.getItem(a.oldKey);if(b){localStorage.removeItem(a.oldKey);try{localStorage.setItem(a.newKey,b)}catch(c){w.error(c.message)}}})}function e(){var a=6e5;return Math.round((new Date).getTime()/a)*a}function f(a,b){return c(a)&&u["get"+b+"CachingInfo"]().enabled}function g(){if(!u||!u.hasOwnProperty("getLastMediaSettingsCachingInfo"))throw new Error("Missing config parameter(s)")}function h(a){if(g(),!f(m,p))return null;var b=null,c=l.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1;b=d.settings,e&&(localStorage.removeItem(c),b=null)}catch(h){return null}return b}function q(a){var b=NaN;if(g(),f(m,o)){var c=k.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1,h=parseFloat(d.bitrate);isNaN(h)||e?e&&localStorage.removeItem(c):(b=h,w.debug("Last saved bitrate for "+a+" was "+h))}catch(i){return null}}return b}function r(a,b){if(f(m,p)){var c=l.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({settings:b,timestamp:e()}))}catch(d){w.error(d.message)}}}function s(a,b){if(f(m,o)&&b){var c=k.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({bitrate:b.toFixed(3),timestamp:e()}))}catch(d){w.error(d.message)}}}a=a||{};var t=this.context,u=a.mediaPlayerModel,v=void 0,w=void 0,x=void 0;return v={getSavedBitrateSettings:q,setSavedBitrateSettings:s,getSavedMediaSettings:h,setSavedMediaSettings:r},b(),v}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(45),i=d(h),j=[{oldKey:"dashjs_vbitrate",newKey:"dashjs_video_bitrate"},{oldKey:"dashjs_abitrate",newKey:"dashjs_audio_bitrate"},{oldKey:"dashjs_vsettings",newKey:"dashjs_video_settings"},{oldKey:"dashjs_asettings",newKey:"dashjs_audio_settings"}],k="dashjs_?_bitrate",l="dashjs_?_settings",m="localStorage",n="sessionStorage",o="LastBitrate",p="LastMediaSettings";e.__dashjs_factory_name="DOMStorage";var q=g.default.getSingletonFactory(e);c.default=q,b.exports=c.default},{45:45,47:47}],150:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){return n}function c(a){n=a}function d(a,b){var c=!0,d=0,e=void 0,f=void 0;if(void 0===b&&(b=!1),a.tag>16777215?(m.getUint32(n)!==a.tag&&(c=!1),d=4):a.tag>65535?(e=m.getUint16(n),f=m.getUint8(n+2),256*e+f!==a.tag&&(c=!1),d=3):a.tag>255?(m.getUint16(n)!==a.tag&&(c=!1),d=2):(m.getUint8(n)!==a.tag&&(c=!1),d=1),!c&&a.required&&!b)throw new Error("required tag not found");return c&&(n+=d),c}function e(a,b){var c=d(a,b);return c&&h(),c}function f(a){var b=void 0;return d(a),b=h(),l[a.parse](b)}function g(a,b){var c=d(a,b),e=void 0;return c&&(e=h(),n+=e),c}function h(a){var b=1,c=128,d=8,e=-1,f=0,g=m.getUint8(n),h=0;for(h=0;h>=1}for(h=0;h0?c[c.length-1]:null}function e(a){if(!a)return null;var b=new g.default(a);return a.hasOwnProperty("_incomplete")&&(b.isComplete=!a._incomplete),b}var f=void 0;return{getBox:a,getBoxes:b,setData:c,getLastBox:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(167),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="IsoFile",c.default=i.default.getClassFactory(e),b.exports=c.default},{167:167,47:47}],154:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){if(!(e&&e.hasOwnProperty("getExpectedLiveEdge")&&f&&f.hasOwnProperty("getCurrentRepresentationInfo")))throw new Error("Missing config parameter(s)")}function c(){b();var a=f.getCurrentRepresentationInfo(),c=a.DVRWindow.end;return a.useCalculatedLiveEdgeTime&&(c=e.getExpectedLiveEdge(),e.setClientTimeOffset(c-a.DVRWindow.end)),c}function d(){e=null,f=null}a=a||{};var e=a.timelineConverter,f=a.streamProcessor;return{getLiveEdge:c,reset:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="LiveEdgeFinder",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],155:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){return(0,i.default)(a,b)}return{areEqual:a}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(6),i=d(h);e.__dashjs_factory_name="ObjectUtils",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47,6:6}],156:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){return a}function b(a){return a}return{modifyRequestURL:a,modifyRequestHeader:b}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="RequestModifier",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47}],157:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){g=(0,i.default)(d).getInstance().getLogger(f)}function b(){var a="cue_TTML_"+h;return h++,a}function c(a,c,d,f,h){var i=void 0,j="",k=[],l=void 0,o=void 0,p={},q={},r="",s="",t={onOpenTag:function(a,b,c){if("image"===b&&"http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt"===a){if(!c[" imagetype"]||"PNG"!==c[" imagetype"].value)return void g.warn("smpte-tt imagetype != PNG. Discarded");r=c["http://www.w3.org/XML/1998/namespace id"].value}},onCloseTag:function(){r&&(q[r]=s.trim()),s="",r=""},onText:function(a){r&&(s+=a)}};if(!a)throw j="no ttml data to parse",new Error(j);p.data=a,e.trigger(m.default.TTML_TO_PARSE,p);var u=(0,n.fromXML)(p.data,function(a){j=a},t);e.trigger(m.default.TTML_PARSED,{ttmlString:p.data,ttmlDoc:u});var v=u.getMediaTimeEvents();for(i=0;if?f:v[i+1]+c,l/,n=/(^[\s]+|[\s]+$)/g,o=/\s\b/g}function b(a){var b=[],e=void 0,g=void 0;if(!a)return b;a=a.split(l),e=a.length,g=-1;for(var h=0;h0&&i!==j&&i.match(m)){var o=d(i),p=o.cuePoints,q=o.styles,r=f(a,h+1),s=c(p[0].replace(n,"")),t=c(p[1].replace(n,""));!isNaN(s)&&!isNaN(t)&&s>=g&&t>s?""!==r?(g=s,b.push({start:s,end:t,data:r,styles:q})):k.error("Skipping cue due to empty/malformed cue text"):k.error("Skipping cue due to incorrect cue timing")}}return b}function c(a){var b=a.split(":"),c=b.length-1;return a=60*parseInt(b[c-1],10)+parseFloat(b[c]),2===c&&(a+=3600*parseInt(b[0],10)),a}function d(a){var b=a.split(m),c=b[1].split(o);return c.shift(),b[1]=c[0],c.shift(),{cuePoints:b,styles:e(c)}}function e(a){var b={};return a.forEach(function(a){if(a.split(/:/).length>1){var c=a.split(/:/)[1];c&&-1!=c.search(/%/)&&(c=parseInt(c.replace(/%/,""),10)),(a.match(/align/)||a.match(/A/))&&(b.align=c),(a.match(/line/)||a.match(/L/))&&(b.line=c),(a.match(/position/)||a.match(/P/))&&(b.position=c),(a.match(/size/)||a.match(/S/))&&(b.size=c)}}),b}function f(a,b){for(var c=b,d="",e="",f=void 0;""!==a[c]&&c1)for(var g=0;g1&&(h.forEach(function(a){d+=a.dvb_weight,e.push(d)}),g=Math.floor(Math.random()*(d-1)),e.every(function(a,b){return f=b,!(g=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].start}function g(a){return e(a),a>=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].end}return{customTimeRangeArray:[],length:0,add:a,clear:b,remove:c,mergeRanges:d,start:f,end:g}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="CustomTimeRanges",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],149:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){w=(0,i.default)(t).getInstance().getLogger(v),d()}function c(a){if(void 0!==x)return x;x=!1;var b="1",c="1",d=void 0;try{"undefined"!=typeof window&&(d=window[a])}catch(e){return w.warn("DOMStorage access denied: "+e.message),x}if(!d||a!==m&&a!==n)return x;try{d.setItem(b,c),d.removeItem(b),x=!0}catch(e){w.warn("DOMStorage is supported, but cannot be used: "+e.message)}return x}function d(){c(m)&&j.forEach(function(a){var b=localStorage.getItem(a.oldKey);if(b){localStorage.removeItem(a.oldKey);try{localStorage.setItem(a.newKey,b)}catch(c){w.error(c.message)}}})}function e(){var a=6e5;return Math.round((new Date).getTime()/a)*a}function f(a,b){return c(a)&&u["get"+b+"CachingInfo"]().enabled}function g(){if(!u||!u.hasOwnProperty("getLastMediaSettingsCachingInfo"))throw new Error("Missing config parameter(s)")}function h(a){if(g(),!f(m,p))return null;var b=null,c=l.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1;b=d.settings,e&&(localStorage.removeItem(c),b=null)}catch(h){return null}return b}function q(a){var b=NaN;if(g(),f(m,o)){var c=k.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1,h=parseFloat(d.bitrate);isNaN(h)||e?e&&localStorage.removeItem(c):(b=h,w.debug("Last saved bitrate for "+a+" was "+h))}catch(i){return null}}return b}function r(a,b){if(f(m,p)){var c=l.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({settings:b,timestamp:e()}))}catch(d){w.error(d.message)}}}function s(a,b){if(f(m,o)&&b){var c=k.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({bitrate:b.toFixed(3),timestamp:e()}))}catch(d){w.error(d.message)}}}a=a||{};var t=this.context,u=a.mediaPlayerModel,v=void 0,w=void 0,x=void 0;return v={getSavedBitrateSettings:q,setSavedBitrateSettings:s,getSavedMediaSettings:h,setSavedMediaSettings:r},b(),v}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(45),i=d(h),j=[{oldKey:"dashjs_vbitrate",newKey:"dashjs_video_bitrate"},{oldKey:"dashjs_abitrate",newKey:"dashjs_audio_bitrate"},{oldKey:"dashjs_vsettings",newKey:"dashjs_video_settings"},{oldKey:"dashjs_asettings",newKey:"dashjs_audio_settings"}],k="dashjs_?_bitrate",l="dashjs_?_settings",m="localStorage",n="sessionStorage",o="LastBitrate",p="LastMediaSettings";e.__dashjs_factory_name="DOMStorage";var q=g.default.getSingletonFactory(e);c.default=q,b.exports=c.default},{45:45,47:47}],150:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){return n}function c(a){n=a}function d(a,b){var c=!0,d=0,e=void 0,f=void 0;if(void 0===b&&(b=!1),a.tag>16777215?(m.getUint32(n)!==a.tag&&(c=!1),d=4):a.tag>65535?(e=m.getUint16(n),f=m.getUint8(n+2),256*e+f!==a.tag&&(c=!1),d=3):a.tag>255?(m.getUint16(n)!==a.tag&&(c=!1),d=2):(m.getUint8(n)!==a.tag&&(c=!1),d=1),!c&&a.required&&!b)throw new Error("required tag not found");return c&&(n+=d),c}function e(a,b){var c=d(a,b);return c&&h(),c}function f(a){var b=void 0;return d(a),b=h(),l[a.parse](b)}function g(a,b){var c=d(a,b),e=void 0;return c&&(e=h(),n+=e),c}function h(a){var b=1,c=128,d=8,e=-1,f=0,g=m.getUint8(n),h=0;for(h=0;h>=1}for(h=0;h0?c[c.length-1]:null}function e(a){if(!a)return null;var b=new g.default(a);return a.hasOwnProperty("_incomplete")&&(b.isComplete=!a._incomplete),b}var f=void 0;return{getBox:a,getBoxes:b,setData:c,getLastBox:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(167),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="IsoFile",c.default=i.default.getClassFactory(e),b.exports=c.default},{167:167,47:47}],154:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){if(!(e&&e.hasOwnProperty("getExpectedLiveEdge")&&f&&f.hasOwnProperty("getCurrentRepresentationInfo")))throw new Error("Missing config parameter(s)")}function c(){b();var a=f.getCurrentRepresentationInfo(),c=a.DVRWindow.end;return a.useCalculatedLiveEdgeTime&&(c=e.getExpectedLiveEdge(),e.setClientTimeOffset(c-a.DVRWindow.end)),c}function d(){e=null,f=null}a=a||{};var e=a.timelineConverter,f=a.streamProcessor;return{getLiveEdge:c,reset:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="LiveEdgeFinder",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],155:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){return(0,i.default)(a,b)}return{areEqual:a}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(6),i=d(h);e.__dashjs_factory_name="ObjectUtils",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47,6:6}],156:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){return a}function b(a){return a}return{modifyRequestURL:a,modifyRequestHeader:b}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="RequestModifier",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47}],157:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){g=(0,i.default)(d).getInstance().getLogger(f)}function b(){var a="cue_TTML_"+h;return h++,a}function c(a,c,d,f,h){var i=void 0,j="",k=[],l=void 0,o=void 0,p={},q={},r="",s="",t={onOpenTag:function(a,b,c){if("image"===b&&"http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt"===a){if(!c[" imagetype"]||"PNG"!==c[" imagetype"].value)return void g.warn("smpte-tt imagetype != PNG. Discarded");r=c["http://www.w3.org/XML/1998/namespace id"].value}},onCloseTag:function(){r&&(q[r]=s.trim()),s="",r=""},onText:function(a){r&&(s+=a)}};if(!a)throw j="no ttml data to parse",new Error(j);p.data=a,e.trigger(m.default.TTML_TO_PARSE,p);var u=(0,n.fromXML)(p.data,function(a){j=a},t);e.trigger(m.default.TTML_PARSED,{ttmlString:p.data,ttmlDoc:u});var v=u.getMediaTimeEvents();for(i=0;if?f:v[i+1]+c,l/,n=/(^[\s]+|[\s]+$)/g,o=/\s\b/g}function b(a){var b=[],e=void 0,g=void 0;if(!a)return b;a=a.split(l),e=a.length,g=-1;for(var h=0;h0&&i!==j&&i.match(m)){var o=d(i),p=o.cuePoints,q=o.styles,r=f(a,h+1),s=c(p[0].replace(n,"")),t=c(p[1].replace(n,""));!isNaN(s)&&!isNaN(t)&&s>=g&&t>s?""!==r?(g=s,b.push({start:s,end:t,data:r,styles:q})):k.error("Skipping cue due to empty/malformed cue text"):k.error("Skipping cue due to incorrect cue timing")}}return b}function c(a){var b=a.split(":"),c=b.length-1;return a=60*parseInt(b[c-1],10)+parseFloat(b[c]),2===c&&(a+=3600*parseInt(b[0],10)),a}function d(a){var b=a.split(m),c=b[1].split(o);return c.shift(),b[1]=c[0],c.shift(),{cuePoints:b,styles:e(c)}}function e(a){var b={};return a.forEach(function(a){if(a.split(/:/).length>1){var c=a.split(/:/)[1];c&&-1!=c.search(/%/)&&(c=parseInt(c.replace(/%/,""),10)),(a.match(/align/)||a.match(/A/))&&(b.align=c),(a.match(/line/)||a.match(/L/))&&(b.line=c),(a.match(/position/)||a.match(/P/))&&(b.position=c),(a.match(/size/)||a.match(/S/))&&(b.size=c)}}),b}function f(a,b){for(var c=b,d="",e="",f=void 0;""!==a[c]&&c1)for(var g=0;g1&&(h.forEach(function(a){d+=a.dvb_weight,e.push(d)}),g=Math.floor(Math.random()*(d-1)),e.every(function(a,b){return f=b,!(g=0&&u0?0:a-1;return arguments.length<3&&(i=e[o?o[c]:c],c+=n),t(e,r,i,o,c,a)}}function r(n){return function(t,e,r){e=x(e,r);for(var i=T(t),u=n>0?0:i-1;u>=0&&u0?o=u>=0?u:Math.max(u+a,o):a=u>=0?Math.min(u+1,a):u+a+1;else if(e&&u&&a)return u=e(r,i),r[u]===i?u:-1;if(i!==i)return(u=t(p.call(r,o,a),m.isNaN))>=0?u+o:-1;for(u=n>0?o:a-1;u>=0&&u=0&&t<=E};m.each=m.forEach=function(n,t,e){t=b(t,e);var r,i;if(A(n))for(r=0,i=n.length;r=0},m.invoke=function(n,t){var e=p.call(arguments,2),r=m.isFunction(t);return m.map(n,function(n){var i=r?t:n[t];return null==i?i:i.apply(n,e)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,e){var r,i,u=-1/0,o=-1/0;if(null==t&&null!=n)for(var a=0,c=(n=A(n)?n:m.values(n)).length;au&&(u=r);else t=x(t,e),m.each(n,function(n,e,r){((i=t(n,e,r))>o||i===-1/0&&u===-1/0)&&(u=n,o=i)});return u},m.min=function(n,t,e){var r,i,u=1/0,o=1/0;if(null==t&&null!=n)for(var a=0,c=(n=A(n)?n:m.values(n)).length;ar||void 0===e)return 1;if(et?(o&&(clearTimeout(o),o=null),a=s,u=n.apply(r,i),o||(r=i=null)):o||!1===e.trailing||(o=setTimeout(c,l)),u}},m.debounce=function(n,t,e){var r,i,u,o,a,c=function(){var s=m.now()-o;s=0?r=setTimeout(c,t-s):(r=null,e||(a=n.apply(u,i),r||(u=i=null)))};return function(){u=this,i=arguments,o=m.now();var s=e&&!r;return r||(r=setTimeout(c,t)),s&&(a=n.apply(u,i),u=i=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var e=t,r=n[t].apply(this,arguments);e--;)r=n[e].call(this,r);return r}},m.after=function(n,t){return function(){if(--n<1)return t.apply(this,arguments)}},m.before=function(n,t){var e;return function(){return--n>0&&(e=t.apply(this,arguments)),n<=1&&(t=null),e}},m.once=m.partial(m.before,2);var F=!{toString:null}.propertyIsEnumerable("toString"),q=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(d)return d(n);var t=[];for(var e in n)m.has(n,e)&&t.push(e);return F&&u(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var e in n)t.push(e);return F&&u(n,t),t},m.values=function(n){for(var t=m.keys(n),e=t.length,r=Array(e),i=0;i":">",'"':""","'":"'","`":"`"},N=m.invert(M),R=function(n){var t=function(t){return n[t]},e="(?:"+m.keys(n).join("|")+")",r=RegExp(e),i=RegExp(e,"g");return function(n){return n=null==n?"":""+n,r.test(n)?n.replace(i,t):n}};m.escape=R(M),m.unescape=R(N),m.result=function(n,t,e){var r=null==n?void 0:n[t];return void 0===r&&(r=e),m.isFunction(r)?r.call(n):r};var L=0;m.uniqueId=function(n){var t=++L+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var U=/(.)^/,D={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},W=/\\|'|\r|\n|\u2028|\u2029/g,P=function(n){return"\\"+D[n]};m.template=function(n,t,e){!t&&e&&(t=e),t=m.defaults({},t,m.templateSettings);var r=RegExp([(t.escape||U).source,(t.interpolate||U).source,(t.evaluate||U).source].join("|")+"|$","g"),i=0,u="__p+='";n.replace(r,function(t,e,r,o,a){return u+=n.slice(i,a).replace(W,P),i=a+t.length,e?u+="'+\n((__t=("+e+"))==null?'':_.escape(__t))+\n'":r?u+="'+\n((__t=("+r+"))==null?'':__t)+\n'":o&&(u+="';\n"+o+"\n__p+='"),t}),u+="';\n",t.variable||(u="with(obj||{}){\n"+u+"}\n"),u="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+u+"return __p;\n";try{var o=new Function(t.variable||"obj","_",u)}catch(n){throw n.source=u,n}var a=function(n){return o.call(this,n,m)},c=t.variable||"obj";return a.source="function("+c+"){\n"+u+"}",a},m.chain=function(n){var t=m(n);return t._chain=!0,t};var B=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var e=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),B(this,e.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=c[n];m.prototype[n]=function(){var e=this._wrapped;return t.apply(e,arguments),"shift"!==n&&"splice"!==n||0!==e.length||delete e[0],B(this,e)}}),m.each(["concat","join","slice"],function(n){var t=c[n];m.prototype[n]=function(){return B(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this)},{}],3:[function(n,t,e){"use strict";var r=n("underscore"),i=n("../events");t.exports=function(n){var t=n.getComponent("MenuItem");return n.extend(t,{constructor:function(n,e){var i=e.source;if(!r.isObject(i))throw new Error('was not provided a "source" object, but rather: '+typeof i);e=r.extend({selectable:!0,label:i.label},e),t.call(this,n,e),this.source=i},handleClick:function(n){t.prototype.handleClick.call(this,n),this.player().trigger(i.QUALITY_REQUESTED,this.source)}})}},{"../events":5,underscore:2}],4:[function(n,t,e){"use strict";var r=n("underscore"),i=n("../events"),u=n("./QualityOption");t.exports=function(n){var t,e=n.getComponent("MenuButton"),o=u(n);return t=n.extend(e,{constructor:function(n,t){e.call(this,n,t),n.on(i.QUALITY_REQUESTED,function(t,e){this.setSelectedSource(e),n.addClass("vjs-quality-changing"),n.one("loadeddata",function(){n.removeClass("vjs-quality-changing")})}.bind(this)),n.on(i.QUALITY_SELECTED,function(n,t){this.setSelectedSource(t)}.bind(this)),n.one("ready",function(){this.selectedSrc=n.src(),this.update()}.bind(this)),this.controlText("Open quality selector menu")},setSelectedSource:function(n){var t=n?n.src:void 0;this.selectedSrc!==t&&(this.selectedSrc=t,this.update())},createItems:function(){var n=this.player(),t=n.currentSources();return r.map(t,function(t){return new o(n,{source:t,selected:t.src===this.selectedSrc})}.bind(this))},buildWrapperCSSClass:function(){return"vjs-quality-selector "+e.prototype.buildWrapperCSSClass.call(this)}}),n.registerComponent("QualitySelector",t),t}},{"../events":5,"./QualityOption":3,underscore:2}],5:[function(n,t,e){"use strict";t.exports={QUALITY_REQUESTED:"qualityRequested",QUALITY_SELECTED:"qualitySelected"}},{}],6:[function(n,t,e){"use strict";var r=n("underscore"),i=n("./events"),u=n("./components/QualitySelector"),o=n("./middleware/SourceInterceptor"),a=n("./util/SafeSeek");t.exports=function(n){n=n||window.videojs,u(n),o(n),n.hook("setup",function(n){n.on(i.QUALITY_REQUESTED,function(t,e){var i=n.currentSources(),u=n.currentTime(),o=(n.playbackRate(),n.paused());r.each(i,function(n){n.selected=!1}),r.findWhere(i,{src:e.src}).selected=!0,n._qualitySelectorSafeSeek&&n._qualitySelectorSafeSeek.onQualitySelectionChange(),n.src(i),n.ready(function(){n._qualitySelectorSafeSeek&&!n._qualitySelectorSafeSeek.hasFinished()||(n._qualitySelectorSafeSeek=new a(n,u),n.playbackRate=playbackRate),o||n.play()})})})},t.exports.EVENTS=i},{"./components/QualitySelector":4,"./events":5,"./middleware/SourceInterceptor":7,"./util/SafeSeek":9,underscore:2}],7:[function(n,t,e){"use strict";var r=n("underscore"),i=n("../events");t.exports=function(n){n.use("*",function(n){return{setSource:function(t,e){var u,o=n.currentSources();n._qualitySelectorSafeSeek&&n._qualitySelectorSafeSeek.onPlayerSourcesChange(),u=r.find(o,function(n){return!0===n.selected||"true"===n.selected})||t,n.trigger(i.QUALITY_SELECTED,u),e(null,u)}}})}},{"../events":5,underscore:2}],8:[function(n,t,e){"use strict";n("./index")()},{"./index":6}],9:[function(n,t,e){"use strict";var r=n("class.extend");t.exports=r.extend({init:function(n,t){this._player=n,this._seekToTime=t,this._hasFinished=!1,this._keepThisInstanceWhenPlayerSourcesChange=!1,this._seekWhenSafe()},_seekWhenSafe:function(){this._player.readyState()<3?(this._seekFn=this._seek.bind(this),this._player.one("canplay",this._seekFn)):this._seek()},onPlayerSourcesChange:function(){this._keepThisInstanceWhenPlayerSourcesChange?this._keepThisInstanceWhenPlayerSourcesChange=!1:this.cancel()},onQualitySelectionChange:function(){this.hasFinished()||(this._keepThisInstanceWhenPlayerSourcesChange=!0)},_seek:function(){this._player.currentTime(this._seekToTime),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0},hasFinished:function(){return this._hasFinished},cancel:function(){this._player.off("canplay",this._seekFn),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0}})},{"class.extend":1}]},{},[8]); \ No newline at end of file From bc1e62ce5162aa3a7c7b13c7381b4f73f50ce642 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 11:37:52 -0600 Subject: [PATCH 04/19] Add 'external_port' --- src/invidious/helpers/helpers.cr | 1 + src/invidious/helpers/utils.cr | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 3cc4d9cf..075bf84e 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -24,6 +24,7 @@ user: String, registration_enabled: {type: Bool, default: true}, statistics_enabled: {type: Bool, default: false}, admins: {type: Array(String), default: [] of String}, + external_port: {type: Int32 | Nil, default: nil}, }) end diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index c200801f..3123b1d2 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -195,6 +195,7 @@ end def make_host_url(config, kemal_config) ssl = config.https_only || kemal_config.ssl + port = config.external_port || kemal_config.port if ssl scheme = "https://" @@ -202,7 +203,8 @@ def make_host_url(config, kemal_config) scheme = "http://" end - if kemal_config.port != 80 && kemal_config.port != 443 + # Add if non-standard port + if port != 80 && port != 443 port = ":#{kemal_config.port}" else port = "" From 64e4791dca6186ca15e98afc85076a4fe5e6f377 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 12:01:31 -0600 Subject: [PATCH 05/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20f509aa..fd7d452e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Invidious is an alternative front-end to YouTube - Audio-only mode (and no need to keep window open on mobile) -- [Open-source](https://github.com/omarroth/invidious) (AGPLv3 licensed) +- [Free software(https://github.com/omarroth/invidious) (AGPLv3 licensed) - No ads - No need to create a Google account to save subscriptions - Lightweight (homepage is ~4 KB compressed) From c69fbb72d3bb7d18bf6f8acb48b26b54b8598710 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 12:01:43 -0600 Subject: [PATCH 06/19] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd7d452e..d8aaef47 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Invidious is an alternative front-end to YouTube - Audio-only mode (and no need to keep window open on mobile) -- [Free software(https://github.com/omarroth/invidious) (AGPLv3 licensed) +- [Free software](https://github.com/omarroth/invidious) (AGPLv3 licensed) - No ads - No need to create a Google account to save subscriptions - Lightweight (homepage is ~4 KB compressed) From c4d77bc18a726941823dfcc6a0b8e2159d73517e Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 14:42:37 -0600 Subject: [PATCH 07/19] Use host_url for generating thumbnails --- src/invidious.cr | 32 ++++++++++++++++---------------- src/invidious/videos.cr | 32 +++++++++++++++++--------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index f5c97c13..2d1d88ce 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2826,7 +2826,7 @@ get "/api/v1/videos/:id" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end video.description, description = html_to_content(video.description) @@ -2989,7 +2989,7 @@ get "/api/v1/videos/:id" do |env| json.field "videoId", rv["id"] json.field "title", rv["title"] json.field "videoThumbnails" do - generate_thumbnails(json, rv["id"]) + generate_thumbnails(json, rv["id"], config, Kemal.config) end json.field "author", rv["author"] json.field "lengthSeconds", rv["length_seconds"].to_i @@ -3031,7 +3031,7 @@ get "/api/v1/trending" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "lengthSeconds", video.length_seconds @@ -3072,7 +3072,7 @@ get "/api/v1/popular" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "lengthSeconds", video.length_seconds @@ -3111,7 +3111,7 @@ get "/api/v1/top" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "lengthSeconds", video.info["length_seconds"].to_i @@ -3294,7 +3294,7 @@ get "/api/v1/channels/:ucid" do |env| end json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "description", video.description @@ -3392,7 +3392,7 @@ end end json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "description", video.description @@ -3444,7 +3444,7 @@ end json.field "authorUrl", "/channel/#{ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "description", video.description @@ -3515,7 +3515,7 @@ end json.field "lengthSeconds", video.length_seconds json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end end @@ -3568,7 +3568,7 @@ get "/api/v1/channels/search/:ucid" do |env| json.field "authorUrl", "/channel/#{item.ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, item.id) + generate_thumbnails(json, item.id, config, Kemal.config) end json.field "description", item.description @@ -3600,7 +3600,7 @@ get "/api/v1/channels/search/:ucid" do |env| json.field "lengthSeconds", video.length_seconds json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end end @@ -3697,7 +3697,7 @@ get "/api/v1/search" do |env| json.field "authorUrl", "/channel/#{item.ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, item.id) + generate_thumbnails(json, item.id, config, Kemal.config) end json.field "description", item.description @@ -3729,7 +3729,7 @@ get "/api/v1/search" do |env| json.field "lengthSeconds", video.length_seconds json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end end @@ -3845,7 +3845,7 @@ get "/api/v1/playlists/:plid" do |env| json.field "authorUrl", "/channel/#{video.ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "index", video.index @@ -3921,7 +3921,7 @@ get "/api/v1/mixes/:rdid" do |env| json.field "videoThumbnails" do json.array do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end @@ -4332,7 +4332,7 @@ get "/vi/:id/:name" do |env| client = make_client(URI.parse(host)) if name == "maxres.jpg" - VIDEO_THUMBNAILS.each do |thumb| + build_thumbnails(id, config, Kemal.config).each do |thumb| if client.head("/vi/#{id}/#{thumb[:url]}.jpg").status_code == 200 name = thumb[:url] + ".jpg" break diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 1de591b3..cd24afdc 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -136,18 +136,6 @@ BYPASS_REGIONS = { "TR", } -VIDEO_THUMBNAILS = { - {name: "maxres", host: "#{CONFIG.domain}", url: "maxres", height: 720, width: 1280}, - {name: "maxresdefault", host: "i.ytimg.com", url: "maxresdefault", height: 720, width: 1280}, - {name: "sddefault", host: "i.ytimg.com", url: "sddefault", height: 480, width: 640}, - {name: "high", host: "i.ytimg.com", url: "hqdefault", height: 360, width: 480}, - {name: "medium", host: "i.ytimg.com", url: "mqdefault", height: 180, width: 320}, - {name: "default", host: "i.ytimg.com", url: "default", height: 90, width: 120}, - {name: "start", host: "i.ytimg.com", url: "1", height: 90, width: 120}, - {name: "middle", host: "i.ytimg.com", url: "2", height: 90, width: 120}, - {name: "end", host: "i.ytimg.com", url: "3", height: 90, width: 120}, -} - # See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L380-#L476 VIDEO_FORMATS = { "5" => {"ext" => "flv", "width" => 400, "height" => 240, "acodec" => "mp3", "abr" => 64, "vcodec" => "h263"}, @@ -826,12 +814,26 @@ def process_video_params(query, preferences) return params end -def generate_thumbnails(json, id) +def build_thumbnails(id, config, kemal_config) + return { + {name: "maxres", host: "#{make_host_url(config, kemal_config)}", url: "maxres", height: 720, width: 1280}, + {name: "maxresdefault", host: "https://i.ytimg.com", url: "maxresdefault", height: 720, width: 1280}, + {name: "sddefault", host: "https://i.ytimg.com", url: "sddefault", height: 480, width: 640}, + {name: "high", host: "https://i.ytimg.com", url: "hqdefault", height: 360, width: 480}, + {name: "medium", host: "https://i.ytimg.com", url: "mqdefault", height: 180, width: 320}, + {name: "default", host: "https://i.ytimg.com", url: "default", height: 90, width: 120}, + {name: "start", host: "https://i.ytimg.com", url: "1", height: 90, width: 120}, + {name: "middle", host: "https://i.ytimg.com", url: "2", height: 90, width: 120}, + {name: "end", host: "https://i.ytimg.com", url: "3", height: 90, width: 120}, + } +end + +def generate_thumbnails(json, id, config, kemal_config) json.array do - VIDEO_THUMBNAILS.each do |thumbnail| + build_thumbnails(id, config, kemal_config).each do |thumbnail| json.object do json.field "quality", thumbnail[:name] - json.field "url", "https://#{thumbnail[:host]}/vi/#{id}/#{thumbnail["url"]}.jpg" + json.field "url", "#{thumbnail[:host]}/vi/#{id}/#{thumbnail["url"]}.jpg" json.field "width", thumbnail[:width] json.field "height", thumbnail[:height] end From 9b8703cf490e85f4f55c1579ff597dd1585e0223 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 22:01:59 -0600 Subject: [PATCH 08/19] Fix tab name for auto-generated channels --- src/invidious/views/channel.ecr | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 5d635b4e..d40d8d9f 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -22,13 +22,17 @@
<%= translate(locale, "View channel on YouTube") %> + <% if !auto_generated %>
<%= translate(locale, "Videos") %>
+ <% end %>
- <% if !auto_generated %> - <%= translate(locale, "Playlists") %> - <% end %> + <% if auto_generated %> + <%= translate(locale, "Playlists") %> + <% else %> + <%= translate(locale, "Playlists") %> + <% end %>
From a1d38a694048a5b39bfda3c3e72038e919d1116f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Sat, 2 Mar 2019 06:56:49 +0000 Subject: [PATCH 09/19] =?UTF-8?q?Update=20Norwegian=20Bokm=C3=A5l=20transl?= =?UTF-8?q?ation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/nb_NO.json | 572 ++++++++++++++++++++++----------------------- 1 file changed, 286 insertions(+), 286 deletions(-) diff --git a/locales/nb_NO.json b/locales/nb_NO.json index d299da2f..64b2337c 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -1,288 +1,288 @@ { - "`x` subscribers": "`x` abonnenter", - "`x` videos": "`x` videoer", - "LIVE": "SANNTIDSVISNING", - "Shared `x` ago": "Delt for `x` siden", - "Unsubscribe": "Opphev abonnement", - "Subscribe": "Abonner", - "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", - "View channel on YouTube": "Vis kanal på YouTube", - "newest": "nyeste", - "oldest": "eldste", - "popular": "populært", - "Preview page": "Forhåndsvis side", - "Next page": "Neste side", - "Clear watch history?": "Tøm visningshistorikk?", - "Yes": "Ja", - "No": "Nei", - "Import and Export Data": "Importer- og eksporter data", - "Import": "Importer", - "Import Invidious data": "Importer Invidious-data", - "Import YouTube subscriptions": "Importer YouTube-abonnenter", - "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", - "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", - "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", - "Export": "Eksporter", - "Export subscriptions as OPML": "Eksporter abonnenter som OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", - "Export data as JSON": "Eksporter data som JSON", - "Delete account?": "Slett konto?", - "History": "Historikk", - "Previous page": "Forrige side", - "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", - "JavaScript license information": "JavaScript-lisensinformasjon", - "source": "kilde", - "Login": "Logg inn", - "Login/Register": "Logg inn/registrer", - "Login to Google": "Logg inn med Google", - "User ID:": "Bruker-ID:", - "Password:": "Passord:", - "Time (h:mm:ss):": "Tid (h:mm:ss):", - "Text CAPTCHA": "Tekst-CAPTCHA", - "Image CAPTCHA": "Bilde-CAPTCHA", - "Sign In": "Innlogging", - "Register": "Registrer", - "Email:": "E-post:", - "Google verification code:": "Google-bekreftelseskode:", - "Preferences": "Innstillinger", - "Player preferences": "Avspillerinnstillinger", - "Always loop: ": "Alltid gjenta: ", - "Autoplay: ": "Autoavspilling: ", - "Autoplay next video: ": "Autospill neste video: ", - "Listen by default: ": "Lytt som forvalg: ", - "Default speed: ": "Forvalgt hastighet: ", - "Preferred video quality: ": "Foretrukket videokvalitet: ", - "Player volume: ": "Avspillerlydstyrke: ", - "Default comments: ": "Forvalgte kommentarer: ", - "Default captions: ": "Forvalgte undertitler: ", - "Fallback captions: ": "Tilbakefallsundertitler: ", - "Show related videos? ": "Vis relaterte videoer? ", - "Visual preferences": "Visuelle innstillinger", - "Dark mode: ": "Mørk drakt: ", - "Thin mode: ": "Tynt modus: ", - "Subscription preferences": "Abonnementsinnstillinger", - "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", - "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", - "Sort videos by: ": "Sorter videoer etter: ", - "published": "publisert", - "published - reverse": "publisert - motsatt", - "alphabetically": "alfabetisk", - "alphabetically - reverse": "alfabetisk - motsatt", - "channel name": "kanalnavn", - "channel name - reverse": "kanalnavn - motsatt", - "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", - "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", - "Only show unwatched: ": "Kun vis usette: ", - "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", - "Data preferences": "Datainnstillinger", - "Clear watch history": "Tøm visningshistorikk", - "Import/Export data": "Importer/eksporter data", - "Manage subscriptions": "Behandle abonnementer", - "Watch history": "Visningshistorikk", - "Delete account": "Slett konto", - "Administrator preferences": "Administratorinnstillinger", - "Default homepage: ": "Forvalgt hjemmeside: ", - "Feed menu: ": "Flyt-meny: ", - "Top enabled? ": "", - "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", - "Login enabled? ": "Innlogging påskrudd? ", - "Registration enabled? ": "Registrering påskrudd? ", - "Report statistics? ": "", - "Save preferences": "Lagre innstillinger", - "Subscription manager": "Abonnementsbehandler", - "`x` subscriptions": "`x` abonnementer", - "Import/Export": "Importer/eksporter", - "unsubscribe": "opphev abonnement", - "Subscriptions": "Abonnement", - "`x` unseen notifications": "`x` usette merknader", - "search": "søk", - "Sign out": "Logg ut", - "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", - "Source available here.": "Kildekode tilgjengelig her.", - "View JavaScript license information.": "Vis JavaScript-lisensinfo.", - "Trending": "Trendsettende", - "Watch video on Youtube": "Vis video på YouTube", - "Genre: ": "Sjanger: ", - "License: ": "Lisens: ", - "Family friendly? ": "Familievennlig? ", - "Wilson score: ": "Wilson-poengsum: ", - "Engagement: ": "Engasjement: ", - "Whitelisted regions: ": "Hvitlistede regioner: ", - "Blacklisted regions: ": "Svartelistede regioner: ", - "Shared `x`": "Delt `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", - "View YouTube comments": "Vis YouTube-kommentarer", - "View more comments on Reddit": "Vis flere kommenterer på Reddit", - "View `x` comments": "Vis `x` kommentarer", - "View Reddit comments": "Vis Reddit-kommentarer", - "Hide replies": "Skjul svar", - "Show replies": "Vis svar", - "Incorrect password": "Feil passord", - "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", - "Invalid TFA code": "Ugyldig tofaktorkode", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", - "Invalid answer": "Ugyldig svar", - "Invalid CAPTCHA": "Ugyldig CAPTCHA", - "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", - "User ID is a required field": "Bruker-ID er et påkrevd felt", - "Password is a required field": "Passord er et påkrevd felt", - "Invalid username or password": "Ugyldig brukernavn eller passord", - "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", - "Password cannot be empty": "Passordet kan ikke være tomt", - "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", - "Please sign in": "Logg inn", - "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", - "channel:`x`": "kanal `x`", - "Deleted or invalid channel": "Slettet eller ugyldig kanal", - "This channel does not exist.": "Denne kanalen finnes ikke.", - "Could not get channel info.": "Kunne ikke innhente kanalinfo.", - "Could not fetch comments": "Kunne ikke hente kommentarer", - "View `x` replies": "Vis `x` svar", - "`x` ago": "`x` siden", - "Load more": "Last inn flere", - "`x` points": "`x` poeng", - "Could not create mix.": "Kunne ikke opprette miks.", - "Playlist is empty": "Spillelisten er tom", - "Invalid playlist.": "Ugyldig spilleliste.", - "Playlist does not exist.": "Spillelisten finnes ikke.", - "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", - "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", - "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", - "Invalid challenge": "Ugyldig utfordring", - "Invalid token": "Ugyldig symbol", - "Invalid user": "Ugyldig bruker", - "Token is expired, please try again": "Symbol utløpt, prøv igjen", - "English": "Engelsk", - "English (auto-generated)": "Engelsk (auto-generert)", - "Afrikaans": "", - "Albanian": "Albansk", - "Amharic": "", - "Arabic": "Arabisk", - "Armenian": "Armensk", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "Hviterussisk", - "Bosnian": "Bosnisk", - "Bulgarian": "Bulgarsk", - "Burmese": "Burmesisk", - "Catalan": "Katalansk", - "Cebuano": "", - "Chinese (Simplified)": "", - "Chinese (Traditional)": "", - "Corsican": "", - "Croatian": "", - "Czech": "Tsjekkisk", - "Danish": "Dansk", - "Dutch": "", - "Esperanto": "Esperanto", - "Estonian": "", - "Filipino": "", - "Finnish": "Finsk", - "French": "Fransk", - "Galician": "", - "Georgian": "", - "German": "", - "Greek": "", - "Gujarati": "", - "Haitian Creole": "", - "Hausa": "", - "Hawaiian": "", - "Hebrew": "", - "Hindi": "", - "Hmong": "", - "Hungarian": "Ungarsk", - "Icelandic": "Islandsk", - "Igbo": "", - "Indonesian": "Indonesisk", - "Irish": "Irsk", - "Italian": "Italiensk", - "Japanese": "Japansk", - "Javanese": "", - "Kannada": "", - "Kazakh": "", - "Khmer": "", - "Korean": "", - "Kurdish": "", - "Kyrgyz": "", - "Lao": "", - "Latin": "", - "Latvian": "", - "Lithuanian": "", - "Luxembourgish": "", - "Macedonian": "", - "Malagasy": "", - "Malay": "", - "Malayalam": "", - "Maltese": "", - "Maori": "", - "Marathi": "", - "Mongolian": "", - "Nepali": "", - "Norwegian": "Norsk bokmål", - "Nyanja": "", - "Pashto": "", - "Persian": "", - "Polish": "", - "Portuguese": "", - "Punjabi": "", - "Romanian": "", - "Russian": "Russisk", - "Samoan": "", - "Scottish Gaelic": "", - "Serbian": "Serbisk", - "Shona": "", - "Sindhi": "", - "Sinhala": "", - "Slovak": "Slovakisk", - "Slovenian": "Slovensk", - "Somali": "Somali", - "Southern Sotho": "", - "Spanish": "Spansk", - "Spanish (Latin America)": "", - "Sundanese": "", - "Swahili": "", - "Swedish": "Svensk", - "Tajik": "", - "Tamil": "", - "Telugu": "", - "Thai": "", - "Turkish": "Tyrkisk", - "Ukrainian": "Ukrainsk", - "Urdu": "", - "Uzbek": "", - "Vietnamese": "Vietnamesisk", - "Welsh": "", - "Western Frisian": "", - "Xhosa": "", - "Yiddish": "", - "Yoruba": "", - "Zulu": "", - "`x` years": "`x` år", - "`x` months": "`x` måneder", - "`x` weeks": "`x` uker", - "`x` days": "`x` dager", - "`x` hours": "`x` timer", - "`x` minutes": "`x` minutter", - "`x` seconds": "`x` sekunder", - "Fallback comments: ": "Tilbakefallskommentarer: ", - "Popular": "Pupulært", - "Top": "Topp", - "About": "Om", - "Rating: ": "Vurdering: ", - "Language: ": "Språk: ", - "Default": "Forvalg", - "Music": "Musikk", - "Gaming": "Spill", - "News": "Nyheter", - "Movies": "Filmer", - "Download": "Last ned", - "Download as: ": "Last ned som: ", - "%A %B %-d, %Y": "", - "(edited)": "(redigert)", - "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", - "`x` marked it with a ❤": "`x` levnet et ❤", - "Audio mode": "Lydmodus", - "Video mode": "Video-modus" + "`x` subscribers": "`x` abonnenter", + "`x` videos": "`x` videoer", + "LIVE": "SANNTIDSVISNING", + "Shared `x` ago": "Delt for `x` siden", + "Unsubscribe": "Opphev abonnement", + "Subscribe": "Abonner", + "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", + "View channel on YouTube": "Vis kanal på YouTube", + "newest": "nyeste", + "oldest": "eldste", + "popular": "populært", + "Preview page": "Forhåndsvis side", + "Next page": "Neste side", + "Clear watch history?": "Tøm visningshistorikk?", + "Yes": "Ja", + "No": "Nei", + "Import and Export Data": "Importer- og eksporter data", + "Import": "Importer", + "Import Invidious data": "Importer Invidious-data", + "Import YouTube subscriptions": "Importer YouTube-abonnenter", + "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", + "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", + "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", + "Export": "Eksporter", + "Export subscriptions as OPML": "Eksporter abonnenter som OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", + "Export data as JSON": "Eksporter data som JSON", + "Delete account?": "Slett konto?", + "History": "Historikk", + "Previous page": "Forrige side", + "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", + "JavaScript license information": "JavaScript-lisensinformasjon", + "source": "kilde", + "Login": "Logg inn", + "Login/Register": "Logg inn/registrer", + "Login to Google": "Logg inn med Google", + "User ID:": "Bruker-ID:", + "Password:": "Passord:", + "Time (h:mm:ss):": "Tid (h:mm:ss):", + "Text CAPTCHA": "Tekst-CAPTCHA", + "Image CAPTCHA": "Bilde-CAPTCHA", + "Sign In": "Innlogging", + "Register": "Registrer", + "Email:": "E-post:", + "Google verification code:": "Google-bekreftelseskode:", + "Preferences": "Innstillinger", + "Player preferences": "Avspillerinnstillinger", + "Always loop: ": "Alltid gjenta: ", + "Autoplay: ": "Autoavspilling: ", + "Autoplay next video: ": "Autospill neste video: ", + "Listen by default: ": "Lytt som forvalg: ", + "Default speed: ": "Forvalgt hastighet: ", + "Preferred video quality: ": "Foretrukket videokvalitet: ", + "Player volume: ": "Avspillerlydstyrke: ", + "Default comments: ": "Forvalgte kommentarer: ", + "Default captions: ": "Forvalgte undertitler: ", + "Fallback captions: ": "Tilbakefallsundertitler: ", + "Show related videos? ": "Vis relaterte videoer? ", + "Visual preferences": "Visuelle innstillinger", + "Dark mode: ": "Mørk drakt: ", + "Thin mode: ": "Tynt modus: ", + "Subscription preferences": "Abonnementsinnstillinger", + "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", + "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", + "Sort videos by: ": "Sorter videoer etter: ", + "published": "publisert", + "published - reverse": "publisert - motsatt", + "alphabetically": "alfabetisk", + "alphabetically - reverse": "alfabetisk - motsatt", + "channel name": "kanalnavn", + "channel name - reverse": "kanalnavn - motsatt", + "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", + "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", + "Only show unwatched: ": "Kun vis usette: ", + "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", + "Data preferences": "Datainnstillinger", + "Clear watch history": "Tøm visningshistorikk", + "Import/Export data": "Importer/eksporter data", + "Manage subscriptions": "Behandle abonnementer", + "Watch history": "Visningshistorikk", + "Delete account": "Slett konto", + "Administrator preferences": "Administratorinnstillinger", + "Default homepage: ": "Forvalgt hjemmeside: ", + "Feed menu: ": "Flyt-meny: ", + "Top enabled? ": "", + "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", + "Login enabled? ": "Innlogging påskrudd? ", + "Registration enabled? ": "Registrering påskrudd? ", + "Report statistics? ": "Innrapporter statistikk? ", + "Save preferences": "Lagre innstillinger", + "Subscription manager": "Abonnementsbehandler", + "`x` subscriptions": "`x` abonnementer", + "Import/Export": "Importer/eksporter", + "unsubscribe": "opphev abonnement", + "Subscriptions": "Abonnement", + "`x` unseen notifications": "`x` usette merknader", + "search": "søk", + "Sign out": "Logg ut", + "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", + "Source available here.": "Kildekode tilgjengelig her.", + "View JavaScript license information.": "Vis JavaScript-lisensinfo.", + "Trending": "Trendsettende", + "Watch video on Youtube": "Vis video på YouTube", + "Genre: ": "Sjanger: ", + "License: ": "Lisens: ", + "Family friendly? ": "Familievennlig? ", + "Wilson score: ": "Wilson-poengsum: ", + "Engagement: ": "Engasjement: ", + "Whitelisted regions: ": "Hvitlistede regioner: ", + "Blacklisted regions: ": "Svartelistede regioner: ", + "Shared `x`": "Delt `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", + "View YouTube comments": "Vis YouTube-kommentarer", + "View more comments on Reddit": "Vis flere kommenterer på Reddit", + "View `x` comments": "Vis `x` kommentarer", + "View Reddit comments": "Vis Reddit-kommentarer", + "Hide replies": "Skjul svar", + "Show replies": "Vis svar", + "Incorrect password": "Feil passord", + "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", + "Invalid TFA code": "Ugyldig tofaktorkode", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", + "Invalid answer": "Ugyldig svar", + "Invalid CAPTCHA": "Ugyldig CAPTCHA", + "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", + "User ID is a required field": "Bruker-ID er et påkrevd felt", + "Password is a required field": "Passord er et påkrevd felt", + "Invalid username or password": "Ugyldig brukernavn eller passord", + "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", + "Password cannot be empty": "Passordet kan ikke være tomt", + "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", + "Please sign in": "Logg inn", + "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", + "channel:`x`": "kanal `x`", + "Deleted or invalid channel": "Slettet eller ugyldig kanal", + "This channel does not exist.": "Denne kanalen finnes ikke.", + "Could not get channel info.": "Kunne ikke innhente kanalinfo.", + "Could not fetch comments": "Kunne ikke hente kommentarer", + "View `x` replies": "Vis `x` svar", + "`x` ago": "`x` siden", + "Load more": "Last inn flere", + "`x` points": "`x` poeng", + "Could not create mix.": "Kunne ikke opprette miks.", + "Playlist is empty": "Spillelisten er tom", + "Invalid playlist.": "Ugyldig spilleliste.", + "Playlist does not exist.": "Spillelisten finnes ikke.", + "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", + "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", + "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", + "Invalid challenge": "Ugyldig utfordring", + "Invalid token": "Ugyldig symbol", + "Invalid user": "Ugyldig bruker", + "Token is expired, please try again": "Symbol utløpt, prøv igjen", + "English": "Engelsk", + "English (auto-generated)": "Engelsk (auto-generert)", + "Afrikaans": "", + "Albanian": "Albansk", + "Amharic": "", + "Arabic": "Arabisk", + "Armenian": "Armensk", + "Azerbaijani": "", + "Bangla": "", + "Basque": "", + "Belarusian": "Hviterussisk", + "Bosnian": "Bosnisk", + "Bulgarian": "Bulgarsk", + "Burmese": "Burmesisk", + "Catalan": "Katalansk", + "Cebuano": "", + "Chinese (Simplified)": "", + "Chinese (Traditional)": "", + "Corsican": "", + "Croatian": "", + "Czech": "Tsjekkisk", + "Danish": "Dansk", + "Dutch": "", + "Esperanto": "Esperanto", + "Estonian": "", + "Filipino": "", + "Finnish": "Finsk", + "French": "Fransk", + "Galician": "", + "Georgian": "", + "German": "", + "Greek": "", + "Gujarati": "", + "Haitian Creole": "", + "Hausa": "", + "Hawaiian": "", + "Hebrew": "", + "Hindi": "", + "Hmong": "", + "Hungarian": "Ungarsk", + "Icelandic": "Islandsk", + "Igbo": "", + "Indonesian": "Indonesisk", + "Irish": "Irsk", + "Italian": "Italiensk", + "Japanese": "Japansk", + "Javanese": "", + "Kannada": "", + "Kazakh": "", + "Khmer": "", + "Korean": "", + "Kurdish": "", + "Kyrgyz": "", + "Lao": "", + "Latin": "", + "Latvian": "", + "Lithuanian": "", + "Luxembourgish": "", + "Macedonian": "", + "Malagasy": "", + "Malay": "", + "Malayalam": "", + "Maltese": "", + "Maori": "", + "Marathi": "", + "Mongolian": "", + "Nepali": "", + "Norwegian": "Norsk bokmål", + "Nyanja": "", + "Pashto": "", + "Persian": "", + "Polish": "", + "Portuguese": "", + "Punjabi": "", + "Romanian": "", + "Russian": "Russisk", + "Samoan": "", + "Scottish Gaelic": "", + "Serbian": "Serbisk", + "Shona": "", + "Sindhi": "", + "Sinhala": "", + "Slovak": "Slovakisk", + "Slovenian": "Slovensk", + "Somali": "Somali", + "Southern Sotho": "", + "Spanish": "Spansk", + "Spanish (Latin America)": "", + "Sundanese": "", + "Swahili": "", + "Swedish": "Svensk", + "Tajik": "", + "Tamil": "", + "Telugu": "", + "Thai": "", + "Turkish": "Tyrkisk", + "Ukrainian": "Ukrainsk", + "Urdu": "", + "Uzbek": "", + "Vietnamese": "Vietnamesisk", + "Welsh": "", + "Western Frisian": "", + "Xhosa": "", + "Yiddish": "", + "Yoruba": "", + "Zulu": "", + "`x` years": "`x` år", + "`x` months": "`x` måneder", + "`x` weeks": "`x` uker", + "`x` days": "`x` dager", + "`x` hours": "`x` timer", + "`x` minutes": "`x` minutter", + "`x` seconds": "`x` sekunder", + "Fallback comments: ": "Tilbakefallskommentarer: ", + "Popular": "Pupulært", + "Top": "Topp", + "About": "Om", + "Rating: ": "Vurdering: ", + "Language: ": "Språk: ", + "Default": "Forvalg", + "Music": "Musikk", + "Gaming": "Spill", + "News": "Nyheter", + "Movies": "Filmer", + "Download": "Last ned", + "Download as: ": "Last ned som: ", + "%A %B %-d, %Y": "", + "(edited)": "(redigert)", + "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", + "`x` marked it with a ❤": "`x` levnet et ❤", + "Audio mode": "Lydmodus", + "Video mode": "Video-modus" } From e96c4732d64fb85acbc9b884604bbefba3a51137 Mon Sep 17 00:00:00 2001 From: dimqua Date: Mon, 4 Mar 2019 21:24:56 +0000 Subject: [PATCH 10/19] Update Russian translation --- locales/ru.json | 584 ++++++++++++++++++++++++------------------------ 1 file changed, 292 insertions(+), 292 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index a840a869..0c666589 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,294 +1,294 @@ { - "`x` subscribers": "`x` подписчиков", - "`x` videos": "`x` видео", - "LIVE": "ПРЯМОЙ ЭФИР", - "Shared `x` ago": "Опубликовано `x` назад", - "Unsubscribe": "Отписаться", - "Subscribe": "Подписаться", - "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", - "View channel on YouTube": "Канал на YouTube", - "newest": "новые", - "oldest": "старые", - "popular": "популярные", - "Preview page": "Предварительный просмотр", - "Next page": "Следующая страница", - "Clear watch history?": "Очистить историю просмотров?", - "Yes": "Да", - "No": "Нет", - "Import and Export Data": "Импорт и экспорт данных", - "Import": "Импорт", - "Import Invidious data": "Импортировать данные Invidious", - "Import YouTube subscriptions": "Импортировать YouTube подписки", - "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", - "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", - "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", - "Export": "Экспорт", - "Export subscriptions as OPML": "Экспортировать подписки в OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", - "Export data as JSON": "Экспортировать данные в JSON", - "Delete account?": "Удалить аккаунт?", - "History": "История", - "Previous page": "Предыдущая страница", - "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", - "JavaScript license information": "Лицензии JavaScript", - "source": "источник", - "Login": "Войти", - "Login/Register": "Войти/Регистрация", - "Login to Google": "Войти через Google", - "User ID:": "ID пользователя:", - "Password:": "Пароль:", - "Time (h:mm:ss):": "Время (ч:мм:сс):", - "Text CAPTCHA": "Текст капчи", - "Image CAPTCHA": "Изображение капчи", - "Sign In": "Войти", - "Register": "Регистрация", - "Email:": "Эл. почта:", - "Google verification code:": "Код подтверждения Google:", - "Preferences": "Настройки", - "Player preferences": "Настройки проигрывателя", - "Always loop: ": "Всегда повторять: ", - "Autoplay: ": "Автовоспроизведение: ", - "Autoplay next video: ": "Автовоспроизведение следующего видео: ", - "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", - "Default speed: ": "Скорость по-умолчанию: ", - "Preferred video quality: ": "Предпочтительное качество видео: ", - "Player volume: ": "Громкость воспроизведения: ", - "Default comments: ": "Источник комментариев: ", - "youtube": "YouTube", - "reddit": "Reddit", - "Default captions: ": "Субтитры по-умолчанию: ", - "Fallback captions: ": "Резервные субтитры: ", - "Show related videos? ": "Показывать похожие видео? ", - "Visual preferences": "Визуальные настройки", - "Dark mode: ": "Темная тема: ", - "Thin mode: ": "Облегченный режим: ", - "Subscription preferences": "Настройки подписок", - "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", - "Number of videos shown in feed: ": "Число видео в ленте: ", - "Sort videos by: ": "Сортировать видео по: ", - "published": "дате публикации", - "published - reverse": "дате - обратный порядок", - "alphabetically": "алфавиту", - "alphabetically - reverse": "алфавиту - обратный порядок", - "channel name": "имени канала", - "channel name - reverse": "имени канала - обратный порядок", - "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", - "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", - "Only show unwatched: ": "Отображать только непросмотренные видео: ", - "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", - "Data preferences": "Настройки данных", - "Clear watch history": "Очистить историю просмотра", - "Import/Export data": "Импорт/Экспорт данных", - "Manage subscriptions": "Управление подписками", - "Watch history": "История просмотров", - "Delete account": "Удалить аккаунт", - "Administrator preferences": "", - "Default homepage: ": "", - "Feed menu: ": "", - "Top enabled? ": "", - "CAPTCHA enabled? ": "", - "Login enabled? ": "", - "Registration enabled? ": "", - "Report statistics? ": "", - "Save preferences": "Сохранить настройки", - "Subscription manager": "Менеджер подписок", - "`x` subscriptions": "`x` подписок", - "Import/Export": "Импорт/Экспорт", - "unsubscribe": "отписаться", - "Subscriptions": "Подписки", - "`x` unseen notifications": "`x` новых оповещений", - "search": "поиск", - "Sign out": "Выйти", - "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", - "Source available here.": "Исходный код доступен здесь.", - "Liberapay: ": "Liberapay: ", - "Patreon: ": "Patreon: ", - "BTC: ": "BTC: ", - "BCH: ": "BCH: ", - "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", - "Trending": "В тренде", - "Watch video on Youtube": "Смотреть на YouTube", - "Genre: ": "Жанр: ", - "License: ": "Лицензия: ", - "Family friendly? ": "Семейный просмотр: ", - "Wilson score: ": "Рейтинг Вильсона: ", - "Engagement: ": "Вовлеченность: ", - "Whitelisted regions: ": "Доступно для: ", - "Blacklisted regions: ": "Недоступно для: ", - "Shared `x`": "Опубликовано `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", - "View YouTube comments": "Смотреть комментарии с YouTube", - "View more comments on Reddit": "Больше комментариев на Reddit", - "View `x` comments": "Показать `x` комментариев", - "View Reddit comments": "Смотреть комментарии с Reddit", - "Hide replies": "Скрыть ответы", - "Show replies": "Показать ответы", - "Incorrect password": "Неправильный пароль", - "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", - "Invalid TFA code": "Неправильный TFA код", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", - "Invalid answer": "Неверный ответ", - "Invalid CAPTCHA": "Неверная капча", - "CAPTCHA is a required field": "Необходимо ввести капчу", - "User ID is a required field": "Необходимо ввести идентификатор пользователя", - "Password is a required field": "Необходимо ввести пароль", - "Invalid username or password": "Недопустимый пароль или имя пользователя", - "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", - "Password cannot be empty": "Пароль не может быть пустым", - "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", - "Please sign in": "Пожалуйста, войдите", - "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", - "channel:`x`": "канал: `x`", - "Deleted or invalid channel": "Канал удален или не найден", - "This channel does not exist.": "Такой канал не существует.", - "Could not get channel info.": "Невозможно получить информацию о канале.", - "Could not fetch comments": "Невозможно получить комментарии", - "View `x` replies": "Показать `x` ответов", - "`x` ago": "`x` назад", - "Load more": "Загрузить больше", - "`x` points": "`x` очков", - "Could not create mix.": "Невозможно создать \"микс\".", - "Playlist is empty": "Плейлист пуст", - "Invalid playlist.": "Некорректный плейлист.", - "Playlist does not exist.": "Плейлист не существует.", - "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", - "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", - "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", - "Invalid challenge": "Неправильный ответ в \"challenge\"", - "Invalid token": "Неправильный токен", - "Invalid user": "Недопустимое имя пользователя", - "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", - "English": "Английский", - "English (auto-generated)": "Английский (созданы автоматически)", - "Afrikaans": "Африкаанс", - "Albanian": "Албанский", - "Amharic": "Амхарский", - "Arabic": "Арабский", - "Armenian": "Армянский", - "Azerbaijani": "Азербайджанский", - "Bangla": "Бенгальский", - "Basque": "Баскский", - "Belarusian": "Белорусский", - "Bosnian": "Боснийский", - "Bulgarian": "Болгарский", - "Burmese": "Бирманский", - "Catalan": "Каталонский", - "Cebuano": "Себуанский", - "Chinese (Simplified)": "Китайский (упрощенный)", - "Chinese (Traditional)": "Китайский (традиционный)", - "Corsican": "Корсиканский", - "Croatian": "Хорватский", - "Czech": "Чешский", - "Danish": "Датский", - "Dutch": "Нидерландский", - "Esperanto": "Эсперанто", - "Estonian": "Эстонский", - "Filipino": "Филиппинский", - "Finnish": "Финский", - "French": "Французский", - "Galician": "Галисийский", - "Georgian": "Грузинский", - "German": "Немецкий", - "Greek": "Греческий", - "Gujarati": "Гуджаратский", - "Haitian Creole": "Гаит. креольский", - "Hausa": "Хауса", - "Hawaiian": "Гавайский", - "Hebrew": "Иврит", - "Hindi": "Хинди", - "Hmong": "Хмонг (мяо)", - "Hungarian": "Венгерский", - "Icelandic": "Исландский", - "Igbo": "Игбо", - "Indonesian": "Индонезийский", - "Irish": "Ирландский", - "Italian": "Итальянский", - "Japanese": "Японский", - "Javanese": "Яванский", - "Kannada": "Каннада", - "Kazakh": "Казахский", - "Khmer": "Кхмерский", - "Korean": "Корейский", - "Kurdish": "Курдский", - "Kyrgyz": "Киргизский", - "Lao": "Лаосский", - "Latin": "Латинский", - "Latvian": "Латышский", - "Lithuanian": "Литовский", - "Luxembourgish": "Люксембургский", - "Macedonian": "Македонский", - "Malagasy": "Малагасийский", - "Malay": "Малайский", - "Malayalam": "Малаялам", - "Maltese": "Мальтийский", - "Maori": "Маори", - "Marathi": "Маратхи", - "Mongolian": "Монгольская", - "Nepali": "Непальский", - "Norwegian": "Норвежский", - "Nyanja": "Ньянджа", - "Pashto": "Пушту", - "Persian": "Персидский", - "Polish": "Польский", - "Portuguese": "Португальский", - "Punjabi": "Панджаби", - "Romanian": "Румынский", - "Russian": "Русский", - "Samoan": "Самоанский", - "Scottish Gaelic": "Шотландский (гэльский)", - "Serbian": "Сербский", - "Shona": "Шона", - "Sindhi": "Синдхи", - "Sinhala": "Сингальский", - "Slovak": "Словацкий", - "Slovenian": "Словенский", - "Somali": "Сомалийский", - "Southern Sotho": "Сесото (южный сото)", - "Spanish": "Испанский", - "Spanish (Latin America)": "Испанский (Латинская Америка)", - "Sundanese": "Сунданский", - "Swahili": "Суахили", - "Swedish": "Шведский", - "Tajik": "Таджикский", - "Tamil": "Тамильский", - "Telugu": "Телугу", - "Thai": "Тайский", - "Turkish": "Турецкий", - "Ukrainian": "Украинский", - "Urdu": "Урду", - "Uzbek": "Узбекский", - "Vietnamese": "Вьетнамский", - "Welsh": "Валлийский", - "Western Frisian": "Западнофризский", - "Xhosa": "Коса", - "Yiddish": "Идиш", - "Yoruba": "Йоруба", - "Zulu": "Зулусский", - "`x` years": "`x` лет", - "`x` months": "`x` месяцев", - "`x` weeks": "`x` недель", - "`x` days": "`x` дней", - "`x` hours": "`x` часов", - "`x` minutes": "`x` минут", - "`x` seconds": "`x` секунд", - "Fallback comments: ": "Резервные комментарии: ", - "Popular": "Популярное", - "Top": "Топ", - "About": "О сайте", - "Rating: ": "Рейтинг: ", - "Language: ": "Язык: ", - "Default": "По-умолчанию", - "Music": "Музыка", - "Gaming": "Игры", - "News": "Новости", - "Movies": "Фильмы", - "Download": "Скачать", - "Download as: ": "Скачать как: ", - "%A %B %-d, %Y": "%-d %B %Y, %A", - "(edited)": "(изменено)", - "Youtube permalink of the comment": "Прямая ссылка на YouTube", - "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", - "Audio mode": "Аудио режим", - "Video mode": "Видео режим" + "`x` subscribers": "`x` подписчиков", + "`x` videos": "`x` видео", + "LIVE": "ПРЯМОЙ ЭФИР", + "Shared `x` ago": "Опубликовано `x` назад", + "Unsubscribe": "Отписаться", + "Subscribe": "Подписаться", + "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", + "View channel on YouTube": "Канал на YouTube", + "newest": "новые", + "oldest": "старые", + "popular": "популярные", + "Preview page": "Предварительный просмотр", + "Next page": "Следующая страница", + "Clear watch history?": "Очистить историю просмотров?", + "Yes": "Да", + "No": "Нет", + "Import and Export Data": "Импорт и экспорт данных", + "Import": "Импорт", + "Import Invidious data": "Импортировать данные Invidious", + "Import YouTube subscriptions": "Импортировать YouTube подписки", + "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", + "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", + "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", + "Export": "Экспорт", + "Export subscriptions as OPML": "Экспортировать подписки в OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", + "Export data as JSON": "Экспортировать данные в JSON", + "Delete account?": "Удалить аккаунт?", + "History": "История", + "Previous page": "Предыдущая страница", + "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", + "JavaScript license information": "Лицензии JavaScript", + "source": "источник", + "Login": "Войти", + "Login/Register": "Войти/Регистрация", + "Login to Google": "Войти через Google", + "User ID:": "ID пользователя:", + "Password:": "Пароль:", + "Time (h:mm:ss):": "Время (ч:мм:сс):", + "Text CAPTCHA": "Текст капчи", + "Image CAPTCHA": "Изображение капчи", + "Sign In": "Войти", + "Register": "Регистрация", + "Email:": "Эл. почта:", + "Google verification code:": "Код подтверждения Google:", + "Preferences": "Настройки", + "Player preferences": "Настройки проигрывателя", + "Always loop: ": "Всегда повторять: ", + "Autoplay: ": "Автовоспроизведение: ", + "Autoplay next video: ": "Автовоспроизведение следующего видео: ", + "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", + "Default speed: ": "Скорость по-умолчанию: ", + "Preferred video quality: ": "Предпочтительное качество видео: ", + "Player volume: ": "Громкость воспроизведения: ", + "Default comments: ": "Источник комментариев: ", + "youtube": "YouTube", + "reddit": "Reddit", + "Default captions: ": "Субтитры по-умолчанию: ", + "Fallback captions: ": "Резервные субтитры: ", + "Show related videos? ": "Показывать похожие видео? ", + "Visual preferences": "Визуальные настройки", + "Dark mode: ": "Темная тема: ", + "Thin mode: ": "Облегченный режим: ", + "Subscription preferences": "Настройки подписок", + "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", + "Number of videos shown in feed: ": "Число видео в ленте: ", + "Sort videos by: ": "Сортировать видео по: ", + "published": "дате публикации", + "published - reverse": "дате - обратный порядок", + "alphabetically": "алфавиту", + "alphabetically - reverse": "алфавиту - обратный порядок", + "channel name": "имени канала", + "channel name - reverse": "имени канала - обратный порядок", + "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", + "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", + "Only show unwatched: ": "Отображать только непросмотренные видео: ", + "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", + "Data preferences": "Настройки данных", + "Clear watch history": "Очистить историю просмотра", + "Import/Export data": "Импорт/Экспорт данных", + "Manage subscriptions": "Управление подписками", + "Watch history": "История просмотров", + "Delete account": "Удалить аккаунт", + "Administrator preferences": "Настройки администратора", + "Default homepage: ": "Главная страница по умолчанию: ", + "Feed menu: ": "Меню ленты: ", + "Top enabled? ": "Включить ТОП? ", + "CAPTCHA enabled? ": "Включить капчу? ", + "Login enabled? ": "Включить логин? ", + "Registration enabled? ": "Включить регистрацию? ", + "Report statistics? ": "Отображать статистику? ", + "Save preferences": "Сохранить настройки", + "Subscription manager": "Менеджер подписок", + "`x` subscriptions": "`x` подписок", + "Import/Export": "Импорт/Экспорт", + "unsubscribe": "отписаться", + "Subscriptions": "Подписки", + "`x` unseen notifications": "`x` новых оповещений", + "search": "поиск", + "Sign out": "Выйти", + "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", + "Source available here.": "Исходный код доступен здесь.", + "Liberapay: ": "Liberapay: ", + "Patreon: ": "Patreon: ", + "BTC: ": "BTC: ", + "BCH: ": "BCH: ", + "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", + "Trending": "В тренде", + "Watch video on Youtube": "Смотреть на YouTube", + "Genre: ": "Жанр: ", + "License: ": "Лицензия: ", + "Family friendly? ": "Семейный просмотр: ", + "Wilson score: ": "Рейтинг Вильсона: ", + "Engagement: ": "Вовлеченность: ", + "Whitelisted regions: ": "Доступно для: ", + "Blacklisted regions: ": "Недоступно для: ", + "Shared `x`": "Опубликовано `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", + "View YouTube comments": "Смотреть комментарии с YouTube", + "View more comments on Reddit": "Больше комментариев на Reddit", + "View `x` comments": "Показать `x` комментариев", + "View Reddit comments": "Смотреть комментарии с Reddit", + "Hide replies": "Скрыть ответы", + "Show replies": "Показать ответы", + "Incorrect password": "Неправильный пароль", + "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", + "Invalid TFA code": "Неправильный TFA код", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", + "Invalid answer": "Неверный ответ", + "Invalid CAPTCHA": "Неверная капча", + "CAPTCHA is a required field": "Необходимо ввести капчу", + "User ID is a required field": "Необходимо ввести идентификатор пользователя", + "Password is a required field": "Необходимо ввести пароль", + "Invalid username or password": "Недопустимый пароль или имя пользователя", + "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", + "Password cannot be empty": "Пароль не может быть пустым", + "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", + "Please sign in": "Пожалуйста, войдите", + "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", + "channel:`x`": "канал: `x`", + "Deleted or invalid channel": "Канал удален или не найден", + "This channel does not exist.": "Такой канал не существует.", + "Could not get channel info.": "Невозможно получить информацию о канале.", + "Could not fetch comments": "Невозможно получить комментарии", + "View `x` replies": "Показать `x` ответов", + "`x` ago": "`x` назад", + "Load more": "Загрузить больше", + "`x` points": "`x` очков", + "Could not create mix.": "Невозможно создать \"микс\".", + "Playlist is empty": "Плейлист пуст", + "Invalid playlist.": "Некорректный плейлист.", + "Playlist does not exist.": "Плейлист не существует.", + "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", + "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", + "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", + "Invalid challenge": "Неправильный ответ в \"challenge\"", + "Invalid token": "Неправильный токен", + "Invalid user": "Недопустимое имя пользователя", + "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", + "English": "Английский", + "English (auto-generated)": "Английский (созданы автоматически)", + "Afrikaans": "Африкаанс", + "Albanian": "Албанский", + "Amharic": "Амхарский", + "Arabic": "Арабский", + "Armenian": "Армянский", + "Azerbaijani": "Азербайджанский", + "Bangla": "Бенгальский", + "Basque": "Баскский", + "Belarusian": "Белорусский", + "Bosnian": "Боснийский", + "Bulgarian": "Болгарский", + "Burmese": "Бирманский", + "Catalan": "Каталонский", + "Cebuano": "Себуанский", + "Chinese (Simplified)": "Китайский (упрощенный)", + "Chinese (Traditional)": "Китайский (традиционный)", + "Corsican": "Корсиканский", + "Croatian": "Хорватский", + "Czech": "Чешский", + "Danish": "Датский", + "Dutch": "Нидерландский", + "Esperanto": "Эсперанто", + "Estonian": "Эстонский", + "Filipino": "Филиппинский", + "Finnish": "Финский", + "French": "Французский", + "Galician": "Галисийский", + "Georgian": "Грузинский", + "German": "Немецкий", + "Greek": "Греческий", + "Gujarati": "Гуджаратский", + "Haitian Creole": "Гаит. креольский", + "Hausa": "Хауса", + "Hawaiian": "Гавайский", + "Hebrew": "Иврит", + "Hindi": "Хинди", + "Hmong": "Хмонг (мяо)", + "Hungarian": "Венгерский", + "Icelandic": "Исландский", + "Igbo": "Игбо", + "Indonesian": "Индонезийский", + "Irish": "Ирландский", + "Italian": "Итальянский", + "Japanese": "Японский", + "Javanese": "Яванский", + "Kannada": "Каннада", + "Kazakh": "Казахский", + "Khmer": "Кхмерский", + "Korean": "Корейский", + "Kurdish": "Курдский", + "Kyrgyz": "Киргизский", + "Lao": "Лаосский", + "Latin": "Латинский", + "Latvian": "Латышский", + "Lithuanian": "Литовский", + "Luxembourgish": "Люксембургский", + "Macedonian": "Македонский", + "Malagasy": "Малагасийский", + "Malay": "Малайский", + "Malayalam": "Малаялам", + "Maltese": "Мальтийский", + "Maori": "Маори", + "Marathi": "Маратхи", + "Mongolian": "Монгольская", + "Nepali": "Непальский", + "Norwegian": "Норвежский", + "Nyanja": "Ньянджа", + "Pashto": "Пушту", + "Persian": "Персидский", + "Polish": "Польский", + "Portuguese": "Португальский", + "Punjabi": "Панджаби", + "Romanian": "Румынский", + "Russian": "Русский", + "Samoan": "Самоанский", + "Scottish Gaelic": "Шотландский (гэльский)", + "Serbian": "Сербский", + "Shona": "Шона", + "Sindhi": "Синдхи", + "Sinhala": "Сингальский", + "Slovak": "Словацкий", + "Slovenian": "Словенский", + "Somali": "Сомалийский", + "Southern Sotho": "Сесото (южный сото)", + "Spanish": "Испанский", + "Spanish (Latin America)": "Испанский (Латинская Америка)", + "Sundanese": "Сунданский", + "Swahili": "Суахили", + "Swedish": "Шведский", + "Tajik": "Таджикский", + "Tamil": "Тамильский", + "Telugu": "Телугу", + "Thai": "Тайский", + "Turkish": "Турецкий", + "Ukrainian": "Украинский", + "Urdu": "Урду", + "Uzbek": "Узбекский", + "Vietnamese": "Вьетнамский", + "Welsh": "Валлийский", + "Western Frisian": "Западнофризский", + "Xhosa": "Коса", + "Yiddish": "Идиш", + "Yoruba": "Йоруба", + "Zulu": "Зулусский", + "`x` years": "`x` лет", + "`x` months": "`x` месяцев", + "`x` weeks": "`x` недель", + "`x` days": "`x` дней", + "`x` hours": "`x` часов", + "`x` minutes": "`x` минут", + "`x` seconds": "`x` секунд", + "Fallback comments: ": "Резервные комментарии: ", + "Popular": "Популярное", + "Top": "Топ", + "About": "О сайте", + "Rating: ": "Рейтинг: ", + "Language: ": "Язык: ", + "Default": "По-умолчанию", + "Music": "Музыка", + "Gaming": "Игры", + "News": "Новости", + "Movies": "Фильмы", + "Download": "Скачать", + "Download as: ": "Скачать как: ", + "%A %B %-d, %Y": "%-d %B %Y, %A", + "(edited)": "(изменено)", + "Youtube permalink of the comment": "Прямая ссылка на YouTube", + "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", + "Audio mode": "Аудио режим", + "Video mode": "Видео режим" } From 8f41130a147d8970df0526d2383b6f2dbda905bb Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 22:23:17 -0600 Subject: [PATCH 11/19] Update and add missing text to locales --- locales/ar.json | 13 +- locales/de.json | 13 +- locales/en-US.json | 9 +- locales/eu.json | 9 +- locales/fr.json | 8 +- locales/it.json | 8 +- locales/nb_NO.json | 575 +++++++++++++++--------------- locales/nl.json | 9 +- locales/pl.json | 9 +- locales/ru.json | 583 +++++++++++++++---------------- src/invidious/views/template.ecr | 8 +- 11 files changed, 632 insertions(+), 612 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index e8b59c46..4f43675f 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -10,8 +10,9 @@ "newest": "الأجدد", "oldest": "الأقدم", "popular": "الاكثر شعبية", - "Preview page": "معاينة الصفحة", + "last": "", "Next page": "الصفحة الثانية", + "Previous page": "الصفحة السابقة", "Clear watch history?": "مسح السجل ؟", "Yes": "نعم", "No": "لا", @@ -28,7 +29,6 @@ "Export data as JSON": "استخراج البيانات كـ JSON", "Delete account?": "حذف الحساب ؟", "History": "السجل", - "Previous page": "الصفحة السابقة", "An alternative front-end to YouTube": "البديل الكامل لموقع يوتيوب", "JavaScript license information": "معلومات ترخيص JavaScript", "source": "المصدر", @@ -101,10 +101,6 @@ "Sign out": "تسجيل الخروج", "Released under the AGPLv3 by Omar Roth.": "تم الإنشاء تحت AGPLv3 بواسطة عمر روث.", "Source available here.": "الأكواد متوفرة هنا.", - "Liberapay: ": "ليبرباى: ", - "Patreon: ": "باتريون: ", - "BTC: ": "بيتكوين: ", - "BCH: ": "بيتكوين كاش: ", "View JavaScript license information.": "مشاهدة معلومات حول تراخيص الجافاسكريبت.", "Trending": "الشائع", "Watch video on Youtube": "مشاهدة الفيديو على اليوتيوب", @@ -290,5 +286,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/de.json b/locales/de.json index e40c0b31..d66ebee3 100644 --- a/locales/de.json +++ b/locales/de.json @@ -10,8 +10,9 @@ "newest": "neueste", "oldest": "älteste", "popular": "beliebt", - "Preview page": "Vorschau Seite", + "last": "", "Next page": "Nächste Seite", + "Previous page": "Vorherige Seite", "Clear watch history?": "Verlauf löschen?", "Yes": "Ja", "No": "Nein", @@ -28,7 +29,6 @@ "Export data as JSON": "Daten als JSON exportieren", "Delete account?": "Account löschen?", "History": "Verlauf", - "Previous page": "Vorherige Seite", "An alternative front-end to YouTube": "Eine alternative Oberfläche für YouTube", "JavaScript license information": "JavaScript Lizenzinformationen", "source": "Quelle", @@ -101,10 +101,6 @@ "Sign out": "Abmelden", "Released under the AGPLv3 by Omar Roth.": "Veröffentlicht unter AGPLv3 von Omar Roth.", "Source available here.": "Quellcode verfügbar hier.", - "Liberapay: ": "Liberapay: ", - "Patreon: ": "Patreon: ", - "BTC: ": "BTC: ", - "BCH: ": "BCH: ", "View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.", "Trending": "Trending", "Watch video on Youtube": "Video auf YouTube ansehen", @@ -290,5 +286,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/en-US.json b/locales/en-US.json index 1848ff20..6a05d1bb 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -10,8 +10,9 @@ "newest": "newest", "oldest": "oldest", "popular": "popular", - "Preview page": "Preview page", + "last": "last", "Next page": "Next page", + "Previous page": "Previous page", "Clear watch history?": "Clear watch history?", "Yes": "Yes", "No": "No", @@ -28,7 +29,6 @@ "Export data as JSON": "Export data as JSON", "Delete account?": "Delete account?", "History": "History", - "Previous page": "Previous page", "An alternative front-end to YouTube": "An alternative front-end to YouTube", "JavaScript license information": "JavaScript license information", "source": "source", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "Youtube permalink of the comment", "`x` marked it with a ❤": "`x` marked it with a ❤", "Audio mode": "Audio mode", - "Video mode": "Video mode" + "Video mode": "Video mode", + "Videos": "Videos", + "Playlists": "Playlists", + "Current version: ": "Current version: " } diff --git a/locales/eu.json b/locales/eu.json index 8f887f71..1e4ad1d4 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -10,8 +10,9 @@ "newest": "berrienak", "oldest": "zaharrenak", "popular": "ospetsuenak", - "Preview page": "Aurrebista orria", + "last": "", "Next page": "Hurrengo orria", + "Previous page": "Aurreko orria", "Clear watch history?": "Garbitu ikusitakoen historia?", "Yes": "Bai", "No": "Ez", @@ -28,7 +29,6 @@ "Export data as JSON": "Datuak JSON bezala esportatu", "Delete account?": "Kontua ezabatu?", "History": "Historia", - "Previous page": "Aurreko orria", "An alternative front-end to YouTube": "YouTuberako interfaze alternatibo bat", "JavaScript license information": "JavaScript lizentzia informazioa", "source": "iturburua", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/fr.json b/locales/fr.json index f5c34a6e..28f0b579 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -10,7 +10,9 @@ "newest": "Date d'ajout (la plus récente)", "oldest": "Date d'ajout (la plus ancienne)", "popular": "Les plus populaires", + "last": "", "Next page": "Page suivante", + "Previous page": "Page précédente", "Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?", "Yes": "Oui", "No": "Non", @@ -27,7 +29,6 @@ "Export data as JSON": "Exporter les données au format JSON", "Delete account?": "Supprimer votre compte ?", "History": "Historique", - "Previous page": "Page précédente", "An alternative front-end to YouTube": "Un front-end alternatif à YouTube", "JavaScript license information": "Informations sur les licences JavaScript", "source": "source", @@ -283,5 +284,8 @@ "Youtube permalink of the comment": "Lien YouTube permanent vers le commentaire", "`x` marked it with a ❤": "`x` l'a marqué d'un ❤", "Audio mode": "Mode Audio", - "Video mode": "Mode Vidéo" + "Video mode": "Mode Vidéo", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/it.json b/locales/it.json index eeae6ed3..42dabf6d 100644 --- a/locales/it.json +++ b/locales/it.json @@ -10,7 +10,9 @@ "newest": "Data di aggiunta (più recente)", "oldest": "Data di aggiunta (più vecchia)", "popular": "Tendenze", + "last": "", "Next page": "Pagina successiva", + "Previous page": "Pagina precedente", "Clear watch history?": "Sei sicuro di voler cancellare la cronologia dei video guardati?", "Yes": "Si", "No": "No", @@ -27,7 +29,6 @@ "Export data as JSON": "Esporta i dati in formato JSON", "Delete account?": "Sei sicuro di voler cancellare l'account?", "History": "Cronologia", - "Previous page": "Pagina precedente", "An alternative front-end to YouTube": "Un'interfaccia alternativa per YouTube", "JavaScript license information": "Info licenze JavaScript", "source": "sorgente", @@ -283,5 +284,8 @@ "Youtube permalink of the comment": "Link permanente al commento di YouTube", "`x` marked it with a ❤": "`x` l'ha contrassegnato con un ❤", "Audio mode": "Modalità audio", - "Video mode": "Modalità video" + "Video mode": "Modalità video", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/nb_NO.json b/locales/nb_NO.json index 64b2337c..f0f544b5 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -1,288 +1,291 @@ { - "`x` subscribers": "`x` abonnenter", - "`x` videos": "`x` videoer", - "LIVE": "SANNTIDSVISNING", - "Shared `x` ago": "Delt for `x` siden", - "Unsubscribe": "Opphev abonnement", - "Subscribe": "Abonner", - "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", - "View channel on YouTube": "Vis kanal på YouTube", - "newest": "nyeste", - "oldest": "eldste", - "popular": "populært", - "Preview page": "Forhåndsvis side", - "Next page": "Neste side", - "Clear watch history?": "Tøm visningshistorikk?", - "Yes": "Ja", - "No": "Nei", - "Import and Export Data": "Importer- og eksporter data", - "Import": "Importer", - "Import Invidious data": "Importer Invidious-data", - "Import YouTube subscriptions": "Importer YouTube-abonnenter", - "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", - "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", - "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", - "Export": "Eksporter", - "Export subscriptions as OPML": "Eksporter abonnenter som OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", - "Export data as JSON": "Eksporter data som JSON", - "Delete account?": "Slett konto?", - "History": "Historikk", - "Previous page": "Forrige side", - "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", - "JavaScript license information": "JavaScript-lisensinformasjon", - "source": "kilde", - "Login": "Logg inn", - "Login/Register": "Logg inn/registrer", - "Login to Google": "Logg inn med Google", - "User ID:": "Bruker-ID:", - "Password:": "Passord:", - "Time (h:mm:ss):": "Tid (h:mm:ss):", - "Text CAPTCHA": "Tekst-CAPTCHA", - "Image CAPTCHA": "Bilde-CAPTCHA", - "Sign In": "Innlogging", - "Register": "Registrer", - "Email:": "E-post:", - "Google verification code:": "Google-bekreftelseskode:", - "Preferences": "Innstillinger", - "Player preferences": "Avspillerinnstillinger", - "Always loop: ": "Alltid gjenta: ", - "Autoplay: ": "Autoavspilling: ", - "Autoplay next video: ": "Autospill neste video: ", - "Listen by default: ": "Lytt som forvalg: ", - "Default speed: ": "Forvalgt hastighet: ", - "Preferred video quality: ": "Foretrukket videokvalitet: ", - "Player volume: ": "Avspillerlydstyrke: ", - "Default comments: ": "Forvalgte kommentarer: ", - "Default captions: ": "Forvalgte undertitler: ", - "Fallback captions: ": "Tilbakefallsundertitler: ", - "Show related videos? ": "Vis relaterte videoer? ", - "Visual preferences": "Visuelle innstillinger", - "Dark mode: ": "Mørk drakt: ", - "Thin mode: ": "Tynt modus: ", - "Subscription preferences": "Abonnementsinnstillinger", - "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", - "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", - "Sort videos by: ": "Sorter videoer etter: ", - "published": "publisert", - "published - reverse": "publisert - motsatt", - "alphabetically": "alfabetisk", - "alphabetically - reverse": "alfabetisk - motsatt", - "channel name": "kanalnavn", - "channel name - reverse": "kanalnavn - motsatt", - "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", - "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", - "Only show unwatched: ": "Kun vis usette: ", - "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", - "Data preferences": "Datainnstillinger", - "Clear watch history": "Tøm visningshistorikk", - "Import/Export data": "Importer/eksporter data", - "Manage subscriptions": "Behandle abonnementer", - "Watch history": "Visningshistorikk", - "Delete account": "Slett konto", - "Administrator preferences": "Administratorinnstillinger", - "Default homepage: ": "Forvalgt hjemmeside: ", - "Feed menu: ": "Flyt-meny: ", - "Top enabled? ": "", - "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", - "Login enabled? ": "Innlogging påskrudd? ", - "Registration enabled? ": "Registrering påskrudd? ", - "Report statistics? ": "Innrapporter statistikk? ", - "Save preferences": "Lagre innstillinger", - "Subscription manager": "Abonnementsbehandler", - "`x` subscriptions": "`x` abonnementer", - "Import/Export": "Importer/eksporter", - "unsubscribe": "opphev abonnement", - "Subscriptions": "Abonnement", - "`x` unseen notifications": "`x` usette merknader", - "search": "søk", - "Sign out": "Logg ut", - "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", - "Source available here.": "Kildekode tilgjengelig her.", - "View JavaScript license information.": "Vis JavaScript-lisensinfo.", - "Trending": "Trendsettende", - "Watch video on Youtube": "Vis video på YouTube", - "Genre: ": "Sjanger: ", - "License: ": "Lisens: ", - "Family friendly? ": "Familievennlig? ", - "Wilson score: ": "Wilson-poengsum: ", - "Engagement: ": "Engasjement: ", - "Whitelisted regions: ": "Hvitlistede regioner: ", - "Blacklisted regions: ": "Svartelistede regioner: ", - "Shared `x`": "Delt `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", - "View YouTube comments": "Vis YouTube-kommentarer", - "View more comments on Reddit": "Vis flere kommenterer på Reddit", - "View `x` comments": "Vis `x` kommentarer", - "View Reddit comments": "Vis Reddit-kommentarer", - "Hide replies": "Skjul svar", - "Show replies": "Vis svar", - "Incorrect password": "Feil passord", - "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", - "Invalid TFA code": "Ugyldig tofaktorkode", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", - "Invalid answer": "Ugyldig svar", - "Invalid CAPTCHA": "Ugyldig CAPTCHA", - "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", - "User ID is a required field": "Bruker-ID er et påkrevd felt", - "Password is a required field": "Passord er et påkrevd felt", - "Invalid username or password": "Ugyldig brukernavn eller passord", - "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", - "Password cannot be empty": "Passordet kan ikke være tomt", - "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", - "Please sign in": "Logg inn", - "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", - "channel:`x`": "kanal `x`", - "Deleted or invalid channel": "Slettet eller ugyldig kanal", - "This channel does not exist.": "Denne kanalen finnes ikke.", - "Could not get channel info.": "Kunne ikke innhente kanalinfo.", - "Could not fetch comments": "Kunne ikke hente kommentarer", - "View `x` replies": "Vis `x` svar", - "`x` ago": "`x` siden", - "Load more": "Last inn flere", - "`x` points": "`x` poeng", - "Could not create mix.": "Kunne ikke opprette miks.", - "Playlist is empty": "Spillelisten er tom", - "Invalid playlist.": "Ugyldig spilleliste.", - "Playlist does not exist.": "Spillelisten finnes ikke.", - "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", - "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", - "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", - "Invalid challenge": "Ugyldig utfordring", - "Invalid token": "Ugyldig symbol", - "Invalid user": "Ugyldig bruker", - "Token is expired, please try again": "Symbol utløpt, prøv igjen", - "English": "Engelsk", - "English (auto-generated)": "Engelsk (auto-generert)", - "Afrikaans": "", - "Albanian": "Albansk", - "Amharic": "", - "Arabic": "Arabisk", - "Armenian": "Armensk", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "Hviterussisk", - "Bosnian": "Bosnisk", - "Bulgarian": "Bulgarsk", - "Burmese": "Burmesisk", - "Catalan": "Katalansk", - "Cebuano": "", - "Chinese (Simplified)": "", - "Chinese (Traditional)": "", - "Corsican": "", - "Croatian": "", - "Czech": "Tsjekkisk", - "Danish": "Dansk", - "Dutch": "", - "Esperanto": "Esperanto", - "Estonian": "", - "Filipino": "", - "Finnish": "Finsk", - "French": "Fransk", - "Galician": "", - "Georgian": "", - "German": "", - "Greek": "", - "Gujarati": "", - "Haitian Creole": "", - "Hausa": "", - "Hawaiian": "", - "Hebrew": "", - "Hindi": "", - "Hmong": "", - "Hungarian": "Ungarsk", - "Icelandic": "Islandsk", - "Igbo": "", - "Indonesian": "Indonesisk", - "Irish": "Irsk", - "Italian": "Italiensk", - "Japanese": "Japansk", - "Javanese": "", - "Kannada": "", - "Kazakh": "", - "Khmer": "", - "Korean": "", - "Kurdish": "", - "Kyrgyz": "", - "Lao": "", - "Latin": "", - "Latvian": "", - "Lithuanian": "", - "Luxembourgish": "", - "Macedonian": "", - "Malagasy": "", - "Malay": "", - "Malayalam": "", - "Maltese": "", - "Maori": "", - "Marathi": "", - "Mongolian": "", - "Nepali": "", - "Norwegian": "Norsk bokmål", - "Nyanja": "", - "Pashto": "", - "Persian": "", - "Polish": "", - "Portuguese": "", - "Punjabi": "", - "Romanian": "", - "Russian": "Russisk", - "Samoan": "", - "Scottish Gaelic": "", - "Serbian": "Serbisk", - "Shona": "", - "Sindhi": "", - "Sinhala": "", - "Slovak": "Slovakisk", - "Slovenian": "Slovensk", - "Somali": "Somali", - "Southern Sotho": "", - "Spanish": "Spansk", - "Spanish (Latin America)": "", - "Sundanese": "", - "Swahili": "", - "Swedish": "Svensk", - "Tajik": "", - "Tamil": "", - "Telugu": "", - "Thai": "", - "Turkish": "Tyrkisk", - "Ukrainian": "Ukrainsk", - "Urdu": "", - "Uzbek": "", - "Vietnamese": "Vietnamesisk", - "Welsh": "", - "Western Frisian": "", - "Xhosa": "", - "Yiddish": "", - "Yoruba": "", - "Zulu": "", - "`x` years": "`x` år", - "`x` months": "`x` måneder", - "`x` weeks": "`x` uker", - "`x` days": "`x` dager", - "`x` hours": "`x` timer", - "`x` minutes": "`x` minutter", - "`x` seconds": "`x` sekunder", - "Fallback comments: ": "Tilbakefallskommentarer: ", - "Popular": "Pupulært", - "Top": "Topp", - "About": "Om", - "Rating: ": "Vurdering: ", - "Language: ": "Språk: ", - "Default": "Forvalg", - "Music": "Musikk", - "Gaming": "Spill", - "News": "Nyheter", - "Movies": "Filmer", - "Download": "Last ned", - "Download as: ": "Last ned som: ", - "%A %B %-d, %Y": "", - "(edited)": "(redigert)", - "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", - "`x` marked it with a ❤": "`x` levnet et ❤", - "Audio mode": "Lydmodus", - "Video mode": "Video-modus" + "`x` subscribers": "`x` abonnenter", + "`x` videos": "`x` videoer", + "LIVE": "SANNTIDSVISNING", + "Shared `x` ago": "Delt for `x` siden", + "Unsubscribe": "Opphev abonnement", + "Subscribe": "Abonner", + "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", + "View channel on YouTube": "Vis kanal på YouTube", + "newest": "nyeste", + "oldest": "eldste", + "popular": "populært", + "last": "", + "Next page": "Neste side", + "Previous page": "Forrige side", + "Clear watch history?": "Tøm visningshistorikk?", + "Yes": "Ja", + "No": "Nei", + "Import and Export Data": "Importer- og eksporter data", + "Import": "Importer", + "Import Invidious data": "Importer Invidious-data", + "Import YouTube subscriptions": "Importer YouTube-abonnenter", + "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", + "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", + "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", + "Export": "Eksporter", + "Export subscriptions as OPML": "Eksporter abonnenter som OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", + "Export data as JSON": "Eksporter data som JSON", + "Delete account?": "Slett konto?", + "History": "Historikk", + "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", + "JavaScript license information": "JavaScript-lisensinformasjon", + "source": "kilde", + "Login": "Logg inn", + "Login/Register": "Logg inn/registrer", + "Login to Google": "Logg inn med Google", + "User ID:": "Bruker-ID:", + "Password:": "Passord:", + "Time (h:mm:ss):": "Tid (h:mm:ss):", + "Text CAPTCHA": "Tekst-CAPTCHA", + "Image CAPTCHA": "Bilde-CAPTCHA", + "Sign In": "Innlogging", + "Register": "Registrer", + "Email:": "E-post:", + "Google verification code:": "Google-bekreftelseskode:", + "Preferences": "Innstillinger", + "Player preferences": "Avspillerinnstillinger", + "Always loop: ": "Alltid gjenta: ", + "Autoplay: ": "Autoavspilling: ", + "Autoplay next video: ": "Autospill neste video: ", + "Listen by default: ": "Lytt som forvalg: ", + "Default speed: ": "Forvalgt hastighet: ", + "Preferred video quality: ": "Foretrukket videokvalitet: ", + "Player volume: ": "Avspillerlydstyrke: ", + "Default comments: ": "Forvalgte kommentarer: ", + "Default captions: ": "Forvalgte undertitler: ", + "Fallback captions: ": "Tilbakefallsundertitler: ", + "Show related videos? ": "Vis relaterte videoer? ", + "Visual preferences": "Visuelle innstillinger", + "Dark mode: ": "Mørk drakt: ", + "Thin mode: ": "Tynt modus: ", + "Subscription preferences": "Abonnementsinnstillinger", + "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", + "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", + "Sort videos by: ": "Sorter videoer etter: ", + "published": "publisert", + "published - reverse": "publisert - motsatt", + "alphabetically": "alfabetisk", + "alphabetically - reverse": "alfabetisk - motsatt", + "channel name": "kanalnavn", + "channel name - reverse": "kanalnavn - motsatt", + "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", + "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", + "Only show unwatched: ": "Kun vis usette: ", + "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", + "Data preferences": "Datainnstillinger", + "Clear watch history": "Tøm visningshistorikk", + "Import/Export data": "Importer/eksporter data", + "Manage subscriptions": "Behandle abonnementer", + "Watch history": "Visningshistorikk", + "Delete account": "Slett konto", + "Administrator preferences": "Administratorinnstillinger", + "Default homepage: ": "Forvalgt hjemmeside: ", + "Feed menu: ": "Flyt-meny: ", + "Top enabled? ": "", + "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", + "Login enabled? ": "Innlogging påskrudd? ", + "Registration enabled? ": "Registrering påskrudd? ", + "Report statistics? ": "Innrapporter statistikk? ", + "Save preferences": "Lagre innstillinger", + "Subscription manager": "Abonnementsbehandler", + "`x` subscriptions": "`x` abonnementer", + "Import/Export": "Importer/eksporter", + "unsubscribe": "opphev abonnement", + "Subscriptions": "Abonnement", + "`x` unseen notifications": "`x` usette merknader", + "search": "søk", + "Sign out": "Logg ut", + "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", + "Source available here.": "Kildekode tilgjengelig her.", + "View JavaScript license information.": "Vis JavaScript-lisensinfo.", + "Trending": "Trendsettende", + "Watch video on Youtube": "Vis video på YouTube", + "Genre: ": "Sjanger: ", + "License: ": "Lisens: ", + "Family friendly? ": "Familievennlig? ", + "Wilson score: ": "Wilson-poengsum: ", + "Engagement: ": "Engasjement: ", + "Whitelisted regions: ": "Hvitlistede regioner: ", + "Blacklisted regions: ": "Svartelistede regioner: ", + "Shared `x`": "Delt `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", + "View YouTube comments": "Vis YouTube-kommentarer", + "View more comments on Reddit": "Vis flere kommenterer på Reddit", + "View `x` comments": "Vis `x` kommentarer", + "View Reddit comments": "Vis Reddit-kommentarer", + "Hide replies": "Skjul svar", + "Show replies": "Vis svar", + "Incorrect password": "Feil passord", + "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", + "Invalid TFA code": "Ugyldig tofaktorkode", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", + "Invalid answer": "Ugyldig svar", + "Invalid CAPTCHA": "Ugyldig CAPTCHA", + "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", + "User ID is a required field": "Bruker-ID er et påkrevd felt", + "Password is a required field": "Passord er et påkrevd felt", + "Invalid username or password": "Ugyldig brukernavn eller passord", + "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", + "Password cannot be empty": "Passordet kan ikke være tomt", + "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", + "Please sign in": "Logg inn", + "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", + "channel:`x`": "kanal `x`", + "Deleted or invalid channel": "Slettet eller ugyldig kanal", + "This channel does not exist.": "Denne kanalen finnes ikke.", + "Could not get channel info.": "Kunne ikke innhente kanalinfo.", + "Could not fetch comments": "Kunne ikke hente kommentarer", + "View `x` replies": "Vis `x` svar", + "`x` ago": "`x` siden", + "Load more": "Last inn flere", + "`x` points": "`x` poeng", + "Could not create mix.": "Kunne ikke opprette miks.", + "Playlist is empty": "Spillelisten er tom", + "Invalid playlist.": "Ugyldig spilleliste.", + "Playlist does not exist.": "Spillelisten finnes ikke.", + "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", + "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", + "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", + "Invalid challenge": "Ugyldig utfordring", + "Invalid token": "Ugyldig symbol", + "Invalid user": "Ugyldig bruker", + "Token is expired, please try again": "Symbol utløpt, prøv igjen", + "English": "Engelsk", + "English (auto-generated)": "Engelsk (auto-generert)", + "Afrikaans": "", + "Albanian": "Albansk", + "Amharic": "", + "Arabic": "Arabisk", + "Armenian": "Armensk", + "Azerbaijani": "", + "Bangla": "", + "Basque": "", + "Belarusian": "Hviterussisk", + "Bosnian": "Bosnisk", + "Bulgarian": "Bulgarsk", + "Burmese": "Burmesisk", + "Catalan": "Katalansk", + "Cebuano": "", + "Chinese (Simplified)": "", + "Chinese (Traditional)": "", + "Corsican": "", + "Croatian": "", + "Czech": "Tsjekkisk", + "Danish": "Dansk", + "Dutch": "", + "Esperanto": "Esperanto", + "Estonian": "", + "Filipino": "", + "Finnish": "Finsk", + "French": "Fransk", + "Galician": "", + "Georgian": "", + "German": "", + "Greek": "", + "Gujarati": "", + "Haitian Creole": "", + "Hausa": "", + "Hawaiian": "", + "Hebrew": "", + "Hindi": "", + "Hmong": "", + "Hungarian": "Ungarsk", + "Icelandic": "Islandsk", + "Igbo": "", + "Indonesian": "Indonesisk", + "Irish": "Irsk", + "Italian": "Italiensk", + "Japanese": "Japansk", + "Javanese": "", + "Kannada": "", + "Kazakh": "", + "Khmer": "", + "Korean": "", + "Kurdish": "", + "Kyrgyz": "", + "Lao": "", + "Latin": "", + "Latvian": "", + "Lithuanian": "", + "Luxembourgish": "", + "Macedonian": "", + "Malagasy": "", + "Malay": "", + "Malayalam": "", + "Maltese": "", + "Maori": "", + "Marathi": "", + "Mongolian": "", + "Nepali": "", + "Norwegian": "Norsk bokmål", + "Nyanja": "", + "Pashto": "", + "Persian": "", + "Polish": "", + "Portuguese": "", + "Punjabi": "", + "Romanian": "", + "Russian": "Russisk", + "Samoan": "", + "Scottish Gaelic": "", + "Serbian": "Serbisk", + "Shona": "", + "Sindhi": "", + "Sinhala": "", + "Slovak": "Slovakisk", + "Slovenian": "Slovensk", + "Somali": "Somali", + "Southern Sotho": "", + "Spanish": "Spansk", + "Spanish (Latin America)": "", + "Sundanese": "", + "Swahili": "", + "Swedish": "Svensk", + "Tajik": "", + "Tamil": "", + "Telugu": "", + "Thai": "", + "Turkish": "Tyrkisk", + "Ukrainian": "Ukrainsk", + "Urdu": "", + "Uzbek": "", + "Vietnamese": "Vietnamesisk", + "Welsh": "", + "Western Frisian": "", + "Xhosa": "", + "Yiddish": "", + "Yoruba": "", + "Zulu": "", + "`x` years": "`x` år", + "`x` months": "`x` måneder", + "`x` weeks": "`x` uker", + "`x` days": "`x` dager", + "`x` hours": "`x` timer", + "`x` minutes": "`x` minutter", + "`x` seconds": "`x` sekunder", + "Fallback comments: ": "Tilbakefallskommentarer: ", + "Popular": "Pupulært", + "Top": "Topp", + "About": "Om", + "Rating: ": "Vurdering: ", + "Language: ": "Språk: ", + "Default": "Forvalg", + "Music": "Musikk", + "Gaming": "Spill", + "News": "Nyheter", + "Movies": "Filmer", + "Download": "Last ned", + "Download as: ": "Last ned som: ", + "%A %B %-d, %Y": "", + "(edited)": "(redigert)", + "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", + "`x` marked it with a ❤": "`x` levnet et ❤", + "Audio mode": "Lydmodus", + "Video mode": "Video-modus", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/nl.json b/locales/nl.json index 2224d326..b4ea3862 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -10,8 +10,9 @@ "newest": "nieuwste", "oldest": "oudste", "popular": "populair", - "Preview page": "Pagina voorvertonen", + "last": "", "Next page": "Volgende pagina", + "Previous page": "Vorige pagina", "Clear watch history?": "Kijk geschiedenis wissen?", "Yes": "Ja", "No": "Nee", @@ -28,7 +29,6 @@ "Export data as JSON": "Exporteer gegevens als JSON", "Delete account?": "Verwijder account?", "History": "Geschiedenis", - "Previous page": "Vorige pagina", "An alternative front-end to YouTube": "Een alternatieve front-end voor YouTube", "JavaScript license information": "JavaScript licentie informatie", "source": "bron", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/pl.json b/locales/pl.json index d9a21cf1..0db43c51 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -10,8 +10,9 @@ "newest": "najnowsze", "oldest": "najstarsze", "popular": "popularne", - "Preview page": "Podgląd strony", + "last": "", "Next page": "Następna strona", + "Previous page": "Poprzednia strona", "Clear watch history?": "Wyczyścić historię?", "Yes": "Tak", "No": "Nie", @@ -28,7 +29,6 @@ "Export data as JSON": "Eksportuj dane jako JSON", "Delete account?": "Usunąć konto?", "History": "Historia", - "Previous page": "Poprzednia strona", "An alternative front-end to YouTube": "Alternatywny front-end dla YouTube", "JavaScript license information": "Informacja o licencji JavaScript", "source": "źródło", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "Odnośnik bezpośredni do komentarza na YouTube", "`x` marked it with a ❤": "", "Audio mode": "Tryb audio", - "Video mode": "Tryb wideo" + "Video mode": "Tryb wideo", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/ru.json b/locales/ru.json index 0c666589..222b693f 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,294 +1,293 @@ { - "`x` subscribers": "`x` подписчиков", - "`x` videos": "`x` видео", - "LIVE": "ПРЯМОЙ ЭФИР", - "Shared `x` ago": "Опубликовано `x` назад", - "Unsubscribe": "Отписаться", - "Subscribe": "Подписаться", - "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", - "View channel on YouTube": "Канал на YouTube", - "newest": "новые", - "oldest": "старые", - "popular": "популярные", - "Preview page": "Предварительный просмотр", - "Next page": "Следующая страница", - "Clear watch history?": "Очистить историю просмотров?", - "Yes": "Да", - "No": "Нет", - "Import and Export Data": "Импорт и экспорт данных", - "Import": "Импорт", - "Import Invidious data": "Импортировать данные Invidious", - "Import YouTube subscriptions": "Импортировать YouTube подписки", - "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", - "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", - "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", - "Export": "Экспорт", - "Export subscriptions as OPML": "Экспортировать подписки в OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", - "Export data as JSON": "Экспортировать данные в JSON", - "Delete account?": "Удалить аккаунт?", - "History": "История", - "Previous page": "Предыдущая страница", - "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", - "JavaScript license information": "Лицензии JavaScript", - "source": "источник", - "Login": "Войти", - "Login/Register": "Войти/Регистрация", - "Login to Google": "Войти через Google", - "User ID:": "ID пользователя:", - "Password:": "Пароль:", - "Time (h:mm:ss):": "Время (ч:мм:сс):", - "Text CAPTCHA": "Текст капчи", - "Image CAPTCHA": "Изображение капчи", - "Sign In": "Войти", - "Register": "Регистрация", - "Email:": "Эл. почта:", - "Google verification code:": "Код подтверждения Google:", - "Preferences": "Настройки", - "Player preferences": "Настройки проигрывателя", - "Always loop: ": "Всегда повторять: ", - "Autoplay: ": "Автовоспроизведение: ", - "Autoplay next video: ": "Автовоспроизведение следующего видео: ", - "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", - "Default speed: ": "Скорость по-умолчанию: ", - "Preferred video quality: ": "Предпочтительное качество видео: ", - "Player volume: ": "Громкость воспроизведения: ", - "Default comments: ": "Источник комментариев: ", - "youtube": "YouTube", - "reddit": "Reddit", - "Default captions: ": "Субтитры по-умолчанию: ", - "Fallback captions: ": "Резервные субтитры: ", - "Show related videos? ": "Показывать похожие видео? ", - "Visual preferences": "Визуальные настройки", - "Dark mode: ": "Темная тема: ", - "Thin mode: ": "Облегченный режим: ", - "Subscription preferences": "Настройки подписок", - "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", - "Number of videos shown in feed: ": "Число видео в ленте: ", - "Sort videos by: ": "Сортировать видео по: ", - "published": "дате публикации", - "published - reverse": "дате - обратный порядок", - "alphabetically": "алфавиту", - "alphabetically - reverse": "алфавиту - обратный порядок", - "channel name": "имени канала", - "channel name - reverse": "имени канала - обратный порядок", - "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", - "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", - "Only show unwatched: ": "Отображать только непросмотренные видео: ", - "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", - "Data preferences": "Настройки данных", - "Clear watch history": "Очистить историю просмотра", - "Import/Export data": "Импорт/Экспорт данных", - "Manage subscriptions": "Управление подписками", - "Watch history": "История просмотров", - "Delete account": "Удалить аккаунт", - "Administrator preferences": "Настройки администратора", - "Default homepage: ": "Главная страница по умолчанию: ", - "Feed menu: ": "Меню ленты: ", - "Top enabled? ": "Включить ТОП? ", - "CAPTCHA enabled? ": "Включить капчу? ", - "Login enabled? ": "Включить логин? ", - "Registration enabled? ": "Включить регистрацию? ", - "Report statistics? ": "Отображать статистику? ", - "Save preferences": "Сохранить настройки", - "Subscription manager": "Менеджер подписок", - "`x` subscriptions": "`x` подписок", - "Import/Export": "Импорт/Экспорт", - "unsubscribe": "отписаться", - "Subscriptions": "Подписки", - "`x` unseen notifications": "`x` новых оповещений", - "search": "поиск", - "Sign out": "Выйти", - "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", - "Source available here.": "Исходный код доступен здесь.", - "Liberapay: ": "Liberapay: ", - "Patreon: ": "Patreon: ", - "BTC: ": "BTC: ", - "BCH: ": "BCH: ", - "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", - "Trending": "В тренде", - "Watch video on Youtube": "Смотреть на YouTube", - "Genre: ": "Жанр: ", - "License: ": "Лицензия: ", - "Family friendly? ": "Семейный просмотр: ", - "Wilson score: ": "Рейтинг Вильсона: ", - "Engagement: ": "Вовлеченность: ", - "Whitelisted regions: ": "Доступно для: ", - "Blacklisted regions: ": "Недоступно для: ", - "Shared `x`": "Опубликовано `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", - "View YouTube comments": "Смотреть комментарии с YouTube", - "View more comments on Reddit": "Больше комментариев на Reddit", - "View `x` comments": "Показать `x` комментариев", - "View Reddit comments": "Смотреть комментарии с Reddit", - "Hide replies": "Скрыть ответы", - "Show replies": "Показать ответы", - "Incorrect password": "Неправильный пароль", - "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", - "Invalid TFA code": "Неправильный TFA код", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", - "Invalid answer": "Неверный ответ", - "Invalid CAPTCHA": "Неверная капча", - "CAPTCHA is a required field": "Необходимо ввести капчу", - "User ID is a required field": "Необходимо ввести идентификатор пользователя", - "Password is a required field": "Необходимо ввести пароль", - "Invalid username or password": "Недопустимый пароль или имя пользователя", - "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", - "Password cannot be empty": "Пароль не может быть пустым", - "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", - "Please sign in": "Пожалуйста, войдите", - "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", - "channel:`x`": "канал: `x`", - "Deleted or invalid channel": "Канал удален или не найден", - "This channel does not exist.": "Такой канал не существует.", - "Could not get channel info.": "Невозможно получить информацию о канале.", - "Could not fetch comments": "Невозможно получить комментарии", - "View `x` replies": "Показать `x` ответов", - "`x` ago": "`x` назад", - "Load more": "Загрузить больше", - "`x` points": "`x` очков", - "Could not create mix.": "Невозможно создать \"микс\".", - "Playlist is empty": "Плейлист пуст", - "Invalid playlist.": "Некорректный плейлист.", - "Playlist does not exist.": "Плейлист не существует.", - "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", - "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", - "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", - "Invalid challenge": "Неправильный ответ в \"challenge\"", - "Invalid token": "Неправильный токен", - "Invalid user": "Недопустимое имя пользователя", - "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", - "English": "Английский", - "English (auto-generated)": "Английский (созданы автоматически)", - "Afrikaans": "Африкаанс", - "Albanian": "Албанский", - "Amharic": "Амхарский", - "Arabic": "Арабский", - "Armenian": "Армянский", - "Azerbaijani": "Азербайджанский", - "Bangla": "Бенгальский", - "Basque": "Баскский", - "Belarusian": "Белорусский", - "Bosnian": "Боснийский", - "Bulgarian": "Болгарский", - "Burmese": "Бирманский", - "Catalan": "Каталонский", - "Cebuano": "Себуанский", - "Chinese (Simplified)": "Китайский (упрощенный)", - "Chinese (Traditional)": "Китайский (традиционный)", - "Corsican": "Корсиканский", - "Croatian": "Хорватский", - "Czech": "Чешский", - "Danish": "Датский", - "Dutch": "Нидерландский", - "Esperanto": "Эсперанто", - "Estonian": "Эстонский", - "Filipino": "Филиппинский", - "Finnish": "Финский", - "French": "Французский", - "Galician": "Галисийский", - "Georgian": "Грузинский", - "German": "Немецкий", - "Greek": "Греческий", - "Gujarati": "Гуджаратский", - "Haitian Creole": "Гаит. креольский", - "Hausa": "Хауса", - "Hawaiian": "Гавайский", - "Hebrew": "Иврит", - "Hindi": "Хинди", - "Hmong": "Хмонг (мяо)", - "Hungarian": "Венгерский", - "Icelandic": "Исландский", - "Igbo": "Игбо", - "Indonesian": "Индонезийский", - "Irish": "Ирландский", - "Italian": "Итальянский", - "Japanese": "Японский", - "Javanese": "Яванский", - "Kannada": "Каннада", - "Kazakh": "Казахский", - "Khmer": "Кхмерский", - "Korean": "Корейский", - "Kurdish": "Курдский", - "Kyrgyz": "Киргизский", - "Lao": "Лаосский", - "Latin": "Латинский", - "Latvian": "Латышский", - "Lithuanian": "Литовский", - "Luxembourgish": "Люксембургский", - "Macedonian": "Македонский", - "Malagasy": "Малагасийский", - "Malay": "Малайский", - "Malayalam": "Малаялам", - "Maltese": "Мальтийский", - "Maori": "Маори", - "Marathi": "Маратхи", - "Mongolian": "Монгольская", - "Nepali": "Непальский", - "Norwegian": "Норвежский", - "Nyanja": "Ньянджа", - "Pashto": "Пушту", - "Persian": "Персидский", - "Polish": "Польский", - "Portuguese": "Португальский", - "Punjabi": "Панджаби", - "Romanian": "Румынский", - "Russian": "Русский", - "Samoan": "Самоанский", - "Scottish Gaelic": "Шотландский (гэльский)", - "Serbian": "Сербский", - "Shona": "Шона", - "Sindhi": "Синдхи", - "Sinhala": "Сингальский", - "Slovak": "Словацкий", - "Slovenian": "Словенский", - "Somali": "Сомалийский", - "Southern Sotho": "Сесото (южный сото)", - "Spanish": "Испанский", - "Spanish (Latin America)": "Испанский (Латинская Америка)", - "Sundanese": "Сунданский", - "Swahili": "Суахили", - "Swedish": "Шведский", - "Tajik": "Таджикский", - "Tamil": "Тамильский", - "Telugu": "Телугу", - "Thai": "Тайский", - "Turkish": "Турецкий", - "Ukrainian": "Украинский", - "Urdu": "Урду", - "Uzbek": "Узбекский", - "Vietnamese": "Вьетнамский", - "Welsh": "Валлийский", - "Western Frisian": "Западнофризский", - "Xhosa": "Коса", - "Yiddish": "Идиш", - "Yoruba": "Йоруба", - "Zulu": "Зулусский", - "`x` years": "`x` лет", - "`x` months": "`x` месяцев", - "`x` weeks": "`x` недель", - "`x` days": "`x` дней", - "`x` hours": "`x` часов", - "`x` minutes": "`x` минут", - "`x` seconds": "`x` секунд", - "Fallback comments: ": "Резервные комментарии: ", - "Popular": "Популярное", - "Top": "Топ", - "About": "О сайте", - "Rating: ": "Рейтинг: ", - "Language: ": "Язык: ", - "Default": "По-умолчанию", - "Music": "Музыка", - "Gaming": "Игры", - "News": "Новости", - "Movies": "Фильмы", - "Download": "Скачать", - "Download as: ": "Скачать как: ", - "%A %B %-d, %Y": "%-d %B %Y, %A", - "(edited)": "(изменено)", - "Youtube permalink of the comment": "Прямая ссылка на YouTube", - "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", - "Audio mode": "Аудио режим", - "Video mode": "Видео режим" + "`x` subscribers": "`x` подписчиков", + "`x` videos": "`x` видео", + "LIVE": "ПРЯМОЙ ЭФИР", + "Shared `x` ago": "Опубликовано `x` назад", + "Unsubscribe": "Отписаться", + "Subscribe": "Подписаться", + "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", + "View channel on YouTube": "Канал на YouTube", + "newest": "новые", + "oldest": "старые", + "popular": "популярные", + "last": "", + "Next page": "Следующая страница", + "Previous page": "Предыдущая страница", + "Clear watch history?": "Очистить историю просмотров?", + "Yes": "Да", + "No": "Нет", + "Import and Export Data": "Импорт и экспорт данных", + "Import": "Импорт", + "Import Invidious data": "Импортировать данные Invidious", + "Import YouTube subscriptions": "Импортировать YouTube подписки", + "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", + "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", + "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", + "Export": "Экспорт", + "Export subscriptions as OPML": "Экспортировать подписки в OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", + "Export data as JSON": "Экспортировать данные в JSON", + "Delete account?": "Удалить аккаунт?", + "History": "История", + "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", + "JavaScript license information": "Лицензии JavaScript", + "source": "источник", + "Login": "Войти", + "Login/Register": "Войти/Регистрация", + "Login to Google": "Войти через Google", + "User ID:": "ID пользователя:", + "Password:": "Пароль:", + "Time (h:mm:ss):": "Время (ч:мм:сс):", + "Text CAPTCHA": "Текст капчи", + "Image CAPTCHA": "Изображение капчи", + "Sign In": "Войти", + "Register": "Регистрация", + "Email:": "Эл. почта:", + "Google verification code:": "Код подтверждения Google:", + "Preferences": "Настройки", + "Player preferences": "Настройки проигрывателя", + "Always loop: ": "Всегда повторять: ", + "Autoplay: ": "Автовоспроизведение: ", + "Autoplay next video: ": "Автовоспроизведение следующего видео: ", + "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", + "Default speed: ": "Скорость по-умолчанию: ", + "Preferred video quality: ": "Предпочтительное качество видео: ", + "Player volume: ": "Громкость воспроизведения: ", + "Default comments: ": "Источник комментариев: ", + "youtube": "YouTube", + "reddit": "Reddit", + "Default captions: ": "Субтитры по-умолчанию: ", + "Fallback captions: ": "Резервные субтитры: ", + "Show related videos? ": "Показывать похожие видео? ", + "Visual preferences": "Визуальные настройки", + "Dark mode: ": "Темная тема: ", + "Thin mode: ": "Облегченный режим: ", + "Subscription preferences": "Настройки подписок", + "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", + "Number of videos shown in feed: ": "Число видео в ленте: ", + "Sort videos by: ": "Сортировать видео по: ", + "published": "дате публикации", + "published - reverse": "дате - обратный порядок", + "alphabetically": "алфавиту", + "alphabetically - reverse": "алфавиту - обратный порядок", + "channel name": "имени канала", + "channel name - reverse": "имени канала - обратный порядок", + "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", + "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", + "Only show unwatched: ": "Отображать только непросмотренные видео: ", + "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", + "Data preferences": "Настройки данных", + "Clear watch history": "Очистить историю просмотра", + "Import/Export data": "Импорт/Экспорт данных", + "Manage subscriptions": "Управление подписками", + "Watch history": "История просмотров", + "Delete account": "Удалить аккаунт", + "Administrator preferences": "Настройки администратора", + "Default homepage: ": "Главная страница по умолчанию: ", + "Feed menu: ": "Меню ленты: ", + "Top enabled? ": "Включить ТОП? ", + "CAPTCHA enabled? ": "Включить капчу? ", + "Login enabled? ": "Включить логин? ", + "Registration enabled? ": "Включить регистрацию? ", + "Report statistics? ": "Отображать статистику? ", + "Save preferences": "Сохранить настройки", + "Subscription manager": "Менеджер подписок", + "`x` subscriptions": "`x` подписок", + "Import/Export": "Импорт/Экспорт", + "unsubscribe": "отписаться", + "Subscriptions": "Подписки", + "`x` unseen notifications": "`x` новых оповещений", + "search": "поиск", + "Sign out": "Выйти", + "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", + "Source available here.": "Исходный код доступен здесь.", + "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", + "Trending": "В тренде", + "Watch video on Youtube": "Смотреть на YouTube", + "Genre: ": "Жанр: ", + "License: ": "Лицензия: ", + "Family friendly? ": "Семейный просмотр: ", + "Wilson score: ": "Рейтинг Вильсона: ", + "Engagement: ": "Вовлеченность: ", + "Whitelisted regions: ": "Доступно для: ", + "Blacklisted regions: ": "Недоступно для: ", + "Shared `x`": "Опубликовано `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", + "View YouTube comments": "Смотреть комментарии с YouTube", + "View more comments on Reddit": "Больше комментариев на Reddit", + "View `x` comments": "Показать `x` комментариев", + "View Reddit comments": "Смотреть комментарии с Reddit", + "Hide replies": "Скрыть ответы", + "Show replies": "Показать ответы", + "Incorrect password": "Неправильный пароль", + "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", + "Invalid TFA code": "Неправильный TFA код", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", + "Invalid answer": "Неверный ответ", + "Invalid CAPTCHA": "Неверная капча", + "CAPTCHA is a required field": "Необходимо ввести капчу", + "User ID is a required field": "Необходимо ввести идентификатор пользователя", + "Password is a required field": "Необходимо ввести пароль", + "Invalid username or password": "Недопустимый пароль или имя пользователя", + "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", + "Password cannot be empty": "Пароль не может быть пустым", + "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", + "Please sign in": "Пожалуйста, войдите", + "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", + "channel:`x`": "канал: `x`", + "Deleted or invalid channel": "Канал удален или не найден", + "This channel does not exist.": "Такой канал не существует.", + "Could not get channel info.": "Невозможно получить информацию о канале.", + "Could not fetch comments": "Невозможно получить комментарии", + "View `x` replies": "Показать `x` ответов", + "`x` ago": "`x` назад", + "Load more": "Загрузить больше", + "`x` points": "`x` очков", + "Could not create mix.": "Невозможно создать \"микс\".", + "Playlist is empty": "Плейлист пуст", + "Invalid playlist.": "Некорректный плейлист.", + "Playlist does not exist.": "Плейлист не существует.", + "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", + "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", + "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", + "Invalid challenge": "Неправильный ответ в \"challenge\"", + "Invalid token": "Неправильный токен", + "Invalid user": "Недопустимое имя пользователя", + "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", + "English": "Английский", + "English (auto-generated)": "Английский (созданы автоматически)", + "Afrikaans": "Африкаанс", + "Albanian": "Албанский", + "Amharic": "Амхарский", + "Arabic": "Арабский", + "Armenian": "Армянский", + "Azerbaijani": "Азербайджанский", + "Bangla": "Бенгальский", + "Basque": "Баскский", + "Belarusian": "Белорусский", + "Bosnian": "Боснийский", + "Bulgarian": "Болгарский", + "Burmese": "Бирманский", + "Catalan": "Каталонский", + "Cebuano": "Себуанский", + "Chinese (Simplified)": "Китайский (упрощенный)", + "Chinese (Traditional)": "Китайский (традиционный)", + "Corsican": "Корсиканский", + "Croatian": "Хорватский", + "Czech": "Чешский", + "Danish": "Датский", + "Dutch": "Нидерландский", + "Esperanto": "Эсперанто", + "Estonian": "Эстонский", + "Filipino": "Филиппинский", + "Finnish": "Финский", + "French": "Французский", + "Galician": "Галисийский", + "Georgian": "Грузинский", + "German": "Немецкий", + "Greek": "Греческий", + "Gujarati": "Гуджаратский", + "Haitian Creole": "Гаит. креольский", + "Hausa": "Хауса", + "Hawaiian": "Гавайский", + "Hebrew": "Иврит", + "Hindi": "Хинди", + "Hmong": "Хмонг (мяо)", + "Hungarian": "Венгерский", + "Icelandic": "Исландский", + "Igbo": "Игбо", + "Indonesian": "Индонезийский", + "Irish": "Ирландский", + "Italian": "Итальянский", + "Japanese": "Японский", + "Javanese": "Яванский", + "Kannada": "Каннада", + "Kazakh": "Казахский", + "Khmer": "Кхмерский", + "Korean": "Корейский", + "Kurdish": "Курдский", + "Kyrgyz": "Киргизский", + "Lao": "Лаосский", + "Latin": "Латинский", + "Latvian": "Латышский", + "Lithuanian": "Литовский", + "Luxembourgish": "Люксембургский", + "Macedonian": "Македонский", + "Malagasy": "Малагасийский", + "Malay": "Малайский", + "Malayalam": "Малаялам", + "Maltese": "Мальтийский", + "Maori": "Маори", + "Marathi": "Маратхи", + "Mongolian": "Монгольская", + "Nepali": "Непальский", + "Norwegian": "Норвежский", + "Nyanja": "Ньянджа", + "Pashto": "Пушту", + "Persian": "Персидский", + "Polish": "Польский", + "Portuguese": "Португальский", + "Punjabi": "Панджаби", + "Romanian": "Румынский", + "Russian": "Русский", + "Samoan": "Самоанский", + "Scottish Gaelic": "Шотландский (гэльский)", + "Serbian": "Сербский", + "Shona": "Шона", + "Sindhi": "Синдхи", + "Sinhala": "Сингальский", + "Slovak": "Словацкий", + "Slovenian": "Словенский", + "Somali": "Сомалийский", + "Southern Sotho": "Сесото (южный сото)", + "Spanish": "Испанский", + "Spanish (Latin America)": "Испанский (Латинская Америка)", + "Sundanese": "Сунданский", + "Swahili": "Суахили", + "Swedish": "Шведский", + "Tajik": "Таджикский", + "Tamil": "Тамильский", + "Telugu": "Телугу", + "Thai": "Тайский", + "Turkish": "Турецкий", + "Ukrainian": "Украинский", + "Urdu": "Урду", + "Uzbek": "Узбекский", + "Vietnamese": "Вьетнамский", + "Welsh": "Валлийский", + "Western Frisian": "Западнофризский", + "Xhosa": "Коса", + "Yiddish": "Идиш", + "Yoruba": "Йоруба", + "Zulu": "Зулусский", + "`x` years": "`x` лет", + "`x` months": "`x` месяцев", + "`x` weeks": "`x` недель", + "`x` days": "`x` дней", + "`x` hours": "`x` часов", + "`x` minutes": "`x` минут", + "`x` seconds": "`x` секунд", + "Fallback comments: ": "Резервные комментарии: ", + "Popular": "Популярное", + "Top": "Топ", + "About": "О сайте", + "Rating: ": "Рейтинг: ", + "Language: ": "Язык: ", + "Default": "По-умолчанию", + "Music": "Музыка", + "Gaming": "Игры", + "News": "Новости", + "Movies": "Фильмы", + "Download": "Скачать", + "Download as: ": "Скачать как: ", + "%A %B %-d, %Y": "%-d %B %Y, %A", + "(edited)": "(изменено)", + "Youtube permalink of the comment": "Прямая ссылка на YouTube", + "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", + "Audio mode": "Аудио режим", + "Video mode": "Видео режим", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index 0f705836..de44dd3e 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -109,15 +109,15 @@
- <%= translate(locale, "BTC: ") %>356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
+ BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
- <%= translate(locale, "BCH: ") %>qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk
+ BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk
From bf11a46abe07ba832f7df45c3ef33a9f3a2ad3b2 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 10:48:38 -0500 Subject: [PATCH 12/19] Bump expire time for pubsub --- src/invidious.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 2d1d88ce..a5fc3658 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2321,7 +2321,8 @@ get "/feed/webhook/:token" do |env| data = "#{time}" end - if Time.now.to_unix - time.to_i > 600 + # The hub will sometimes check if we're still subscribed after delivery errors + if Time.now.to_unix - time.to_i > 432000 halt env, status_code: 400 end From fdc014af6781a306efe91ecd317e13bffb0a282a Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 11:43:48 -0500 Subject: [PATCH 13/19] Add '&local=true' to watch and embed pages --- src/invidious.cr | 56 ++++++++++++++++------- src/invidious/videos.cr | 7 ++- src/invidious/views/components/player.ecr | 6 +-- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index a5fc3658..6ea4b54b 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -396,6 +396,12 @@ get "/watch" do |env| fmt_stream = video.fmt_stream(decrypt_function) adaptive_fmts = video.adaptive_fmts(decrypt_function) + + if params[:local] + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + end + video_streams = video.video_streams(adaptive_fmts) audio_streams = video.audio_streams(adaptive_fmts) @@ -496,6 +502,12 @@ get "/embed/:id" do |env| fmt_stream = video.fmt_stream(decrypt_function) adaptive_fmts = video.adaptive_fmts(decrypt_function) + + if params[:local] + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + end + video_streams = video.video_streams(adaptive_fmts) audio_streams = video.audio_streams(adaptive_fmts) @@ -4209,24 +4221,32 @@ get "/videoplayback" do |env| query_params = env.params.query fvip = query_params["fvip"]? || "3" - mn = query_params["mn"].split(",")[-1] + mn = query_params["mn"].split(",").pop host = "https://r#{fvip}---#{mn}.googlevideo.com" url = "/videoplayback?#{query_params.to_s}" - headers = env.request.headers - headers.delete("Host") - headers.delete("Cookie") - headers.delete("User-Agent") - headers.delete("Referer") + headers = HTTP::Headers.new + {"Range", "Accept", "Accept-Encoding"}.each do |header| + if env.request.headers[header]? + headers[header] = env.request.headers[header] + end + end region = query_params["region"]? response = HTTP::Client::Response.new(403) - loop do + 5.times do begin client = make_client(URI.parse(host), proxies, region) response = client.head(url, headers) break + rescue Socket::Addrinfo::Error + if fvip == "3" + break + end + + fvip = "3" + host = "https://r#{fvip}---#{mn}.googlevideo.com" rescue ex end end @@ -4284,11 +4304,12 @@ get "/ggpht/*" do |env| client = make_client(URI.parse(host)) url = env.request.path.lchop("/ggpht") - headers = env.request.headers - headers.delete("Host") - headers.delete("Cookie") - headers.delete("User-Agent") - headers.delete("Referer") + headers = HTTP::Headers.new + {"Range", "Accept", "Accept-Encoding"}.each do |header| + if env.request.headers[header]? + headers[header] = env.request.headers[header] + end + end client.get(url, headers) do |response| env.response.status_code = response.status_code @@ -4342,11 +4363,12 @@ get "/vi/:id/:name" do |env| end url = "/vi/#{id}/#{name}" - headers = env.request.headers - headers.delete("Host") - headers.delete("Cookie") - headers.delete("User-Agent") - headers.delete("Referer") + headers = HTTP::Headers.new + {"Range", "Accept", "Accept-Encoding"}.each do |header| + if env.request.headers[header]? + headers[header] = env.request.headers[header] + end + end client.get(url, headers) do |response| env.response.status_code = response.status_code diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index cd24afdc..b5bd2083 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -731,11 +731,12 @@ end def process_video_params(query, preferences) autoplay = query["autoplay"]?.try &.to_i? continue = query["continue"]?.try &.to_i? - related_videos = query["related_videos"]? listen = query["listen"]? && (query["listen"] == "true" || query["listen"] == "1").to_unsafe + local = query["local"]? && (query["local"] == "true").to_unsafe preferred_captions = query["subtitles"]?.try &.split(",").map { |a| a.downcase } quality = query["quality"]? region = query["region"]? + related_videos = query["related_videos"]? speed = query["speed"]?.try &.to_f? video_loop = query["loop"]?.try &.to_i? volume = query["volume"]?.try &.to_i? @@ -765,8 +766,9 @@ def process_video_params(query, preferences) autoplay = autoplay == 1 continue = continue == 1 - related_videos = related_videos == 1 listen = listen == 1 + local = local == 1 + related_videos = related_videos == 1 video_loop = video_loop == 1 if query["t"]? @@ -799,6 +801,7 @@ def process_video_params(query, preferences) continue: continue, controls: controls, listen: listen, + local: local, preferred_captions: preferred_captions, quality: quality, raw: raw, diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index e2cfa3a1..cecb9b2f 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -11,7 +11,7 @@ <% else %> <% if params[:listen] %> <% audio_streams.each_with_index do |fmt, i| %> - " type='<%= fmt["type"] %>' label="<%= fmt["bitrate"] %>k" selected="<%= i == 0 ? true : false %>"> + <% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["bitrate"] %>k" selected="<%= i == 0 ? true : false %>"> <% end %> <% else %> <% if params[:quality] == "dash" %> @@ -19,9 +19,9 @@ <% end %> <% fmt_stream.each_with_index do |fmt, i| %> <% if params[:quality] %> - " type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= params[:quality] == fmt["label"].split(" - ")[0] %>"> + <% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= params[:quality] == fmt["label"].split(" - ")[0] %>"> <% else %> - " type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= i == 0 ? true : false %>"> + <% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= i == 0 ? true : false %>"> <% end %> <% end %> <% end %> From 46e985b306b06fe37cd066cc1ecd69b504316fb0 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 12:44:25 -0500 Subject: [PATCH 14/19] Add 'dark_mode', 'thin_mode' as query parameters --- src/invidious.cr | 153 +++++++++++----------- src/invidious/views/components/player.ecr | 4 +- src/invidious/views/template.ecr | 4 +- src/invidious/views/watch.ecr | 10 +- 4 files changed, 84 insertions(+), 87 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 6ea4b54b..998b9291 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -192,6 +192,7 @@ proxies = PROXY_LIST before_all do |env| env.response.headers["X-XSS-Protection"] = "1; mode=block;" env.response.headers["X-Content-Type-Options"] = "nosniff" + preferences = DEFAULT_USER_PREFERENCES.dup if env.request.cookies.has_key? "SID" headers = HTTP::Headers.new @@ -210,9 +211,9 @@ before_all do |env| env.set "challenge", challenge env.set "token", token - locale = user.preferences.locale + preferences = user.preferences + env.set "user", user - env.set "preferences", user.preferences env.set "sid", sid end else @@ -223,9 +224,9 @@ before_all do |env| env.set "challenge", challenge env.set "token", token - locale = user.preferences.locale + preferences = user.preferences + env.set "user", user - env.set "preferences", user.preferences env.set "sid", sid rescue ex end @@ -234,14 +235,20 @@ before_all do |env| if env.request.cookies.has_key? "PREFS" preferences = Preferences.from_json(env.request.cookies["PREFS"].value) - - locale = preferences.locale - env.set "preferences", preferences end - locale = env.params.query["hl"]? || locale - locale ||= "en-US" - env.set "locale", locale + dark_mode = env.params.query["dark_mode"]? || preferences.dark_mode.to_s + dark_mode = dark_mode == "true" + + thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s + thin_mode = thin_mode == "true" + + locale = env.params.query["hl"]? || preferences.locale + + preferences.dark_mode = dark_mode + preferences.thin_mode = thin_mode + preferences.locale = locale + env.set "preferences", preferences current_page = env.request.path if env.request.query @@ -258,7 +265,7 @@ before_all do |env| end get "/" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -285,14 +292,14 @@ get "/" do |env| end get "/licenses" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? rendered "licenses" end # Videos get "/watch" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? if env.params.query.to_s.includes?("%20") || env.params.query.to_s.includes?("+") @@ -464,7 +471,7 @@ get "/watch" do |env| end get "/embed/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? id = env.params.url["id"] if id.includes?("%20") || id.includes?("+") || env.params.query.to_s.includes?("%20") || env.params.query.to_s.includes?("+") @@ -558,7 +565,7 @@ end # Playlists get "/playlist" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? plid = env.params.query["list"]? if !plid @@ -589,7 +596,7 @@ get "/playlist" do |env| end get "/mix" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? rdid = env.params.query["list"]? if !rdid @@ -612,7 +619,7 @@ end # Search get "/opensearch.xml" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/opensearchdescription+xml" host = make_host_url(config, Kemal.config) @@ -630,7 +637,7 @@ get "/opensearch.xml" do |env| end get "/results" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? query = env.params.query["search_query"]? query ||= env.params.query["q"]? @@ -647,7 +654,7 @@ get "/results" do |env| end get "/search" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? query = env.params.query["search_query"]? @@ -733,7 +740,7 @@ end # Users get "/login" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -779,7 +786,7 @@ end # See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L79 post "/login" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? referer = get_referer(env, "/feed/subscriptions") @@ -1156,7 +1163,7 @@ post "/login" do |env| end get "/signout" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1189,22 +1196,17 @@ get "/signout" do |env| end get "/preferences" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + referer = get_referer(env) - if preferences = env.get? "preferences" - preferences = preferences.as(Preferences) + preferences = env.get("preferences").as(Preferences) - templated "preferences" - else - preferences = DEFAULT_USER_PREFERENCES - - templated "preferences" - end + templated "preferences" end post "/preferences" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? referer = get_referer(env) video_loop = env.params.body["video_loop"]?.try &.as(String) @@ -1347,7 +1349,7 @@ post "/preferences" do |env| end get "/toggle_theme" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? referer = get_referer(env) if user = env.get? "user" @@ -1356,14 +1358,9 @@ get "/toggle_theme" do |env| preferences.dark_mode = !preferences.dark_mode PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences.to_json, user.email) - elsif preferences = env.get? "preferences" - preferences = preferences.as(Preferences) - preferences.dark_mode = !preferences.dark_mode - - env.response.cookies["PREFS"] = preferences.to_json else - preferences = DEFAULT_USER_PREFERENCES - preferences.dark_mode = true + preferences = env.get("preferences").as(Preferences) + preferences.dark_mode = !preferences.dark_mode env.response.cookies["PREFS"] = preferences.to_json end @@ -1372,7 +1369,7 @@ get "/toggle_theme" do |env| end get "/mark_watched" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env, "/feed/subscriptions") @@ -1402,7 +1399,7 @@ get "/mark_watched" do |env| end get "/mark_unwatched" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env, "/feed/history") @@ -1434,7 +1431,7 @@ end # /modify_notifications?receive_all_updates=false&receive_no_updates=false # will "unding" all subscriptions. get "/modify_notifications" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1481,7 +1478,7 @@ get "/modify_notifications" do |env| end get "/subscription_manager" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" sid = env.get? "sid" @@ -1558,7 +1555,7 @@ get "/subscription_manager" do |env| end get "/data_control" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1573,7 +1570,7 @@ get "/data_control" do |env| end post "/data_control" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1672,7 +1669,7 @@ post "/data_control" do |env| end get "/subscription_ajax" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1753,7 +1750,7 @@ get "/subscription_ajax" do |env| end get "/delete_account" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1770,7 +1767,7 @@ get "/delete_account" do |env| end post "/delete_account" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1803,7 +1800,7 @@ post "/delete_account" do |env| end get "/clear_watch_history" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1820,7 +1817,7 @@ get "/clear_watch_history" do |env| end post "/clear_watch_history" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1847,7 +1844,7 @@ end # Feeds get "/feed/top" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? if config.top_enabled templated "top" @@ -1857,13 +1854,13 @@ get "/feed/top" do |env| end get "/feed/popular" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? templated "popular" end get "/feed/trending" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? trending_type = env.params.query["type"]? trending_type ||= "Default" @@ -1882,7 +1879,7 @@ get "/feed/trending" do |env| end get "/feed/subscriptions" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" sid = env.get? "sid" @@ -2030,7 +2027,7 @@ get "/feed/subscriptions" do |env| end get "/feed/history" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -2055,7 +2052,7 @@ get "/feed/history" do |env| end get "/feed/channel/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/atom+xml" @@ -2167,7 +2164,7 @@ get "/feed/channel/:ucid" do |env| end get "/feed/private" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/atom+xml" @@ -2282,7 +2279,7 @@ get "/feed/private" do |env| end get "/feed/playlist/:plid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/atom+xml" @@ -2388,7 +2385,7 @@ end # YouTube appears to let users set a "brand" URL that # is different from their username, so we convert that here get "/c/:user" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? client = make_client(YT_URL) user = env.params.url["user"] @@ -2425,7 +2422,7 @@ get "/user/:user/videos" do |env| end get "/channel/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -2478,7 +2475,7 @@ get "/channel/:ucid" do |env| end get "/channel/:ucid/videos" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? ucid = env.params.url["ucid"] params = env.request.query @@ -2493,7 +2490,7 @@ get "/channel/:ucid/videos" do |env| end get "/channel/:ucid/playlists" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -2550,7 +2547,7 @@ get "/api/v1/stats" do |env| end get "/api/v1/captions/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -2655,7 +2652,7 @@ get "/api/v1/captions/:id" do |env| end get "/api/v1/comments/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? env.response.content_type = "application/json" @@ -2722,7 +2719,7 @@ get "/api/v1/comments/:id" do |env| end get "/api/v1/insights/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? id = env.params.url["id"] env.response.content_type = "application/json" @@ -2813,7 +2810,7 @@ get "/api/v1/insights/:id" do |env| end get "/api/v1/videos/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3023,7 +3020,7 @@ get "/api/v1/videos/:id" do |env| end get "/api/v1/trending" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3074,7 +3071,7 @@ get "/api/v1/trending" do |env| end get "/api/v1/popular" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3108,7 +3105,7 @@ get "/api/v1/popular" do |env| end get "/api/v1/top" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3154,7 +3151,7 @@ get "/api/v1/top" do |env| end get "/api/v1/channels/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3362,7 +3359,7 @@ end ["/api/v1/channels/:ucid/videos", "/api/v1/channels/videos/:ucid"].each do |route| get route do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3433,7 +3430,7 @@ end ["/api/v1/channels/:ucid/latest", "/api/v1/channels/latest/:ucid"].each do |route| get route do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3485,7 +3482,7 @@ end ["/api/v1/channels/:ucid/playlists", "/api/v1/channels/playlists/:ucid"].each do |route| get route do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3553,7 +3550,7 @@ end end get "/api/v1/channels/search/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3657,7 +3654,7 @@ get "/api/v1/channels/search/:ucid" do |env| end get "/api/v1/search" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? env.response.content_type = "application/json" @@ -3786,7 +3783,7 @@ get "/api/v1/search" do |env| end get "/api/v1/playlists/:plid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" plid = env.params.url["plid"] @@ -3889,7 +3886,7 @@ get "/api/v1/playlists/:plid" do |env| end get "/api/v1/mixes/:rdid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index cecb9b2f..160afb2d 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -27,12 +27,12 @@ <% end %> <% preferred_captions.each_with_index do |caption, i| %> - " + " label="<%= caption.name.simpleText %>" <% if i == 0 %>default<% end %>> <% end %> <% captions.each do |caption| %> - " + " label="<%= caption.name.simpleText %>"> <% end %> <% end %> diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index de44dd3e..c3abc7e9 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -18,14 +18,14 @@ - <% if env.get?("preferences").try &.as(Preferences).dark_mode %> + <% if env.get("preferences").as(Preferences).dark_mode %> <% else %> <% end %> -<% locale = LOCALES[env.get("locale").as(String)]? %> +<% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %>
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 39c46d2e..5ad52a7f 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -255,9 +255,9 @@ function get_playlist(timeouts = 0) { var plid = "<%= plid %>" if (plid.startsWith("RD")) { - var plid_url = "/api/v1/mixes/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("locale").as(String) %>"; + var plid_url = "/api/v1/mixes/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; } else { - var plid_url = "/api/v1/playlists/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("locale").as(String) %>"; + var plid_url = "/api/v1/playlists/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; } var xhr = new XMLHttpRequest(); @@ -321,7 +321,7 @@ function get_reddit_comments(timeouts = 0) { comments.innerHTML = '

'; - var url = "/api/v1/comments/<%= video.id %>?source=reddit&format=html&hl=<%= env.get("locale").as(String) %>"; + var url = "/api/v1/comments/<%= video.id %>?source=reddit&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var xhr = new XMLHttpRequest(); xhr.responseType = "json"; xhr.timeout = 20000; @@ -384,7 +384,7 @@ function get_youtube_comments(timeouts = 0) { comments.innerHTML = '

'; - var url = "/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("locale").as(String) %>"; + var url = "/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var xhr = new XMLHttpRequest(); xhr.responseType = "json"; xhr.timeout = 20000; @@ -442,7 +442,7 @@ function get_youtube_replies(target, load_more) { body.innerHTML = '

'; - var url = '/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("locale").as(String) %>&continuation=' + + var url = '/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("preferences").as(Preferences).locale %>&continuation=' + continuation; var xhr = new XMLHttpRequest(); xhr.responseType = 'json'; From 11ff40bcd68f4941fa40017f0bb6f5a8826effa1 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 12:55:05 -0500 Subject: [PATCH 15/19] Fix paths for 'local=true&raw=1' --- src/invidious.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 998b9291..c89c6771 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -405,8 +405,8 @@ get "/watch" do |env| adaptive_fmts = video.adaptive_fmts(decrypt_function) if params[:local] - fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } - adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } end video_streams = video.video_streams(adaptive_fmts) @@ -511,8 +511,8 @@ get "/embed/:id" do |env| adaptive_fmts = video.adaptive_fmts(decrypt_function) if params[:local] - fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } - adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } end video_streams = video.video_streams(adaptive_fmts) From f01152eda1986c901886f81f98cfb5e9cb7bc48b Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 13:14:30 -0500 Subject: [PATCH 16/19] Add 'host' to '/videoplayback' --- src/invidious.cr | 12 +++++++++--- src/invidious/videos.cr | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index c89c6771..ff17ab9b 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4217,9 +4217,15 @@ end get "/videoplayback" do |env| query_params = env.params.query - fvip = query_params["fvip"]? || "3" - mn = query_params["mn"].split(",").pop - host = "https://r#{fvip}---#{mn}.googlevideo.com" + if query_params["host"]? && !query_params["host"].empty? + pp query_params["host"] + host = "https://#{query_params["host"]}" + else + fvip = query_params["fvip"]? || "3" + mn = query_params["mn"].split(",").pop + host = "https://r#{fvip}---#{mn}.googlevideo.com" + end + url = "/videoplayback?#{query_params.to_s}" headers = HTTP::Headers.new diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index b5bd2083..17d17d93 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -317,6 +317,7 @@ class Video end streams.each do |fmt| + fmt["url"] += "&host=" + (URI.parse(fmt["url"]).host || "") fmt["url"] += decrypt_signature(fmt, decrypt_function) end @@ -384,6 +385,7 @@ class Video end adaptive_fmts.each do |fmt| + fmt["url"] += "&host=" + (URI.parse(fmt["url"]).host || "") fmt["url"] += decrypt_signature(fmt, decrypt_function) end From 58f4212aa853306ade1b34197f12f532135a48d4 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 13:32:46 -0500 Subject: [PATCH 17/19] Remove 'host' from query params --- src/invidious.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index ff17ab9b..fa6a4090 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4218,8 +4218,8 @@ get "/videoplayback" do |env| query_params = env.params.query if query_params["host"]? && !query_params["host"].empty? - pp query_params["host"] host = "https://#{query_params["host"]}" + query_params.delete("host") else fvip = query_params["fvip"]? || "3" mn = query_params["mn"].split(",").pop From 1fcd1ff3e8ee1a300431bb24392733b632d41cce Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 14:07:55 -0500 Subject: [PATCH 18/19] Add better fallback for '/videoplayback' --- src/invidious.cr | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index fa6a4090..3e5c4d5e 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4217,13 +4217,14 @@ end get "/videoplayback" do |env| query_params = env.params.query + fvip = query_params["fvip"]? || "3" + mns = query_params["mn"].split(",") + if query_params["host"]? && !query_params["host"].empty? host = "https://#{query_params["host"]}" query_params.delete("host") else - fvip = query_params["fvip"]? || "3" - mn = query_params["mn"].split(",").pop - host = "https://r#{fvip}---#{mn}.googlevideo.com" + host = "https://r#{fvip}---#{mns.pop}.googlevideo.com" end url = "/videoplayback?#{query_params.to_s}" @@ -4244,11 +4245,11 @@ get "/videoplayback" do |env| response = client.head(url, headers) break rescue Socket::Addrinfo::Error - if fvip == "3" - break + if !mns.empty? + mn = mns.pop end - fvip = "3" + host = "https://r#{fvip}---#{mn}.googlevideo.com" rescue ex end From e86eb16d915dfaa9685ecad5c5570e468b818556 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 16:17:40 -0500 Subject: [PATCH 19/19] Add temporary fix for crystal-lang/crystal#7383 --- src/invidious.cr | 2 +- src/invidious/helpers/helpers.cr | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 3e5c4d5e..4fe018f9 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4230,7 +4230,7 @@ get "/videoplayback" do |env| url = "/videoplayback?#{query_params.to_s}" headers = HTTP::Headers.new - {"Range", "Accept", "Accept-Encoding"}.each do |header| + {"Accept", "Accept-Encoding", "Connection", "Range"}.each do |header| if env.request.headers[header]? headers[header] = env.request.headers[header] end diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 075bf84e..1cd40d41 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -75,6 +75,14 @@ class DenyFrame < Kemal::Handler end end +# Temp fix for https://github.com/crystal-lang/crystal/issues/7383 +class HTTP::Client + private def handle_response(response) + # close unless response.keep_alive? + response + end +end + def rank_videos(db, n) top = [] of {Float64, String}