// ==UserScript== // @name AuViDL [vkopt module] // @namespace http://tampermonkey.net/ // @version 3.0.7.22 // @description Опции скачивания аудио и видео // @author KiberInfinity // @match https://vk.com/* // @grant none // ==/UserScript== window.vkopt = (window.vkopt || {}); vkopt['audl'] = { css: function(){ var codes = vk_lib.get_block_comments(function(){ /*dl: .audio_row .audio_acts .audio_act.vk_audio_dl_btn{ display:block; } .audio_row__action_get_link{ float: right; } .audio_row .audio_acts .audio_act.vk_audio_dl_btn>div, .audio_row__action_get_link div{ background-image: url(/images/blog/about_icons.png); width: 12px; height: 14px; background-position: 0px -309px; } .audio_row .audio_acts .audio_act.vk_audio_acts>div { margin: 6px 0 0 6px; } .audio_row .audio_acts .audio_act.vk_audio_dl_btn.dl_url_loading>div, .audio_row__action_get_link.dl_url_loading div{ opacity:0.3; background: url(/images/upload_inv_mini.gif) no-repeat 0% 50%; width: 17px; margin-left: -2px; } .audio_row__action_get_link div, .audio_row__action_get_link.dl_url_loading div{ margin: 5px auto; } .vk_audio_icon_dots .audio_row .audio_acts .audio_act.vk_audio_acts{ display:block; } .vk_audio_icon_dots .audio_row .audio_acts .audio_act.vk_audio_acts>div { background: url(/images/icons/profile_dots.png) no-repeat 0 5px; height: 13px; width: 18px; } .vk_audio_icon_dots .audio_row .audio_acts .audio_act.vk_audio_dl_btn.vk_audio_acts>div{ background: url(/images/icons/profile_dots.png) no-repeat 0 5px; height: 13px; width: 18px; transition: none; } .vk_audio_icon_dots .audio_row .audio_acts .audio_act.vk_audio_dl_btn.vk_audio_acts:hover>div{ background-image: url(/images/blog/about_icons.png); width: 12px; height: 14px; margin-left:3px; margin-right:3px; background-position: 0px -309px; } .audio_row .audio_acts .audio_act.vk_audio_dl_btn.dl_url_loading>div, .audio_row .audio_acts .audio_act.vk_audio_dl_btn.dl_url_loading:hover>div{ opacity:0.3; background: url(/images/upload_inv_mini.gif) no-repeat 0% 50%; width: 18px; } .audio_duration_wrap.vk_with_au_info .audio_duration{ display: inline; } .audio_row.vk_info_loaded .vk_audio_size_info_wrap{ display: table; } .vk_audio_size_info_wrap, .narrow_column .audios_module .vk_audio_size_info_wrap, .audio_row:hover .vk_audio_size_info_wrap { display: none; } .vk_audio_size_info_wrap span{ display: block; } .vk_audio_size_info_wrap{ line-height: 10px; margin-right: 3px; margin-top: 12px; height: 18px; float:right; font-size: 10px; color: #777; } .audio_w_covers .vk_audio_size_info_wrap{ margin-top: 15px; } .vk_audio_size_info{ display: table-cell; vertical-align: middle; } .audio_row.vk_info_loaded .audio_row__info {margin-left: 30px;} .wall_module .media_desc .vk_audio_size_info b { background:none } */ }); return codes.dl; }, tpls: {}, onSettings:{ Media:{ audio_dl: { title: 'seLinkAu', info: 'infoUseNetTrafic' }, audio_size_info: { title: 'seAudioSizeAuto', info: 'infoUseNetTrafic', sub: { audio_wait_hover:{ title: 'seAudioSizeHover' } } } }, Extra:{ mp3u8:{default_value:true} } }, onInit: function(){ vkopt.audl.tpls = vk_lib.get_block_comments(function(){ /*size_info:
{vals.size} {vals.kbps}
*/ }); vkopt.audl.load_sizes_cache(); vk_ext_api.browsers && vk_ext_api.browsers.webext && vkopt.permissions.update(); }, onLibFiles: function(fn){ if (fn == 'audioplayer.js'){ var pl = window.getAudioPlayer && getAudioPlayer(); if (pl && pl._impl && Object.getPrototypeOf(pl._impl)._isHlsUrl) Inj.Start('Object.getPrototypeOf(getAudioPlayer()._impl)._isHlsUrl', function(url,obj){ if (obj && obj.get_url) obj.url = url; }); Inj.End('AudioUtils.drawAudio', function(state){ var obj = this; if (obj.result) { obj.return_result = vkopt_core.mod_str_as_node(obj.result, vkopt_core.plugins.process_node, {source:"drawAudio", state: state}); obj.prevent_all = true; } }) } }, onAudioRowItems: function(audioEl, audioObject, audio){ var items = {actions:[]}; if (vkopt.settings.get('audio_dl')){ items.actions.push([ 'get_link', function(audioEl, audioObject, audio){ vkopt.log(arguments); var filename=vkCleanFileName(unclean(audioObject.performer+' - '+audioObject.title)); var dl_btn = geByClass1('_audio_row__action_get_link', audioEl); if (dl_btn.hasAttribute('url_ready')){ if (dl_btn.dataset["m3u8"]){ vkopt.hls.download( dl_btn.dataset["m3u8"], dl_btn.getAttribute('download'), dl_btn.dataset["aid"] ); return false; } var dlnk = se(''); utilsNode.appendChild(dlnk); if (vkopt.audl.download_file(dlnk)) dlnk.click(); setTimeout(function(){ re(dlnk); },200); } return true; }, '
',// button content 'data-aid="{vals.fullId}" data-urlhash="{vals.urlHash}" data-reqaid="{vals.fullId}_{vals.actionHash}_{vals.urlHash}" id="vk_get_link_{vals.fullId}" href="" onmouseover="vkopt.audl.check_dl_url(this);"',//custom_attributes 'a' ]); } return items; }, insertSizeInfoWrap: function(row, vals){ var dur = geByClass1('audio_row__info', row); var sz_info = se(vk_lib.tpl_process(vkopt.audl.tpls['size_info'], vals)); if (dur && !hasClass('vk_with_au_info',dur.parentNode)){ dur.parentNode.insertBefore(sz_info, dur); addClass(dur.parentNode, 'vk_with_au_info'); } return sz_info; }, processNode: function(node, params){ if (!vkopt.settings.get('audio_dl')) return; if (!vkopt.audl.__full_audio_info_cache) vkopt.audl.__full_audio_info_cache = {}; var cache = vkopt.audl.__full_audio_info_cache; var audios = geByClass('audio_row',node); if (!audios.length && hasClass(node, 'audio_row')) // если вызов из AutoList.prototype._drawRows, то на входе уже элемент audio_row audios = [node]; for (var i = 0; i < audios.length; i++){ var row = audios[i]; var info = null; try { info = JSON.parse(row.dataset["audio"]); } catch(e) {} if (!info) continue; var info_obj = AudioUtils.asObject(info); if (info_obj.url==""){ // собираем очередь из аудио, которым требуется подгрузка инфы if (cache[info_obj.fullId]) info_obj = cache[info_obj.fullId]; else { var queue = vkopt.settings.get('audio_wait_hover') ? vkopt.audl.__hover_load_queue : vkopt.audl.__load_queue; var full_id_req = info_obj.fullId + "_" + info_obj.actionHash + "_" + info_obj.urlHash; if (info_obj.urlHash && queue.indexOf(full_id_req) == -1 && vkopt.audl.__loading_queue.indexOf(full_id_req) == -1) queue.push(full_id_req); } } if (info_obj.url) info_obj.url = vkopt.audl.decode_url(info_obj.url); //var name = unclean(info[4]+' - '+info[3]).replace(/|<\/em>/g, ''); // зачищаем от тегов. //name = vkCleanFileName(name); var size = vkopt.audl._sizes_cache[info_obj.fullId]; var sz_labels = size ? vkopt.audl.size_to_bitrare(size, info_obj.duration) : {}; if (size){ row.dataset['kbps'] = sz_labels.kbps_raw; row.dataset['filesize'] = size; addClass(row, 'vk_info_loaded'); } // Инфа о размере/битрейте if (vkopt.settings.get('audio_size_info')){ if (!vkopt.settings.get('audio_wait_hover') && info_obj.url) vkopt_core.timeout( //setTimeout (function(id, url){ return function(){ vkopt.audl.load_size_info(id, url); } })(info_obj.fullId, info_obj.url), 200 ); vkopt.audl.insertSizeInfoWrap(row, { id: info_obj.fullId, url: info_obj.url || '', size: sz_labels.size || '? Mb', kbps: sz_labels.kbps || '? Kbps' }); } } // TODO: грузить инфу только при наведении на иконку меню/скачивания if (!vkopt.settings.get('audio_wait_hover') && (vkopt.settings.get('audio_size_info') || vkopt.settings.get('audio_dl'))) // URL'ы нужны только для этих опций vkopt.audl.load_audio_urls(); // запускаем процесс загрузки инфы об аудио из очереди }, download_file: function(el){ var result = true; if (el.hasAttribute('url_ready')) result = vkopt.permissions.check_dl_url(el, el.href); if (result) result = vkDownloadFile(el); return result; }, check_dl_url: function(el){ // если на странице не было ссылок на аудио, то при наведении на кнопку загрузки ждём их появления в кэше. if (el.getAttribute('href') == '' && el.dataset["urlhash"]){ addClass(el,'dl_url_loading'); var id = el.dataset["aid"]; var req_id = el.dataset["reqaid"]; function wait_and_set_url(){ var info = vkopt.audl.__full_audio_info_cache[id]; if (info){ var name = unclean(info.performer + ' - ' + info.title); if (vkopt.settings.get('audio_clean_titles')) name = vkopt.audio_clean_titles.remove_trash(name); var name = vkCleanFileName(name); var url = vkopt.audl.make_dl_url(info.url, name); if (/\.m3u8/.test(info.url)){ el.dataset["m3u8"] = info.url; } el.setAttribute('download', name+'.mp3'); el.setAttribute('href', url); el.setAttribute('url_ready','1'); removeClass(el,'dl_url_loading'); //vkopt.permissions.request_on_click(el, url, vkopt.log); vkopt.audl.load_size_info(info.fullId, info.url); } else { setTimeout(function(){ wait_and_set_url(); },300); } } wait_and_set_url(); if (vkopt.settings.get('audio_wait_hover')){ var info = vkopt.audl.__full_audio_info_cache[id]; // если не в загруженной инфе и очередях загрузки var hq = vkopt.audl.__hover_load_queue; if (!info && vkopt.audl.__loading_queue.indexOf(req_id) == -1 && vkopt.audl.__load_queue.indexOf(req_id) == -1){ var idx = hq.indexOf(req_id); if (idx == -1){ // не знаю возможно ли, но лучше добавлю проверку. hq.push(req_id); idx = hq.length - 1; } var start = Math.max(0, idx - 2); var end = Math.min(start + 5, hq.length) - start; var to_load = hq.splice(start, end); vkopt.audl.__load_queue = vkopt.audl.__load_queue.concat(to_load); vkopt.audl.load_audio_urls(); // запускаем процесс загрузки инфы об аудио из очереди } } } }, make_dl_url: function(url, name){ name = vkCleanFileName(name); /* // фикс-костыль, т.к для https://*.vk-cdn.net нет разрешений в манифесте. // если исправить в манифесте, то после обновления расширения, оно отключится у всех пользователей хрома) //url = vkopt.audl.decode_url(url); if (/^https:.+\.vk-cdn\.net\//i.test(url)) url = url.replace(/^https:/,'http:'); */ var ext = /\.m3u8/.test(url) ? '.m3u8' : '.mp3'; return url + '#FILENAME/' + vkEncodeFileName(name) + ext; }, _sizes_cache: {}, // надо бы его загонять в локальное хранилище, но например кэш размеров со списка в ~500 аудио занимает около 10кб. т.е его нужно будет как-то по умному чистить. info_thread_count: 0, save_sizes_cache:function(){ clearTimeout(vkopt.audl._save_size_cache); vkopt.audl._save_size_cache = setTimeout(function(){ var cache = vkopt.audl._sizes_cache; var len = 0; var max_items = vkopt_defaults.config.MAX_CACHE_AUDIO_SIZE_ITEMS; for (var key in cache) len++; if (len > max_items){ var new_cache = {}; var i = 0; for (var key in cache){ i++; if (i > len - max_items) new_cache[key] = cache[key]; } cache = new_cache; } localStorage['vkopt_audio_sizes_cache'] = JSON.stringify(cache); },1500); }, load_sizes_cache:function(){ var sz_cache = {}; try{ sz_cache = JSON.parse(localStorage['vkopt_audio_sizes_cache'] || '{}'); } catch(e){} vkopt.audl._sizes_cache = sz_cache; }, clear_sizes_cache:function(){ localStorage['vkopt_audio_sizes_cache'] = '{}'; }, size_to_bitrare: function(size, duration){ var kbit = size / 128; var kbps = Math.ceil(Math.round(kbit/duration)/16)*16; return { size: vkFileSize(size, 1).replace(/([\d\.]+)/,'$1'), kbps: (kbps > 0 ? '' + kbps + ' Kbps' : ''), kbps_raw: kbps } }, load_size_info: function(id, url){ if (vkopt.audl.info_thread_count >= vkopt_defaults.config.AUDIO_INFO_LOAD_THREADS_COUNT){ var t = setInterval(function(){ if (vkopt.audl.info_thread_count < vkopt_defaults.config.AUDIO_INFO_LOAD_THREADS_COUNT){ clearInterval(t); vkopt.audl.load_size_info(id, url); } },50); return; } if (!vkopt.settings.get('audio_size_info')) return; var size = vkopt.audl._sizes_cache[id]; var custom_duration = 0; var rb = true; var WAIT_TIME = 4000; var els = geByClass('_audio_row_' + id); var set_size_info = function(size){ for (var i = 0; i < els.length; i++){ var el = els[i]; var info = AudioUtils.asObject(AudioUtils.getAudioFromEl(el)); if (custom_duration){ size = size/custom_duration*info.duration } var sz_info = vkopt.audl.size_to_bitrare(size, info.duration); if (!geByClass1('vk_audio_size', el)){ vkopt.audl.insertSizeInfoWrap(el, { id: info.fullId, url: info.url || '', size: sz_info.size || '? Mb', kbps: sz_info.kbps || '? Kbps' }); } else { val(geByClass1('vk_audio_size', el), sz_info.size); val(geByClass1('vk_audio_kbps', el), sz_info.kbps); } el.dataset['kbps'] = sz_info.kbps_raw; el.dataset['filesize'] = size; addClass(el, 'vk_info_loaded'); if (!vkopt.audl._sizes_cache[id]){ if (sz_info.kbps_raw > 120){ vkopt.audl._sizes_cache[id] = size; vkopt.audl.save_sizes_cache(); } else { vkopt.audl._sizes_cache[id] = false; vkopt.audl.save_sizes_cache(); } } return sz_info.kbps_raw > 120; } }; var get_size = function(url){ var reset=setTimeout(function(){ vkopt.audl.info_thread_count--; rb = false; }, WAIT_TIME); vkopt.audl.info_thread_count++; XFR.post(url, {}, function(h, size){ clearTimeout(reset); vkopt.log('get_size response:', id, size, h); if (rb){ vkopt.audl.info_thread_count--; } if (size > 0){ set_size_info(size); } else { // TODO: видать ссылка протухла. нужно подгрузить актуальный URL и снова запросить размер } }, true); } var need_load = true; if (size) need_load = !set_size_info(size); if (need_load && els.length){ if (/\.m3u8/.test(url)){ AjGet(url, function(r){ var base = url.split('?')[0].match(/.+\//)[0]; var info = []; r.replace(/(?:#EXT-X-KEY:METHOD=([^\r\n]+)[\s\S]*?)?#EXTINF:(.+?),[\r\n]+(.+\.ts[^\r\n]+)/g,function(s,mtd,dur,lnk){ if (!mtd || mtd=="NONE") info.push({method:mtd, duration:dur, link:base+lnk}) }); if (info.length){ var ts = info[Math.floor(info.length/2)]; custom_duration = parseFloat(ts.duration); get_size(ts.link); } }) } else { get_size(url); } } }, __load_queue:[], // очередь загрузки инфы __hover_load_queue:[], // очередь, из которой будут аудио перемещаться в __load_queue, при наведении на иконку загрузки. __loading_queue:[], // очередь текущих аудио, по которым в данный момент грузится инфа __load_req_num: 1, __full_audio_info_cache: {}, decode_url: function(url){ var n = function(){}; var tmp = { removeAttribute: n, setAttribute: n, getAttribute: n, setUrl: function(u) { tmp.src = u; return {than: n}; } }; var orig = RegExp.prototype.test; RegExp.prototype.test = function(){return false} try{ var h5proto = Object.getPrototypeOf(getAudioPlayer()._impl); var _currentAudioEl = h5proto._currentAudioEl; h5proto._currentAudioEl = tmp; h5proto.setUrl(url); h5proto._currentAudioEl = _currentAudioEl; }catch(e){} RegExp.prototype.test = orig; if (tmp.src && /\.m3u8/.test(tmp.src) && vkopt.settings.get('mp3u8')) tmp.src = tmp.src.replace(/(\/p\d+\/)[a-f0-9]+\/([a-f0-9]+)\/index.m3u8/,'$1$2.mp3').replace(/(\/c\d+\/[a-z]\d+\/)[a-f0-9]+\/(audios\/[a-f0-9]+)\/index.m3u8/,"$1$2.mp3"); return tmp.src }, load_audio_urls: function(){ if (vkopt.audl.__load_queue.length == 0 || vkopt.audl.__loading_queue.length > 0) // если нет списка на подгрузку, или что-то уже грузится - игнорим вызов return; vkopt.audl.__loading_queue = vkopt.audl.__load_queue.splice(0,Math.min(vkopt.audl.__load_queue.length, vkRandomRange(5,10))); // больше 10 аудио не принимает. var load_info = function(){ //TODO: удаление из __load_queue отсутствущих на странице аудио ajax.post("al_audio.php", { act : "reload_audio", ids : vkopt.audl.__loading_queue.join(",") }, { onDone : function (data) { if (!data){ // вероятно косяк с детектом множества однотипных действий console.log('Load audio info failed:', vkopt.audl.__load_req_num, vkopt.audl.__loading_queue.join(",")); setTimeout(function(){ console.log('try load again'); load_info(); }, 10000); } else { //console.log('on done:', vkopt.audl.__load_req_num, data); vkopt.audl.__loading_queue = []; each(data, function (i, info) { info = AudioUtils.asObject(info); if (info.url) info.url = vkopt.audl.decode_url(info.url); vkopt.audl.__full_audio_info_cache[info.fullId] = info; if (info.url) vkopt.audl.load_size_info(info.fullId, info.url); }); if (vkopt.audl.__load_queue.length > 0) // если в очереди есть аудио - продолжаем грузить vkopt.audl.load_audio_urls(); } } }); vkopt.audl.__load_req_num++; }; clearTimeout(vkopt.audl.__load_delay); // за короткий промежуток времени аудио могло появиться в разных местах. чуть ждём пока устаканится список. vkopt.audl.__load_delay = setTimeout( function(){ load_info(); }, vkopt.audl.__load_req_num %20 == 0 ? 3500 : 350 // попытка избежать тетекта однотипных действий ); }, load_all: function(callback){ // пока не используется. добавлено чтоб не забыть о таком способе получения ссылок (в новом вк нет упоминаний об этом методе). var query = { act : 'load_audios_silent', id : (cur.allAudiosIndex == 'all' ? cur.id : cur.audioFriend), gid : cur.gid, claim : nav.objLoc.claim, // Для silent-подгрузки похоже не работает ни на старой, ни на новой версии вк. // На старой версии по URL https://vk.com/audio?claim=1 показываются только те заблоченные, что попадают в видимый без подгрузки список. please_dont_ddos : 2 }; if (cur.club) query.club = cur.club; ajax.post('/al_audio.php', query, { onDone : (function (data, opts) { callback(data) }) }); } } vkopt['hls'] = { css: function(){ return vk_lib.get_block_comments(function(){ /*css: .vk_grab_progress { height: 3px; background: #6b96cf; position: absolute; bottom: 0px; width: 50%; } .vk_grab_progress:before { content: attr(data-progress)'%'; position: absolute; right: 0px; font-size: 10px; margin-top: -5px; background: #6b96cf; padding: 1px 4px; color: #FFF; font-weight: bold; border-radius: 4px; } */ }).css }, grab: function(m3u8, opts){ var onSegmentReady = opts.onSegmentReady || null, onDone = opts.onDone || null, onProgress = opts.onProgress || null; var url_create = window.URL.createObjectURL; var url_revoke = window.URL.revokeObjectURL; window.URL.createGrabObjectURL = function(ms) { if (ms instanceof MediaGrabSource){ vkopt.log('coURL ', ms); ms.emit('sourceopen'); //new return 'test://'; } else return url_create(ms); }; window.URL.revokeGrabObjectURL = function(ms) { if (ms instanceof MediaGrabSource){ vkopt.log('roURL ', ms); return true; } else return url_revoke(ms); }; function HTML5MediaElement(){} HTML5MediaElement.prototype = Object.create(EventEmitter.prototype) var mel = new HTML5MediaElement(); extend(mel,{ addEventListener: mel.addListener, pause: function() { vkopt.log('Paused'); mel.emit('pause'); mel.emit('play'); return false; }, preload: 'auto', buffered: {}, duration: 1, seeking: false, height: 1080, width: 1920, loop: false, played: {}, removeAttribute: function(attr){}, load: function(l){vkopt.log('Load:',this)}, canPlayType: function(codec){vkopt.log('Got asked about:'+codec); return 'probably'}, nodeName: 'audio', playbackQuality: {}, getVideoPlaybackQuality: function() {return mel.playbackQuality}, addTextTrack: function(tt){vkopt.log('Adding textTrack ',tt); return [{}]}, textTracks: { addEventListener: function(e,cb) { vkopt.log('** Adding eventListener: '+e); } } }) function SourceGrabBuffer(mimetype){ EventEmitter.call(this); this._mimetype = mimetype; return this; } SourceGrabBuffer.prototype = Object.create(EventEmitter.prototype); var sgb = SourceGrabBuffer.prototype; sgb.addEventListener = function(eventName, callback) { this.addListener(eventName, callback); //vkopt.log('sgb addEventListener '+eventName); } sgb.appendBuffer = function(data) { //vkopt.log('append: ', data.length, 'bytes ', this._mimetype); var that = this; onSegmentReady && onSegmentReady(data, this._mimetype); setTimeout(function(){ that.emit('onupdateend'); that.emit('updateend'); },5); } Object.defineProperty(sgb, "buffered", { get: function buffered() { return []; } }); Object.defineProperty(sgb, "mode", { get: function mode() { return ''; } }); SourceGrabBuffer.prototype.constructor = SourceGrabBuffer; function MediaGrabSource(){ EventEmitter.call(this); this.readyState = 'closed'; this._sb = {}; return this; } MediaGrabSource.prototype = Object.create(EventEmitter.prototype); var mgs = MediaGrabSource.prototype; extend(mgs,{ addEventListener: function(eventName,callback) { //vkopt.log('MediaGrabSource addEventListener: '+eventName); this.addListener(eventName,callback); this.readyState = 'open'; //vkopt.log(Object.keys(this._sb).length); if (eventName == 'sourceopen') { vkopt.log('Fired: '+eventName); //this.emit(eventName,this); } }, removeEventListener: function(eventName,b) { this.removeListener(eventName,b); }, addSourceBuffer: function(mimetype) { vkopt.log('MediaGrabSource new buffer for '+mimetype); var nb = new SourceGrabBuffer(mimetype); this._sb[mimetype] = nb; return nb; }, endOfStream: function(error) { vkopt.log('** End of stream: '+error); mel.emit('ended'); // onDone(); }, isTypeSupported: function(codec){return true} }); Object.defineProperty(mgs, "sourceBuffers", { get: function sourceBuffers() { var a = []; for (var sb in this._sb) { a.push(this._sb[sb]); } return a; } }); MediaGrabSource.isTypeSupported = function(codec){return true} var get_module=function(js){ var exports = {}; eval(js); return exports; } var modify_hls = function(js){ js = js.replace(/window\.MediaSource/g,'MediaGrabSource') .replace(/window\.SourceBuffer/g,'SourceGrabBuffer') .replace(/\.createObjectURL/g,'.createGrabObjectURL') .replace(/\.revokeObjectURL/g,'.revokeGrabObjectURL'); return js; } var Hls = {}; function downloadHls(url) { var startSN = 0, endSN = 0; var hls = new Hls({debug:false,autoStartLoad:true}); hls.on(Hls.Events.MEDIA_ATTACHED, function () { vkopt.log("hls.js attached to grabber"); hls.loadSource(url); }); hls.on(Hls.Events.BUFFER_EOS,function(event_name, info){ vkopt.log('GRABBER DONE: ',Hls.Events.FRAG_LOADED); onDone && onDone(); }); hls.on(Hls.Events.FRAG_LOADED,function(event_name, info){ if (info && info.frag && info.frag.sn) setTimeout(function(){ onProgress && onProgress(info.frag.sn - startSN, endSN - startSN); },30); }); hls.on(Hls.Events.MANIFEST_PARSED,function(n,m) { vkopt.log('manifest_parsed', m); startSN = m.levels[0].details.startSN endSN = m.levels[0].details.endSN mel.paused = false; mel.currentTime = 0; mel.emit('pause'); mel.emit('playing'); }); hls.attachMedia(mel); } AjGet('/js/lib/hls.min.js', function(js){ Hls = get_module(modify_hls(js)).Hls; downloadHls(m3u8); }); }, download: function(url, file_name, aid){ var buff = { type: '', data:[] }; var draw_progress = function(percent, aid){ //vkopt.log(percent,'%'); var els = geByClass('_audio_row_'+aid); for (var i = 0; i < els.length; i++){ var bar = geByClass1('vk_grab_progress', els[i]); if (!bar){ bar = se('
'); els[i].insertBefore(bar, els[i].firstChild); } show(bar); bar.dataset['progress'] = percent; bar.style.width = percent+'%'; } }; var hide_progress = function(aid){ var els = geByClass('_audio_row_'+aid); for (var i = 0; i < els.length; i++){ var bar = geByClass1('vk_grab_progress', els[i]); bar && re(bar); } }; vkopt.hls.grab(url, { onSegmentReady: function(data, type){ buff.type = type; buff.data.push(data); }, onDone: function(){ setTimeout(function(){ hide_progress(aid); },500); var url_create = (window.URL || window.webkitURL || window.mozURL || window).createObjectURL; var data = new Blob(buff.data,{type:buff.type}); var url = url_create(data) var dlnk = document.createElement('a'); dlnk.href = url; dlnk.download = file_name; (window.utilsNode || document.body).appendChild(dlnk) dlnk.click(); setTimeout(function(){ re(dlnk); //url_revoke(url); },200); }, onProgress: function(cur, total){ if (total == 0) return; var pc = Math.round(cur/total*100); draw_progress(pc, aid); } }); } } vkopt['videoview'] = { onSettings:{ Media:{ vid_dl: { title: 'seLinkVi' } } }, css: function(){ return vk_lib.get_block_comments(function(){ /*css: .vk_mv_down_icon { background: url(/images/icons/video_icon.png?3) no-repeat; background-position: 0 -52px; height: 19px; width: 20px; transform: rotate(90deg); } .vk_mv_down_links_tt { background: rgba(0,0,0,0.6); border: 1px solid rgba(255,255,255,0.4); } .vk_mv_down_links_tt.eltt.eltt_bottom:before { border-bottom-color: transparent; } .vk_mv_down_links_tt.eltt.eltt_bottom:after { border-bottom-color: rgba(255, 255, 255, 0.4); margin-bottom: 1px; } .vk_mv_down_links_tt.eltt.eltt_bottom .eltt_arrow{ border-bottom-color: #000; } .vk_mv_down_links_tt a { display: block; padding: 3px 10px; color: #FFF; white-space: nowrap; } .vk_mv_down_links_tt a.size_loaded{ padding-right: 80px; } .vk_mv_down_links_tt a .vk_vid_size_info.progress_inv_mini{ position: absolute; margin-top: 6px; right: 3px; } .vk_mv_down_links_tt a.size_loaded .vk_vid_size_info{ position: absolute; right: 10px; } #mv_top_controls{ z-index: 1000; } .vk_vid_size_info b{ color: #FFF; padding-left: 10px; } */ }).css }, onInit: function(){ vkopt.videoview.tpls = vk_lib.get_block_comments(function(){ /*dl_btn:
*/ /*dl_link: {vals.caption} */ /*ext_link: {vals.source_name} */ }); }, onLibFiles: function(fn){ if (fn == 'videoview.js'){ Inj.Start('Videoview.showVideo', function(){ vkopt.videoview.on_show(arguments); }); } }, /* onResponseAnswer: function(answer, url, q){ // запихиваем свой обработчик в момент получения данных о видео. if (url == '/al_video.php' && q.act == 'show'){ vkopt.videoview.check_show_args(answer); } }, */ _cur_mv_data: null, update_dl_btn: function(html){ re('vk_mv_down_icon'); // убиваем кнопку, т.к не выходит убить тултип таким образом: data(ge('vk_mv_down_icon'), 'ett').destroy(); if (!html) return null; var btn; if (!ge('vk_mv_down_icon') && ge('VideoLayerInfo__topControls')){ btn = se(vk_lib.tpl_process(vkopt.videoview.tpls['dl_btn'], {})); ge('VideoLayerInfo__topControls').appendChild(btn); } else { return; } // создаём новое тултип-меню vkopt.videoview._links_tt = new ElementTooltip(btn,{ cls: "vk_mv_down_links_tt", forceSide: "bottom", elClassWhenTooltip: "vk_mv_down_links_shown", content: html, offset: [-3, 0], setPos: function(){ return { left: 0, top: 36, arrowPosition: 21 } } }); }, get_vars: function(opt, video_id){ var vars = null; if (opt && opt.player){ var params_arr = opt.player.params; if (params_arr){ vars = {}; var full_vid = function(vars){ return vars.oid+'_'+vars.vid }; if (params_arr.length > 0 && full_vid(vars) != video_id){ //mvcur.videoRaw vkopt.log('wrong video data. search other...'); for (var i = 0; i < params_arr.length; i++){ if (full_vid(params_arr[i]) == video_id){ // нашли данные о нужном видео vars = params_arr[i]; break; } } } } } return vars; }, check_show_args: function(args){ //args = [videoRaw, title, html, js, desc, serviceBtns, opt] var videoRaw = args[0], js = args[3], opt = args[5], rx = /(var\s*isInline)/; if (opt && opt.player){// новый формат ответа, JSON с данными о плеере находится в 6-ом аргументе. var vars = vkopt.videoview.get_vars(opt,videoRaw); vkopt.videoview.on_player_data(vars); } else if (js && rx.test(args[3])){ // старый формат ответа, vars находится в третьем аргументе. //vkopt.log('video data:', args[3]); args[3] = js.replace(rx, '\n vkopt.videoview.on_player_data(vars);\n $1'); } else vkopt.videoview.on_player_data(null); }, on_show: function(args){ vkopt.log('vkopt.videoview.on_show', args); vkopt.videoview.check_show_args(args); }, download_file: function(el){ var result = vkopt.permissions.check_dl_url(el, el.href); if (result) result = vkDownloadFile(el); return result; }, on_player_data: function(vars){ vkopt.log('Video data:', vars, mvcur.mvData.videoRaw); if (!vkopt.settings.get('vid_dl')) return; vkopt.videoview._cur_mv_data = vars; vkopt.videoview.update_dl_btn(); if (!vars || !vars.md_title || (vars.extra && !vars.hls && !vars.postlive_mp4)){ setTimeout(function(){ var p, ifr; p = ge('mv_player_box'); p && (ifr = geByTag1('iframe', p)); if (ifr) vkopt.videoview.on_iframe_player(ifr.src) else vkopt.videoview.on_iframe_player(vars) }, 300); return; // нет данных - выходим. } var links = vkopt.videoview.get_video_links(vars); var filename = vkCleanFileName(unclean(vars.md_title)); var html = ''; for (var i = 0; i < links.length; i++){ html += vk_lib.tpl_process(vkopt.videoview.tpls['dl_link'], { url: links[i].url + (links[i].ext ? '#FILENAME/' + vkEncodeFileName(filename + '_' + links[i].quality) + links[i].ext : ''), name: filename + '_' + links[i].quality + links[i].ext, caption: links[i].quality }) } vkopt.videoview.update_dl_btn(html); }, get_ext_links: function(url, title, cb){ vkopt.log('External player:', url); if (isString(url)) { if (url.indexOf('ivi.ru') > -1){ vkopt.videoview.get_ivi_links(url, function(links, vid){ var html = ''; var filename = vkCleanFileName(title); html += vk_lib.tpl_process(vkopt.videoview.tpls['ext_link'], { url: 'http://www.ivi.ru/watch/' + vid, source_name:'ivi.ru' }); for (var i = 0; i < links.length; i++){ html += vk_lib.tpl_process(vkopt.videoview.tpls['dl_link'], { url: links[i].url, name: filename + '_' + links[i].quality + '.mp4', caption: links[i].quality }) } cb && cb(html, links); }) } else if (url.indexOf('youtube.com') > -1){ vkopt.videoview.yt.get_links(url, function(links, vid){ var html = ''; var filename = vkCleanFileName(title); html += vk_lib.tpl_process(vkopt.videoview.tpls['ext_link'], { url: 'http://youtube.com/watch?v=' + vid, source_name: 'YouTube' }); for (var i = 0; i < links.length; i++){ html += vk_lib.tpl_process(vkopt.videoview.tpls['dl_link'], { url: links[i].url, name: filename + '_' + links[i].quality + '.mp4', caption: links[i].quality }) } cb && cb(html, links); }) } else cb && cb('', []); } else cb && cb('', []); }, on_iframe_player: function(url){ vkopt.videoview.get_ext_links(url, unclean(mvcur.mvData.title), function(html, link){ vkopt.videoview.update_dl_btn(html); }); }, get_size: function(el,ev){ if (ev && (ev.metaKey || ev.ctrlKey)){ el.href = el.href.split('#')[0]; } if (!el || !el.href || hasClass(el,'size_loaded') || /\.m3u8/.test(el.href)) return; var WAIT_TIME = 4000; var szel = geByTag1('span', el); var set_size_info = function(size){ removeClass(szel,'progress_inv_mini'); if (size != null) szel.innerHTML = vkFileSize(size, 1).replace(/([\d\.]+)/,'$1'); if (size > 500) addClass(el, 'size_loaded'); } szel.innerHTML = ''; addClass(szel,'progress_inv_mini'); var reset=setTimeout(function(){ set_size_info(null); }, WAIT_TIME); XFR.post(el.href, {}, function(h, size){ clearTimeout(reset); if (size > 0){ set_size_info(size); } else { // TODO: видать ссылка протухла. нужно подгрузить актуальный URL и снова запросить размер } }, true); }, get_video_url: function(vars, q) { return vars.live_mp4 ? vars.live_mp4 : vars.extra_data ? vars.extra_data : vars["cache" + q] || vars["url" + q] }, get_video_links: function(vars){ var list = []; // 'ffmpeg -i "'+vars.hls+'" -c copy video.ts' if (vars.hls) list.push({url: vars.hls, quality: 'hls', ext: '.m3u8'}); //if (vars.hls_raw) // list.push({text: vars.hls_raw, quality: 'hls_raw', ext: '.m3u8'}); if (vars.postlive_mp4) list.push({url: vars.postlive_mp4, quality: 'mp4', ext: '.mp4'}); if (vars.live_mp4) list.push({url: vars.live_mp4, quality: 'live_mp4', ext: '.mp4'}); if (vars.extra_data && (vars.extra_data != (vars.author_id+'_'+vars.vid))) list.push({url: vars.extra_data, quality: 'extra'}); var q = [240, 360, 480, 720, 1080]; for (var i = 0; i <= vars.hd; i++){ var qname = q[i] || 0; vars["url" + qname] && list.push({url: vars["url" + qname], quality: qname+'p', ext: '.mp4'}); vars["cache" + qname] && list.push({url: vars["cache" + qname], quality: qname+'p_alt', ext: '.mp4'}) } return list; }, yt: { decode_data : function (qa) { // декодирование URL-encoded объектов if (!qa) return {}; var exclude = { 'url' : 1, 'type' : 1, 'ttsurl' : 1 }; var query = {}, dec = function (str) { try { return decodeURIComponent(str); } catch (e) { return str; } }; qa = qa.split('&'); for (var i = 0; i < qa.length; i++) { var a = qa[i]; var t = a.split('='); if (t[0]) { var key = dec(t[0]); var v = exclude[key] ? [dec(t[1] + '')] : dec(t[1] + '').split(','); query[key] = []; for (var j = 0; j < v.length; j++) { if (v[j].indexOf('&') != -1 && v[j].indexOf('=') != -1 && !exclude[key]) v[j] = vkopt.videoview.yt.decode_data(v[j]); query[key].push(v[j]); } if (query[key].length == 1) query[key] = query[key][0]; } } return query; }, video_itag_formats: { //YouTube formats list '0': '240p.flv', '5': '240p.flv', '6': '360p.flv', '34': '360p.flv', '35': '480p.flv', '13': '144p.3gp (small)', '17': '144p.3gp (medium)', '36': '240p.3gp', '160': '240p.mp4 (no audio)', '18': '360p.mp4', '135': '480p.mp4 (no audio)', '22': '720p.mp4', '37': '1080p.mp4', '137': '1080p.mp4 (no audio)', '38': '4k.mp4', '82': '360p.mp4',//3d? //'83': '480p.mp4',//3d? '84': '720p.mp4',//3d? //'85': '1080p.mp4',//3d? '242': '240p.WebM (no audio)', '43': '360p.WebM', '44': '480p.WebM', '244': '480p.WebM (low, no audio)', '45': '720p.WebM', '247': '720p.WebM (no audio)', '46': '1080p.WebM', '248': '1080p.WebM (no audio)', '100':'360p.WebM',//3d? //'101':'480p.WebM',//3d? '102':'720p.WebM',//3d? //'103':'1080p.WebM',//3d? '139': '48kbs.aac', '140': '128kbs.aac', '141': '256kbs.aac', '171': '128kbs.ogg', '172': '172kbs.ogg' }, get_links : function (url, callback) { var vid = String(url).split('?')[0].split('/').pop(); var req_url = (vk_ext_api.ready ? 'http:' : location.protocol) + '//www.youtube.com/get_video_info?video_id=' + vid; XFR.post(req_url, {}, function (t) { /* var decode_s = function (a) { var mod = { del_left : function (a, b) { a.splice(0, b) }, calc : function (a, b) { var c = a[0]; a[0] = a[b % a.length]; a[b] = c }, reverse : function (a) { a.reverse() } }; a = a.split(""); mod.calc(a, 19); mod.reverse(a); mod.del_left(a, 1); mod.reverse(a); mod.del_left(a, 1); mod.calc(a, 7); mod.reverse(a); mod.calc(a, 38); mod.del_left(a, 3); return a.join("") }; */ var obj = vkopt.videoview.yt.decode_data(t); vkopt.log('YT raw data:', obj); var map = (obj.fmt_url_map || obj.url_encoded_fmt_stream_map || obj.adaptive_fmts); if (!map) { callback([], vid); return; } var links = []; for (var i = 0; i < map.length; i++) { if (!map[i].sig && map[i].s) continue; //map[i].sig = decode_s(map[i].s); var format = vkopt.videoview.yt.video_itag_formats[map[i].itag]; var info = (map[i].type + '').split(';')[0] + ' ' + (obj.fmt_list[i] + '').split('/')[1]; if (!format) vkopt.log('YT ' + map[i].itag + ': \n' + (map[i].stereo3d ? '3D/' : '') + info, 1); format = (map[i].stereo3d ? '3D/' : '') + (format || info); obj.title = isArray(obj.title) ? obj.title.join('') : obj.title; var url = map[i].url; if (url.indexOf('&signature=') == -1 && map[i].sig) url += '&signature=' + map[i].sig; url += '&quality=' + map[i].quality + (obj.title ? '&title=' + encodeURIComponent(obj.title.replace(/\+/g, ' ')) : ''); links.push({ url : url, quality : format, info : info }); // adaptive_fmts } callback(links, vid); }); } }, get_ivi_links:function(url,callback){ var vid = isNumeric(url) ? url : (url.match(/(?:videoId|id)=(\d+)/) || [])[1]; if (!vid) return; // 'http://www.ivi.ru/watch/'+vid // https://www.ivi.ru/embeds/video/?id=126872&app_version=340&autostart=1 var app_ver = 340; var rnd=Math.random()*1000000000000; var data={ "method" : "da.content.get", "params" : [ vid, { "sourceid" : "", "utmfullinfo" : "", "_domain" : "www.ivi.ru", "app_version" : app_ver, "_url" : "https://www.ivi.ru/embeds/video/?id="+vid+"&app_version="+app_ver+"&autostart=1", "site" : "s132", "campaignid" : "", "uid" : rnd+"" } ] }; var ondone=function(r) { var vars=JSON.parse(r); var links=vars.result.files; if (!links){ callback([]); return; } var res=[]; for (var i=0; idiv.vk_video_thumb_action_link .icon { background-image: url("/images/icons/pv_actions.png"); background-size: cover; background-position: 1px 3px; } .video_thumb_actions>div.vk_video_thumb_action_link.vk_cant_get_link, .video_thumb_actions>div.vk_video_thumb_action_link.vk_cant_get_link:hover { opacity:0.2; cursor: default; } .video_thumb_actions>div.vk_video_thumb_action_link { display: inline-block; } .vk_video_thumb_action_link a { display: block; line-height: 14px; } .video_thumb_actions>div.vk_video_thumb_action_link:active { position: static; } .video_thumb_actions>div.vk_video_thumb_action_link.vk_links_loading .icon { background: url(/images/upload_inv_mini.gif) 50% 50% no-repeat; } .videoplayer_settings_menu_sublist_item.vk_wg_dl_item{ justify-content: flex-start; } #vk_wg_dl_menu{ width: 160px; } .videoplayer_settings_menu_list_icon.vk_wg_dl_icon { width: 18px; height: 16px; display: inline-block; margin-right: 23px; vertical-align: top; background-image: url(/images/icons/pv_actions.png?3); background-position: 0 0px; } */ }).css }, onLibFiles: function(fn){ if (fn == 'video.js'){ Inj.End('Video.buildVideoEl', function(){ if (this.result) vkopt.videos.processNode(this.result); }) } if ((fn == 'videoplayer.js') && nav.objLoc[0]=="video_ext.php"){ var params = document.body.innerHTML.match(/playerParams\s*=\s*(\{[\s\S]+\}\]\});/); params = params && JSON.parse(params[1]); params && setTimeout(function(){vkopt.videos.widget_player(params)}, 10); } }, onInit: function(){ vkopt.videos.tpls = vk_lib.get_block_comments(function(){ /*dl_btn: */ /*wg_show_dl:
{lng.download}
*/ /*wg_sub_menu: */ /*wg_dl_item: {vals.caption} */ }); }, processNode: function(node, params){ if (!vkopt.settings.get('vid_dl')) return; if (!node) return; var nodes = geByClass('video_thumb_actions',node); for (var i = 0; i < nodes.length; i++){ var acts = nodes[i]; if (geByClass1('vk_video_thumb_action_link', acts)) continue; var vid_el = gpeByClass('video_item', acts); if (!vid_el) continue; var a = (geByTag1('a',vid_el) || {}).href || ''; var ids = a.match(/video(-?\d+_\d+)(?:\?list=([a-f0-9]+))?/); if (!ids) continue; acts.appendChild( se( vk_lib.tpl_process(vkopt.videos.tpls['dl_btn'], { video: ids[1], list: ids[2] || '' }) ) ); } }, widget_player: function(params){ if (!vkopt.settings.get('vid_dl')) return; var vars = vkopt.videoview.get_vars({player:params},nav.objLoc.oid+'_'+nav.objLoc.id), menu = geByClass1('videoplayer_settings_menu_list'); if (!vars || !menu) return; var links = vkopt.videoview.get_video_links(vars); var filename = vkCleanFileName(unclean(vars.md_title)); var html = ''; for (var i = 0; i < links.length; i++){ html += vk_lib.tpl_process(vkopt.videos.tpls['wg_dl_item'], { url: links[i].url + (links[i].ext ? '#FILENAME/' + vkEncodeFileName(filename + '_' + links[i].quality) + links[i].ext : ''), name: filename + '_' + links[i].quality + links[i].ext, caption: links[i].quality }) } var dl_menu = se( vk_lib.tpl_process(vkopt.videos.tpls['wg_sub_menu'], { content: html }) ); menu.parentNode.appendChild(dl_menu); menu.insertBefore(se(vk_lib.tpl_process(vkopt.videos.tpls['wg_show_dl'],{})),menu.firstChild) }, show_links: function(ev, el, video, list){ cancelEvent(ev); if (hasClass(el,'vk_links_loading') || hasClass(el,'vk_links_loaded')) return false; addClass(el,'vk_links_loading'); var on_links_ready = function(html, links){ if (links.length){ removeClass(el,'vk_links_loading'); addClass(el,'vk_links_loaded'); el.dl_ett = new ElementTooltip(el,{ cls: "vk_mv_down_links_tt", forceSide: "bottom", elClassWhenTooltip: "vk_mv_down_links_shown", content: html, offset: [-3, 0], setPos: function(){ return { left: 33, top: 34, arrowPosition: 21 } } }); el.dl_ett.show(); } } var failed = function(){ addClass(el, 'vk_cant_get_link') } ajax.post('al_video.php', {act: "show", list: list, video: video}, { onDone: function(title, vid_box, js, html, data){ if (vid_box && /