NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Download Sibnet Video as MP4 // @namespace http://video.sibnet.ru // @description Скачивание видео одним кликом, правильные названия видео при скачивании // @include *://video.sibnet.ru/* // @include *://cv*.sibnet.ru/* // @include *://dv*.sibnet.ru/* // @connect sibnet.ru // @version 1.4.1 // @author Iron_man // @updateURL https://openuserjs.org/meta/Iron_man/Download_Sibnet_Video_as_MP4.meta.js // @copyright 2017, Iron_man (https://openuserjs.org/users/Iron_man) // @license MIT // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @run-at document-start // ==/UserScript== (function(){ console.log("Download Sibnet Videos start.."); var userOptions = initOptions(); userOptions.set({ clickCancel : true,// автоматически нажать на отмену в конец видео removeAds : true,// удалить рекламу addVideoId : true,// добавить видео id в название файла useGMDownloader : false,// всегда использовать GM.xmlHttpRequest для скачивания файла (не рекомендуется) maxSize : 16 * 1024 * 1024,// (16 Mb) максимальный размер файла для скачивания с помощью GM.xmlHttpRequest delay : 500,// задержка в мс перед закрытием загрузочного iframe'a style: 0,// номер стиля диалогового окна }); var RANDOM = '1669048',// Math.round(Math.random() * 1000000 + 1000000), isSourcePage = downloadFromSourcePage(); if( !isSourcePage ) document.addEventListener('DOMContentLoaded', start, false ); function downloadFromSourcePage() { var href = window.location.href, match = href.match(/^((https?\:)?\/\/(([^\/\?\#\.]+)\.([^\/\?\#]+)))([^\?\#]+)([^\#]+)(.*)/); if( match[4] !== 'video' && match[8] && match[8].indexOf('.mp4') != -1 ) { var fileName = match[8].slice(1), fileUrl = href.match(/([^\#]+)(.*)/)[1], sendMessage = function(){window.parent.postMessage( 'closeIFrame', '*' );}, closeWindow = function(){window.close();}, videoClose = function(){ if( window.self !== window.parent ) setTimeout( sendMessage, userOptions.val('delay') ); else setTimeout( closeWindow, userOptions.val('delay') ); }; fileName = decodeURIComponent(fileName); document.addEventListener('readystatechange', function(event){ if( this.readyState === 'interactive' ) { downloadFile( fileName, fileUrl ); var video = document.querySelector('video'); if( video ) video.addEventListener('error', videoClose, false); } else if( this.readyState === 'complete' ) videoClose(); }, false); return true; } return false; } function downloadFile( name, resource ) { var a = document.createElement('a'), body = document.body || document.getElementsByTagName('body')[0]; a.setAttribute('download', name); a.href = resource; body.appendChild(a); a.click(); a.parentNode.removeChild(a); } function start() { if( userOptions.val('removeAds') ) removeAds(); if( userOptions.val('clickCancel') ) setCancelPostvideo(); var pladform = detectPladformIFrames(); if( pladform ) { console.log('embeded video from pladform.ru'); return; } var video_path = getVideoPath();// get video path name '/v/{numbers}/{videoid}.mpd' from current html source page console.log("video path: " + video_path); if( video_path ) { GM.xmlHttpRequest({ url: video_path, method: 'HEAD', onload: makeVideoLink, }); } newCssClasses('dsv-style-id'); } function getVideoPath() { var video, source_str, pos, end; video = document.getElementById('video'); if( video ) source_str = video.innerHTML; else source_str = document.body.innerHTML; pos = source_str.indexOf( "player.src([{src: \"" ); if( pos == -1 ) return null; pos = source_str.indexOf("/v/", pos); end = source_str.indexOf(".mpd", pos); if( end == -1 ) return null; return source_str.substring(pos, end+4); } function makeVideoLink( xhr ) { var video_link = xhr.finalUrl.replace('/manifest.mpd', '.mp4');// get downloadable video link console.log("video file: ", video_link); if( !video_link ) { console.error("[makeVideoLink] can't find video source link"); return; } var st = insertLink( video_link );// try to insert the link into 'video_size' element of html source page if( st !== 0 ) confirmDownloadFile( video_link ); } function insertLink( source_link, size_mb ) // insert hyper reference into video_size element { var video_size = document.getElementsByClassName('video_size')[0]; if( !video_size ) return 1; size_mb = size_mb || video_size.innerHTML; video_size.innerHTML = '' + '<a id="video_file_' + RANDOM + '" class="video_file_active" href="' + source_link + '" ' + 'title="Скачать">' + size_mb + '</a>'; var video_file = document.getElementById('video_file_' + RANDOM), bytes = (parseInt(size_mb.match(/\d+/)[0], 10) || 0) * 1024 * 1024; if( bytes ) video_file.setAttribute('file-size', bytes); video_file.addEventListener('click', handleDownloadFileEvent, false); return 0; } function handleDownloadFileEvent(event) { var t = event.target; if( event.ctrlKey ) return; else if( !t.classList.contains('video_file_active') ) return; event.preventDefault(); if( t.classList.contains('video_file_active') ) smartDownloadFile( getFileName(), t.href, t.getAttribute('file-size') ); } function smartDownloadFile( name, source, size ) { if( userOptions.val('useGMDownloader') || (size && size < userOptions.val('maxSize')) ) GM_downloadFile(name, source); else makeIFrame( name, source ); } function getFileName() { var fileName = document.querySelector('td.video_name > h1'), videoIdStr = (userOptions.val('addVideoId') ? " " + getVideoId() : ""); if( fileName ) return fileName.innerHTML + videoIdStr + ".mp4"; fileName = document.querySelector('meta[property="og:title"]'); if( fileName ) return fileName.getAttribute('content') + videoIdStr + ".mp4"; return "video_name_" + getVideoId() + ".mp4"; } function getVideoId() { var href = window.location.href; return href.match(/video(id\=|)(\d+)/)[2]; } function confirmDownloadFile( source ) { var fileName = getFileName(); makeConfirmWindow(); setConfirmWindow( fileName, 0, source); GM.xmlHttpRequest({ url: source, method: 'HEAD', context: {'url': source, 'name': fileName}, onload: function(xhr){ if( xhr.status !== 200 ) { console.error("xhr.status: ", xhr.status, xhr.statusText ); console.error("url: " + source); console.error("method: " + 'HEAD'); return; } var fileSize = getContentLength( xhr.responseHeaders ); setConfirmWindow( xhr.context.name, fileSize, xhr.context.url ); } }); } function makeConfirmWindow() { var confirmWnd = document.querySelector('#confirm_downlaod_window_' + RANDOM); if( !confirmWnd ) { confirmWnd = document.createElement('div'); confirmWnd.setAttribute('id', 'confirm_downlaod_window_' + RANDOM); confirmWnd.setAttribute('class', 'confirm_download_window'); var body = document.body || document.getElementsByTagName('body')[0]; body.appendChild(confirmWnd); var html = '' + '<div id="confirm-filename"></div>' + '<div id="confirm-filesize"></div>' + '<div id="confirm-bottom">' + '<button id="confirm-download-button-true" class="confirm-button">Скачать</button>' + '<button id="open-video-source-file" class="confirm-button">Открыть</button>' + '<button id="confirm-download-button-false" class="confirm-button">Отмена</button>' + '</div>' + /* '<div id="color-style-id" style="text-align: center;">' + '<button id="change-color-style" class="confirm-button">Сменить стиль</button>' + '</div>' + */ ''; confirmWnd.innerHTML = html; confirmWnd.addEventListener('click', handleConfirmEvent, false); } confirmWnd.style.display = 'block'; return confirmWnd; } function setConfirmWindow( fileName, fileSize, fileUrl ) { var confirmWnd = document.querySelector('#confirm_downlaod_window_' + RANDOM) || makeConfirmWindow(); var fileNameDiv = confirmWnd.querySelector('#confirm-filename'), fileSizeDiv = confirmWnd.querySelector('#confirm-filesize'), fileSourceBtn = confirmWnd.querySelector('#open-video-source-file'); fileNameDiv.innerHTML = 'Имя файла: ' + shortenFileName(fileName); fileNameDiv.setAttribute('title', fileName); fileSizeDiv.innerHTML = 'Размер файла: ' + bytesToMB(fileSize, 1) + ' Mb'; if( fileSize ) fileSizeDiv.setAttribute('title', bytesToKB(fileSize) + ' Kb' ); fileSourceBtn.setAttribute('title', fileUrl ); confirmWnd.setAttribute('file-name', fileName); confirmWnd.setAttribute('file-size', fileSize); confirmWnd.setAttribute('file-source', fileUrl ); confirmWnd.style.display = 'block'; } function bytesToKB( bytes, precision ) { if( bytes ) return (bytes/1024).toFixed(precision || 0); return '--'; } function shortenFileName( fileName ) { this.maxLen = this.maxLen || 25; var nameLen = fileName.length; if( nameLen > this.maxLen ) { var nameEnd = fileName.slice(-11); fileName = fileName.slice(0, (this.maxLen - nameEnd.length) ); fileName += '...' + nameEnd; } return fileName; } function handleConfirmEvent(event) { var t = event.target; if( t.tagName !== 'BUTTON' ) return; if( t.id === 'confirm-download-button-false' ) this.style.display = 'none'; else if( t.id === 'confirm-download-button-true' ) { var fileName = this.getAttribute('file-name'), fileUrl = this.getAttribute('file-source'), fileSize = this.getAttribute('file-size'); this.style.display = 'none'; smartDownloadFile( fileName, fileUrl, fileSize ); } else if( t.id === 'open-video-source-file' ) window.open( this.getAttribute('file-source') ); /* else if( t.id === 'change-color-style' ) { var idx = userOptions.data.style.idx; userOptions.val('style', idx+1); resetCssClasses('dsv-style-id'); } */ } function getContentLength( headersStr ) { var headers = headersStr.split('\r\n'); for( var i = 0, h; i < headers.length; ++i ) { h = headers[i]; if( h.indexOf('Content-Length') != -1 ) return parseInt(h.match(/\d+/)[0], 10); } return 0; } function makeIFrame( name, source ) { var iframe = document.querySelector('#video_download_iframe_' + RANDOM); if( !iframe ) { iframe = document.createElement('iframe'); iframe.setAttribute('id', 'video_download_iframe_' + RANDOM); var body = document.querySelector('body'); body.appendChild(iframe); } name = encodeURIComponent(name); iframe.src = (source + '#' + name); } function closeIFrame() { var ifr = document.querySelector('#video_download_iframe_' + RANDOM); if( ifr ) ifr.parentNode.removeChild(ifr); } function GM_downloadFile( name, source ) { GM.xmlHttpRequest({ url: source, method: 'GET', responseType: 'blob', onload: function(xhr){ if( xhr.status !== 200 ) { console.error("xhr.status: ", xhr.status); console.error("url: ", source); return; } console.log("source: " + source); console.log("name: " + name); var wURL = window.webkitURL || window.URL, resource = wURL.createObjectURL(xhr.response); downloadFile( name, resource ); var video_file = document.querySelector('#video_file_' + RANDOM); if( video_file ) video_file.classList.add('video_file_active'); wURL.revokeObjectURL(resource); }, onprogress: function(xhr){ if( !xhr.lengthComputable ) return; showDownloadWindow(xhr.total, xhr.loaded); } }); } function showDownloadWindow(total, loaded) { var dlWnd = document.querySelector('#download_window_' + RANDOM); if( !dlWnd ) { dlWnd = document.createElement('div'); dlWnd.setAttribute('id', 'download_window_' + RANDOM); dlWnd.setAttribute('class', 'video_download_window'); dlWnd = (document.body || document.getElementsByTagName('body')[0]).appendChild(dlWnd); } dlWnd.style.display = ''; var html = bytesToMB(loaded, 1) + ' Mb / ' + bytesToMB(total, 1) + ' Mb' + ' (' + (loaded/total*100).toPrecision(3) + '%)'; dlWnd.innerHTML = html; if( total === loaded ) { setTimeout(function(){ dlWnd.style.display = 'none'; }, 3000 ); } } function bytesToMB( bytes, precision ) { if( bytes ) return (bytes/(1024*1024)).toFixed(precision || 0); return '--'; } function addCssClass( cssClass, id ) { var head = document.head || document.getElementsByTagName('head')[0], style = document.createElement('style'); style.type = 'text/css'; if( id ) style.id = id; if( style.styleSheets ) style.styleSheets.cssText = cssClass; else style.appendChild(document.createTextNode(cssClass)); head.appendChild(style); } function resetCssClasses( id ) { if( !id ) return; var style = document.getElementById(id); if( style ) style.parentNode.removeChild( style ); newCssClasses( id ); } function newCssClasses( id ) { addCssClass(` .confirm-button { background-color: ${userOptions.val('style')['background-color']}; color: #c7c7c7; font-size: 1.0em; border-radius: 5px; font-family: sans-serif; border: 2px solid #c7c7c7; cursor: pointer; margin: 0.4% 2%; padding: 0 3px; test-align: center; } .confirm-button:hover { background-color: ${userOptions.val('style')['background-color-hover']}; border-color: white; color: white; } #confirm-bottom { padding: 3px 0 2px 0; } #confirm-bottom { text-align: center; } .confirm_download_window, .video_download_window { position: fixed; bottom: 20px; right: 20px; z-index: 9999; background-color: ${userOptions.val('style')['background-color']}; color: white; //box-shadow: 5px 5px 5px #555555; font-size: 1.0em; font-family: sans-serif; border-radius: 5px; padding: 2px 10px; display:; cursor: default; border: 2px solid #c7c7c7; } `, id); } function removeAds() { var tbody = document.querySelector('.main tbody'); if( tbody && tbody.children && tbody.children[0] ) tbody.children[0].innerHTML = '<td style="height:0px"></td>'; } function setCancelPostvideo() { var player_container = document.getElementById('player_container'); if( player_container ) { player_container.addEventListener('ended', function(event) { var postvideo_cancel = document.getElementsByClassName('vjs-postvideo-cancel')[0]; if( postvideo_cancel ) setTimeout( function(){postvideo_cancel.click();}, 1000 ); }, true ); } } function detectPladformIFrames() { var iFrames = document.getElementsByTagName('iframe'), count = 0; for( var i = 0; i < iFrames.length; ++i ) { if( iFrames[i].src.indexOf('pladform.ru') != -1 ) { iFrames[i].setAttribute('name', 'pladform' + i); iFrames[i].setAttribute('id', 'pladform' + i); ++count; } } return count; } function recieveMessage(event) { if(event.origin.search(/(cv|dv).*\.sibnet\.ru/) != -1 ){ console.log( "origin: " + event.origin ); if( event.data === 'closeIFrame' ) setTimeout( closeIFrame, userOptions.val('delay') ); }else{ //console.info('[recieveMessage] message from ' + event.origin + ' is ignored'); } } window.addEventListener('message', recieveMessage, false ); function initOptions() { function _setDef(){this.val = this.def;} var wndStyle = []; function addColor( bg, bgh ) { wndStyle.push({ 'background-color': bg, 'background-color-hover': bgh, }); } addColor('#16a085', '#058f74' ); addColor('#0b72aa', '#095d8c' ); addColor('#2091d8', '#2080c7' ); addColor('#3678cc', '#2668bc' ); var retVal = { data: { 'clickCancel' : { val: null, def: true,// автоматически нажать на отмену в конец видео setDef: _setDef, }, 'removeAds' : { val: null, def: true,// удалить рекламу setDef: _setDef, }, 'addVideoId' : { val: null, def: true,// добавить видео id в название файла setDef: _setDef, }, 'useGMDownloader' : { val: null, def: false,// всегда использовать GM.xmlHttpRequest для скачивания файла (не рекомендуется) setDef: _setDef, }, 'maxSize' : { val: null, def: 16 * 1024 * 1024,// (16 Mb) максимальный размер файла для скачивания с помощью GM.xmlHttpRequest setDef: _setDef, validator: function(v){ return v < 268435456;// 256 * 1024 * 1024 } }, 'delay' : { val: null, def: 500,// задержка в мс перед закрытием загрузочного iframe'a setDef: _setDef, validator: function(v){ return v > 99; } }, 'style': { get val(){return wndStyle[this.idx];}, set val(n){ this.idx = n%wndStyle.length;}, def: 0,// номер стиля setDef: function(){ this.idx = this.def; }, validator: function(n){ return n >= 0; } }, }, val: function( prop, v ){ if( this.data[prop] ) { if( v !== undefined ) { if( this.data[prop].validator && this.data[prop].validator(v) ) this.data[prop].val = v; else this.data[prop].val = v; }else return this.data[prop].val; }else return null; }, setDefs: function(){ for( var key in this.data ) this.data[key].setDef(); }, set: function( opts ){ for( var key in opts ) this.val( key, opts[key] ); }, }; retVal.setDefs(); return retVal; } function remove(elm) { if( elm && elm.parentNode ) return elm.parentNode.removeChild(elm); } })();