// ==UserScript== // @include *.youtube.com/* // @include *.youtube-nocookie.com/* // @include *.youtubeeducation.com/* // ==/UserScript== (function(opera){ var ScriptName = 'youtube_fuck_html5.js v22'; var conf = { relatedVideos : false // click title if not works , blockPlayerBase : true , videoQuality : 'auto' // highres hd1440 hd1080 hd720 large medium small tiny auto , autoplay : 0 // 1 0; not in background; not in iframe , del43 : true // remove format 43 (webm 640x360); can help switching to 720 , useManifest: false // buggy , noSubtitles: true // they don't works , swf : 'https://s.ytimg.com/yts/swfbin/player-vfljf_kbO/watch_as3.swf' // dead , deleteCookies: [ //'VISITOR_INFO1_LIVE' // messes with signatures ] , replaceCookies: { } , addParams: { theme: 'dark' // dark, light , color: 'red' // red, white //, hl: 'en' } //, wmode : 'direct' // window, direct, opaque, transparent, gpu https://helpx.adobe.com/ru/flash/kb/flash-object-embed-tag-attributes.html } /* Need something against crossdomain.xml and watch_as3.swf Filters for Proxomitron: [Patterns] Name = "youtube dash manifest fix" Active = TRUE URL = "manifest.googlevideo.com(:443|)/api/manifest/dash/" Limit = 256 Match = "(<SegmentList>)\1(<SegmentURL media="\2")\3" Replace = "\1<Initialization sourceURL="\2"/>\3" [HTTP headers] In = FALSE Out = TRUE Key = "Accept-encoding: youtube no gzip, enable filtering" URL = "manifest.googlevideo.com(:443|)/$FILTER(True)" [HTTP headers] In = FALSE Out = TRUE Key = "URL: youtube crossdomain.xml" URL = "([^/]++.|)(youtube(-nocookie|education|).com|ytimg.com|googlevideo.com)(:443|)/crossdomain.xml$RDIR(http://local.ptron/crossdomain.xml)" In = FALSE Out = TRUE Key = "URL: youtube watch_as3.swf" URL = "s.ytimg.com(:443|)/yts/swfbin/player-*watch_as3.swf$RDIR(http://local.ptron/watch_as3.swf)" In = FALSE Out = TRUE Key = "Accept-encoding: youtube no gzip, enable filtering" URL = "manifest.googlevideo.com(:443|)/$FILTER(True)" crossdomain.xml (put in Proxomitron\html\): <?xml version="1.0"?> <cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy> */ window.spf = {}; var sts, pr, formats={}, itags=[]; document.createElement("video").constructor.prototype.canPlayType = function(type){return ""}; // disable HTML5 var showConf = true; function log(str){ opera.postError(ScriptName + (str?': '+str:'')+(showConf?'\nconf='+JSON.stringify(conf):'')); showConf = false; } //https://learn.javascript.ru/cookie function setCookie(name, value, options) {options = options || {};var expires = options.expires;if (typeof expires == "number" && expires) {var d = new Date();d.setTime(d.getTime() + expires * 1000);expires = options.expires = d;}if (expires && expires.toUTCString) {options.expires = expires.toUTCString();}var updatedCookie = name + "=" + value;for (var propName in options) {updatedCookie += "; " + propName;var propValue = options[propName];if (propValue !== true) {updatedCookie += "=" + propValue;}}document.cookie = updatedCookie;} function deleteCookie(name, options) {options=options||{}; options.expires=-1; setCookie(name, "", options)} function deleteCookieForDomains(name, domain){ deleteCookie(name,{domain:domain}); deleteCookie(name,{domain:domain.replace(/^www\./,'.')}); deleteCookie(name,{domain:domain.replace(/^www\./,'')}); } if (conf.deleteCookies) for (var i=0; i<conf.deleteCookies.length; i++) { deleteCookieForDomains(conf.deleteCookies[i],location.hostname); } if (conf.replaceCookies) for (var prop in conf.replaceCookies) if (conf.replaceCookies.hasOwnProperty(prop) && typeof(conf.replaceCookies[prop]) == 'string') { deleteCookieForDomains(prop,location.hostname); setCookie(prop, conf.replaceCookies[prop], {expires:'Tue, 15-Jan-2030 03:14:07 GMT', path:'/'}) } /* /s/player/4fbb4d5b/player_ias.vflset/ru_RU/base.js */ //opera.postError(ScriptName); opera.addEventListener("BeforeScript",function(e){ if (e.element.src.match(/\/player.*?\/base\.js/)) { opera.postError('base.js: '+ e.element.src); sts = e.element.text.match(/signatureTimestamp[\s:=]+(\d+)[,;}]/); sts=sts&&sts[1]; if (sts) { log('player: '+e.element.src+'\nsts='+sts+'\nUA='+navigator.userAgent); if (conf.blockPlayerBase) { if (window.canBlockPlayerBase) { var decf = e.element.text.match(/=function\(a\){(a=a.split\(""\)[^}]*?;return a.join\(""\))};/); decf = decf && decf[1]; var deco = decf && decf.match(/([\w$_]+)\.[\w$_]+\(a,\d+\);/); deco = deco && e.element.text.match(new RegExp('var '+deco[1].replace(/\$/,'\\$')+'={[\\w$_]+:function\\(a[\\s\\S]*?};','m')); deco = deco && deco[0]; if (decf && deco) { decodeSignature = new Function('a', deco+decf); e.preventDefault(); log('blocked player base.js: '+e.element.src); } else { log("error: can't get decoder from player base.js\ndeco="+deco+'\ndecf='+decf) } fuckn = e.element.text.match(/=function\(a\){(var b=a.split\(""\)[\s\S]*?return b.join\(""\))};/m); if (fuckn) { log('found fuck n function'); fuckn = new Function('a', fuckn[1]); } else { log('fuck n function is not found!'); } } else { log('blockPlayerBase requires blockYoutubeScripts in youtube_fix.js'); } } else { e.element.text = e.element.text.replace('=function(a){a=a.split("")','=decodeSignature=function(a){a=a.split("")') .replace('=function(a){var b=a.split(""),c=','=fuckn=function(a){var b=a.split(""),c='); /* hha=function(a){var b=a.split(""),c=[function(d,e){e=(e%d.length+d.length)%d.length;d.splice(0,1,d.splice(e,1,d[0])[0])}, */ } } } },false); function getField(obj) { var tmp = obj; if (tmp) for (var i=1; i<arguments.length; i++) { tmp = tmp[arguments[i]]; if (!tmp) break; } return tmp; } function xhr(url, method, post, xml, headers){ var x = new XMLHttpRequest(); x.open(method || 'GET', url, false); if (headers) for (var i=0; i<headers.length; i++) x.setRequestHeader(headers[i][0], headers[i][1]); x.send(post); return xml?x.responseXML:x.responseText; } function splitKV(str, o) { o = o || {}; var tmp; var re = /([\da-z_]+)=([^&]*)/gi; while ( (tmp = re.exec(str))!==null ) { o[tmp[1]] = decodeURIComponent(tmp[2].replace(/\+/g,' ')); } return o; } function concatKV(obj){ var tmp = []; for (var n in obj) { var value = obj[n]; //typeof(obj[n])=='object'? JSON.stringify(obj[n]) : obj[n]; tmp.push(n+'='+encodeURIComponent(value).replace(/,/g,'%2C') ); } return tmp.join('&'); } var onceFix = true; function fix(str){ var tmp = str.split(','); for (var i=tmp.length-1; i>=0; i--) { var obj = splitKV(tmp[i]); del(obj,['xtags']); if (fuckn) obj.url = obj.url.replace(/&n=([\w_-]+)/,function(a,b){return '&n='+fuckn(b)}); if (conf.del43 && obj.itag==43) { tmp.splice(i,1); // del log('removed format 43') } else if (obj.clen==0 && obj.url.indexOf('live=1')==-1) { tmp.splice(i,1); // del log('removed clen==0 itag='+obj.itag); } else if (obj.s) { obj.itag = parseInt(obj.itag); if (formats[obj.itag]) { obj.url = formats[obj.itag]; delete obj.s; } else if (!window.decodeSignature) { if (onceFix) onceFix = log('error: no decodeSignature()') return str; } else { obj.url += '&'+(obj.sp||'signature')+'='+decodeSignature(unescape(obj.s)); delete obj.s; } tmp[i] = concatKV(obj); } } return tmp.join(','); } function del(obj,arr){ for (var d in arr) delete obj[arr[d]]; } function set(obj,arr){ for (var d in arr) obj[d]=arr[d]; } function setNew(obj,arr){ for (var d in arr) if (typeof(obj[d])=='undefined') obj[d]=arr[d]; } if (location.pathname.indexOf('/embed/')==0 || location.pathname=='/watch' && window==window.top) document.addEventListener('DOMContentLoaded',function(){ var errMsg = document.querySelector('#unavailable-message'); var div; var selectors = ['div.html5-video-player', 'div#player-api', 'div#player']; for (var i=0,s; s=selectors[i]; i++) if (div = document.querySelector(s)) break; if (!div) { log('player div not found'); } else { log('found div:'+(div.id?' id="'+div.id+'"':'') + (div.className?' class="'+div.className+'"':'')); if (!sts) log('warning: sts from /player-./base.js not found! Check content blocker.'); div.classList.remove('off-screen-target'); div.innerHTML = '<br><br>'+ScriptName; var u = document.querySelector('div#player-unavailable'); if (u) u.parentNode.removeChild(u); var id = (''+location).match(/\/embed\/([\da-z_-]{11})/i) || (''+location).match(/[&?]v=([\da-z_-]{11})/i); id = id&&id[1]; if (!id) { div.innerHTML += ' - no video id'; return log('no video id'); } var bg = 'https://i.ytimg.com/vi/'+id+'/hqdefault.jpg'; var info, infoLength, status, reason, proceed; function gvi(data) { //var url = '/get_video_info?html5=1&asv=3&ps=default'+(sts?'&sts='+sts:'')+'&video_id='+id+(data||''); //info = xhr(url); //if (info.indexOf('status=')==-1) info = xhr(url); // second try var crap = JSON.stringify({"videoId":id, "context": {"client": {"clientName": "WEB_EMBEDDED_PLAYER","clientVersion": "1.20210616.1.0"}}}); var crapHeaders = [["X-Youtube-Client-Name","56"], ["X-Youtube-Client-Version","1.20210615.1.0"] ]; info = xhr('/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8', 'POST', crap, false, crapHeaders) infoLength = info.length; //info = splitKV(info); info = JSON.parse(info); //if (info.player_response) info.player_response = JSON.parse(info.player_response); status = getField(info, 'playabilityStatus', 'status') || getField(info, 'playabilityStatus', 'status'); if (status) status=status.toLowerCase(); var ps = getField(info, 'playabilityStatus') || info.playabilityStatus || {}; reason = ps.reason || getField(ps, 'errorScreen', "playerErrorMessageRenderer", "reason", "simpleText"); proceed = getField(ps, 'errorScreen', 'playerErrorMessageRenderer', 'proceedButton', 'buttonRenderer', 'text', 'simpleText'); } gvi(); if (status=="unplayable") { if (reason.indexOf("youtube.com/watch")!=-1 || (proceed&&proceed.indexOf('YouTube')!=-1) ) { opera.postError([ScriptName, 'error',reason,proceed,status]) var page = xhr('/watch?v='+id); var tmp = page.match(/var ytInitialPlayerResponse\s*=\s*({.*?});/) || page.match(/ytplayer.config = ({.*?});ytplayer/); tmp = tmp && JSON.parse(tmp[1]) if (tmp.args) tmp=tmp.args; if (tmp) { info = tmp; //if (info.player_response) info.player_response = JSON.parse(info.player_response); status = getField(info, 'playabilityStatus', 'status') || getField(info, 'playabilityStatus', 'status'); if (status) status=status.toLowerCase(); reason = getField(info, 'playabilityStatus', 'reason') || getField(info, 'playabilityStatus', 'reason'); } //alert(JSON.stringify(tmp,null,'\t')) } else { gvi('&eurl=https://www.google.com/'); } } if (status!='ok') { div.innerHTML = '<br><br>'+reason; return log('reason: '+reason); } log(JSON.stringify({cbr:info.cbr, cbrver:info.cbrver, cbrand:info.cbrand, infoLength:infoLength})); if (!conf.relatedVideos) delete info.rvs; del (info, ['ppv_remarketing_url','remarketing_url', 'allowed_ads', 'survey_smartpixel_url']); pr = info//.player_response; //alert(JSON.stringify(info,null,'\t')); if (pr) { var sd = pr.streamingData || info.streamingData || {}; var tmp = sd.formats; if (tmp) for (var i=0; i<tmp.length; i++) formats[parseInt(tmp[i].itag)]=tmp[i].url; tmp = sd.adaptiveFormats; if (tmp) for (var i=0; i<tmp.length; i++) formats[parseInt(tmp[i].itag)]=tmp[i].url; // manifests don't work? if (!info.dashmpd) info.dashmpd = sd.dashManifestUrl; //if (!info.hlsvp) info.hlsvp = sd.hlsManifestUrl; //var st = getField(pr, 'playabilityStatus', 'status') || getField(info, 'playabilityStatus', 'status'); //var re = //if (st=="UNPLAYABLE") return div.innerHTML = '<br><br>' + pr.playabilityStatus.reason; if (status=="unplayable") return div.innerHTML = '<br><br>' + reason; } //var eventid = (info.url_encoded_fmt_stream_map||info.adaptive_fmts).match(/ei%3D([^%]+)/)[1]; var crap = { storyboard_spec: getField(pr, 'storyboards', 'playerStoryboardSpecRenderer', 'spec') || "https://i9.ytimg.com/sb/LpWKhuvcXGU/storyboard3_L$L/$N.jpg?sqp=ovOX_wMGCMLdv58F|48#27#100#10#10#0#default#rs$AOn4CLCZ4iJXVfCkSXp3QGpGckl5vCoKAA|80#45#76#10#10#1000#M$M#rs$AOn4CLCLFHKsZ4N97Z2y-gYuQauK7NH3SQ|160#90#76#5#5#1000#M$M#rs$AOn4CLDLKWOa64VAWs_XCvNKMPnOd1xWCQ" , token: 'fuck' , length_seconds: getField(pr, 'videoDetails', 'lengthSeconds') , title: getField(pr, 'videoDetails', 'title') , video_id: getField(pr, 'videoDetails', 'videoId') } //alert(JSON.stringify(crap,null,'\t')) setNew(info, crap); if (conf.noSubtitles) { del(info, ['ttsurl','caption_tracks','caption_audio_tracks', 'cc_asr', 'cc3_module', 'caption_translation_languages', 'cc_font']); info.has_cc = 'False'; } else { var tmp = JSON.parse(info||'{}'); if (tmp.captions) { tmp = tmp.captions.playerCaptionsTracklistRenderer.captionTracks } } if (conf.del43 && info.fmt_list) info.fmt_list = info.fmt_list.replace('43/640x360,',''); if (conf.addParams) set(info, conf.addParams); if (conf.videoQuality) info.vq = conf.videoQuality; info.autoplay = conf.autoplay && !document.hidden && window==window.top && 1 || 0; if (info.url_encoded_fmt_stream_map) info.url_encoded_fmt_stream_map = fix(info.url_encoded_fmt_stream_map); if (info.adaptive_fmts) info.adaptive_fmts = fix(info.adaptive_fmts); function fix2(dest, arr, clean) { if (info[dest] || !arr) return; info[dest] = arr; for (var obj,i=arr.length-1; obj=arr[i]; i--) { if (conf.del43 && obj.itag==43 || obj.type=='FORMAT_STREAM_TYPE_OTF' || obj.projectionType!="RECTANGULAR") { arr.splice(i,1); // del } else { obj.itag = parseInt(obj.itag); itags.push(obj.itag); var repl = { contentLength:'clen', mimeType:'type', lastModified:'lmt', qualityLabel:'quality_label' } for (var prop in repl) if (obj.hasOwnProperty(prop)) obj[repl[prop]] = obj[prop] || obj[repl[prop]]; obj.clen = obj.clen || obj.contentLength; obj.type = obj.mimeType || obj.type; obj.size = obj.size || obj.width && obj.width+'x'+obj.height; obj.projection_type = 1; if (obj.cipher||obj.signatureCipher) { splitKV(obj.cipher||obj.signatureCipher, obj); } if (obj.initRange) obj.init = obj.initRange.start+'-'+obj.initRange.end; if (obj.indexRange) obj.index = obj.indexRange.start+'-'+obj.indexRange.end; if (obj.s) { if (!window.decodeSignature) { if (onceFix) onceFix = log('error: no decodeSignature()') return; } else { obj.url += '&'+(obj.sp||'signature')+'='+decodeSignature(unescape(obj.s)); delete obj.s; } } del(obj, ['cipher','signatureCipher','initRange','indexRange','colorInfo']); if (clean) for (var prop in obj) if (obj.hasOwnProperty(prop)) { if (prop!='itag'&&prop!='fallback_host'&&prop!='url'&&prop!='type'&&prop!='quality') delete obj[prop]; } arr[i] = concatKV(obj); } } } delete info.url_encoded_fmt_stream_map; delete info.adaptive_fmts; fix2('url_encoded_fmt_stream_map', getField(info, 'streamingData', 'formats'), true); fix2('adaptive_fmts', getField(info, 'streamingData', 'adaptiveFormats')); if (!info.fmt_list) { var tmp2 = []; if (~itags.indexOf(22)) tmp2.push('22/1280x720'); // order! if (~itags.indexOf(43)) tmp2.push('43/640x360'); if (~itags.indexOf(18)) tmp2.push('18/640x360'); info.fmt_list = tmp2.join(','); } if (info.dashmpd) { info.dashmpd = info.dashmpd.replace(/\/s\/([\dA-F.]+)/,function(a,s){return '/signature/'+decodeSignature(unescape(s))}); if (conf.useManifest) del(info, ['fmt_list', 'url_encoded_fmt_stream_map', 'adaptive_fmts']); } set(info, {no_stats:1, allow_embed:1, no_get_video_log:1, rmkt:0, rtl:0, ad3_module:0, ad_preroll:0, enable_cardio:0 , enable_cardio_before_playback:0, advideo:0, skl:1 }); del(info, ['watch_next_response']); // for some videos del(info, ['fflags']); //alert(JSON.stringify(info,null,'\t')); info = concatKV(info); //alert(info); var wmode = conf.wmode ? 'wmode="'+conf.wmode+'"' : ''; div.innerHTML = '<embed src="'+conf.swf+'" flashvars="'+info+'" '+wmode+' style="background: url('+bg+') center no-repeat" type="application/x-shockwave-flash" width=100% height=100% allowfullscreen="true" allowscriptaccess="always" bgcolor="#000000" />' } // !div },false); })(opera) |