NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Download FOX+CN+CC Videos as MP4
// @description Скачать видео каналов FOX, Cartoon Network, Comedy Central одним кликом. Прямые ссылки на видео под плеером (fox-fan.ru, cn-fan.ru, cc-fan.tv)
// @namespace https://openuserjs.org/users/Iron_man
// @include *://*.fox-fan.ru/series.php*
// @include *://*.cn-fan.ru/series.php*
// @include *://*.cc-fan.tv/series.php*
// @include *://*.cc-fan.ru/series.php*
// @include *://*.fox-fan.ru/video*
// @include *://88.150.190.2/video*
// @include *://81.94.196.236/video*
// @include *://109.169.87.137/video*
// @version 1.3.0
// @run-at document-start
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @updateURL https://openuserjs.org/meta/Iron_man/Download_FOX+CN+CC_Videos_as_MP4.meta.js
// @copyright 2017, Iron_man (https://openuserjs.org/users/Iron_man)
// @license MIT
// ==/UserScript==
var RANDOM = 1687511;//Math.round(Math.random()*1.e6 + 1.e6);
(function(){
var clog = console.log;
//clog = function(){};
var userOptions = initOptions();
userOptions.set({
'delim': ' - ',
'space': ' ',
'addEpisodeEng': true,// добавить англ. название эпизода
'delay': 500,
});
var isSource = downloadFromSource();
if( isSource )
return;
else
document.addEventListener('DOMContentLoaded', start, false);
function downloadFromSource()
{
/*
try{
alert("href: " + window.location.href + "\nhash" + window.location.hash);
}catch(e){console.error(e); alert(e);}
*/
var hash = window.location.hash,
name, source;
if( hash )
{
name = hash.slice(1);
name = decodeURIComponent(name);
source = window.location.origin + window.location.pathname;
//alert("name: " + name + "\nsource: " + source);
}else
return false;
if( source && name && /\.mp4$/.test(name) )
{
var sendMessage = function(){window.parent.postMessage({msg: 'close-iframe', 'id': window.self.name,}, '*');},
closeWindow = function(){window.close();},
videoClose = function(){
remove(document.querySelector('video'));
if( window.self !== window.parent )
setTimeout( sendMessage, userOptions.val('delay') );
else
setTimeout( closeWindow, userOptions.val('delay') );
};
document.addEventListener('readystatechange', function(event){
if( this.readyState === 'interactive' )
{
fileDownloader(name, source);
var video = document.querySelector('video');
if( video ) video.addEventListener('error', videoClose, false);
}
else if( this.readyState === 'complete' )
videoClose();
}, false);
return true;
}
return false;
}
function fileDownloader( name, resource )
{
var a = document.createElement('a'),
body = document.body || document.getElementsByTagName('body')[0];
a.href = resource;
a.setAttribute('download', name);
body.appendChild(a);
a.click();
body.removeChild(a);
}
function start()
{
console.log("start Download FOX+CN+CC Videos as MP4..");
getLinks();
showDetail('description');// описание
//showAll();
}
function getLinks()
{
var links = document.querySelectorAll('div#voice a');
for( var i = 0, href; i < links.length; ++i)
{
href = links[i].href;
GM.xmlHttpRequest({
url: href,
method: 'GET',
onload: handleXHR,
});
}
}
function handleXHR( xhr )
{
var doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = xhr.responseText;
setLink(doc);
}
function setLink( doc )
{
doc = doc || document;
var script = doc.querySelector('#centerSeries > script'),
video_obj, video_src, subt, div, video_div, video_link, file_name, player;
if( !script )
{
player = doc.querySelector('div[id*="player"]');
if( player )
script = player.nextElementSibling;
}
video_obj = script ? getVideoObject( script.innerHTML ) : null;
if( video_obj )
{
video_src = video_obj.file;
subt = video_obj.subtitle;
}
if( video_src )
{
div = document.getElementById('video-links-' + RANDOM) || createVideoDiv('video-links-' + RANDOM);
video_div = document.createElement('div');
file_name = getFileName(doc);
clog("file name: " + file_name);
video_div.innerHTML = '<a href="' + video_src + '" title="Скачать" ' +
'data-file-name="' + file_name + '" data-file-ext="mp4" data-file-source="' + video_src + '" ' +
(subt ? ('data-subtitle="' + subt + '"') : '') + ' ' +
'class="video-link">' + file_name + '</a>';
div.appendChild( video_div );
video_link = video_div.querySelector('a');
video_link.addEventListener('click', handleDownloadEvent, false);
GM.xmlHttpRequest({
url: video_src,
method: 'HEAD',
context: {url: video_src},
onload: function(xhr){
if( xhr.status != 200 )
{
console.error("Error: xhr.status = ", xhr.status, xhr.statusText);
return;
}
var videoSize = getContentLength(xhr.responseHeaders),
smartSize = "" + (videoSize/(1024*1024)).toFixed(1) + " Mb";
video_link.setAttribute('title', "Скачать, " + smartSize );
console.log("source: ", xhr.context.url, "size: ", smartSize);
},
});
}else{
console.error("can't find video source");
}
}
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 createVideoDiv( id )
{
var div = document.createElement('div');
div.setAttribute('id', id);
var html = '' +
'<font class="size18 link_20" title="Нажмите на ссылку, чтобы скачать">Ссылки на видео</font>' +
'<br><div id="tochki2" class="tocka"></div>';
div.innerHTML = html;
var insPlace = document.getElementById('4');
return insPlace.parentNode.insertBefore( div, insPlace.nextSibling );
}
function getVideoObject( text )
{
if( !text )
return null;
var str = text.match(/{.*}/)[0];
str = str.replace(/([\w]+)\:([^\/]{1})/g, function(m, p1, p2){
return '"' + p1 + '":' + p2;
});
str = str.replace(/'/g, '"');
return JSON.parse( str );
}
function getFileName( doc )
{
this.fn_count = this.fn_count || 0;
++this.fn_count;
var delim = userOptions.val('delim'),
space = userOptions.val('space');
doc = doc || document;
var topContent = doc.querySelector('.topContent'),
topContentDiv, topContentLinks;
if( topContent )
{
topContentDiv = doc.querySelector('.topContent > div');
topContentLinks = doc.querySelectorAll('.topContent > a');
}else{
topContentDiv = doc.querySelector('table.content tr > td > div');
topContentLinks = doc.querySelectorAll('table.content tr > td > a');
}
var voiceId, videoId, videoIdStr,
seasonNum, episodeNum;
if( topContentDiv )
{
// ID озвучки и ID видео
voiceId = topContentDiv.querySelector('#voice').innerHTML;
videoId = topContentDiv.querySelector('#actual').innerHTML;
}
if( videoId )
{
var videoIdStr = videoId.toString(),
videoIdMatch = videoIdStr.match(/(\d+)(\D)?/);
// номер сезон и номер эпизода
seasonNum = videoIdMatch[1].slice(0, -2);
episodeNum = videoIdMatch[1].slice(-2) + (videoIdMatch[2] || '');
}
var seriesRus, episodeRus, episodeEng, voiceRus;
if( topContentLinks )
{
// название серий и название эпизода (рус.)
seriesRus = topContentLinks[1].innerText;
var lastLink = last(topContentLinks),
regExp = new RegExp('id=' + videoId);
if( videoId && lastLink && regExp.test(getLocation(lastLink.href, 'search')) )
episodeRus = lastLink.innerText;
}
var titleSeriesEng = doc.querySelector('#titleSeriesEng > span'),
titleSeries = doc.querySelector('#titleSeries');
// название эпизода (англ.)
if( titleSeriesEng )
episodeEng = titleSeriesEng.innerText;
if( !episodeRus && titleSeries )
episodeRus = titleSeries.innerText;
var voice = doc.querySelectorAll('#voice .voiceOn a');
if( voice )
{
// название озвучки (рус.)
if( voice.length == 1 )
voiceRus = voice[0].innerText;
else if( voiceId !== undefined )
{
for( var i = 0, regExp = new RegExp('voice=' + voiceId + '\\D?'); i < voice.length; ++i )
{
if( regExp.test(getLocation(voice[i].href, 'search')) )
voiceRus = voice[i].innerText;
}
}
}
var fileName = '';
fileName += seriesRus.replace(/\s+/g, space) + delim;
if( seasonNum )
fileName += 'сезон' + space + padRight(seasonNum, 2, '0') + delim;
if( episodeNum )
fileName += 'серия' + space + padRight(episodeNum, 2, '0') + delim;
if( episodeRus )
fileName += episodeRus.replace(/\s+/g, space) + delim;
else if( videoId )
fileName += 'видео' + space + videoId + delim;
else
fileName += 'видео' + space + padRight(this.fn_count, 4, '0') + delim;
if( episodeEng && userOptions.val('addEpisodeEng') )
fileName += episodeEng.replace(/\s+/g, space) + delim;
if( voiceRus )
fileName += voiceRus.replace(/\s+/g, space);
else
fileName = fileName.slice(0, -delim.length);
return fileName;
}
function handleDownloadEvent(event)
{
if( event.ctrlKey )
return;
event.preventDefault();
var fileName = this.getAttribute('data-file-name') + '.' + this.getAttribute('data-file-ext');
downloadFile( fileName, this.getAttribute('data-file-source') );
var subtitle = this.getAttribute('data-subtitle'),
subtitleExt = subtitle ? subtitle.match(/\.([^\.]+)$/)[1] : null;
if( subtitle )
GM_fileDownloader( this.getAttribute('data-file-name') + '.' + subtitleExt, subtitle );
}
function downloadFile( name, source )
{
var href = source + '#' + encodeURIComponent(name),
orig = getLocation(href, 'origin');
if( orig === window.location.origin )
fileDownloader( name, source );
else{
clog("name: ", name);
window.open( href );
//makeIFrame( name, source );
}
}
function makeIFrame( name, resource )
{
this.num = this.num || 0;
++this.num;
var iframe = document.createElement('iframe'),
body = document.body || document.getElementsByTagName('body')[0];
name = encodeURIComponent(name);
var hash = getLocation(resource, 'hash');
if( hash )
resource = resource.replace(hash, '');
iframe.src = resource + '#' + name;
iframe.id = 'iframe-' + this.num;
iframe.setAttribute('name', 'iframe-' + this.num);
iframe.setAttribute('style', 'display: none;');
body.appendChild(iframe);
}
function GM_fileDownloader( name, source )
{
GM.xmlHttpRequest({
url: source,
method: 'GET',
responseType: 'blob',
onload: function(xhr){
if( xhr.status !== 200 )
{
console.error("ERROR: status = ", xhr.status, xhr.statusText);
console.error("url: ", this.url);
return;
}
var wURL = window.webkitURL || window.URL,
resource = wURL.createObjectURL(xhr.response);
fileDownloader( name, resource );
wURL.revokeObjectURL( resource );
},
});
}
function initOptions()
{
function _setDef( v ){this.val = this.def;}
var retVal = {
data: {
'delim': {
val: null,
def: ' - ',
setDef: _setDef,
},
'space': {
val: null,
def: ' ',
setDef: _setDef,
},
'addEpisodeEng': {
val: null,
def: true,
setDef: _setDef,
},
'delay': {
val: null,
def: 500,
setDef: _setDef,
},
},
setDefs: function(){
for( var key in this.data )
this.data[key].setDef();
},
val: function( prop, v ){
if( this.data[prop] )
{
if( v !== undefined )
this.data[prop].val = v;
return this.data[prop].val;
}
return null;
},
set: function( obj ){
for( var key in obj )
this.val( key, obj[key] );
},
};
retVal.setDefs();
return retVal;
}
function getIDs( n )
{
this.ids = this.ids || ['zero1', 'zero2', 'description', 'trivia', 'parody', 'criticism', 'awards', 'censorship'];
if( n !== undefined )
return this.ids[n];
return this.ids;
}
function showDetail( id )
{
var ids, n, elm;
if( typeof id === 'number' ){
elm = document.getElementById(id);
}else{
ids = getIDs();
n = ids.indexOf(id);
elm = document.getElementById(n);
}
if( elm )
elm.style.display = 'block';
}
function showAll()
{
for( var i = 1, len = getIDs().length; i < len; ++i )
showDetail( i );
}
function padRight( t, n, fill )
{
if( !t || !n )
return t;
fill = (fill === undefined ? ' ' : fill );
t = t.toString();
while( t.length < n )
t = fill + t;
return t;
}
function getLocation( url, prop )
{
var a = document.createElement('a');
a.href = url;
return a[prop];
}
function last( arr )
{
if( arr && arr.length > 0 )
return arr[arr.length-1];
return null;
}
function indexOfAttr( arr, attr, val )
{
if( !arr || !attr || !val )
return -1;
for( var i = 0, len = arr.length, elm; i < len; ++i )
{
elm = arr[i];
if( elm[attr] == val )
return i;
}
return -1;
}
function remove( elm )
{
if( elm && elm.parentNode )
return elm.parentNode.removeChild(elm);
return elm;
}
window.addEventListener('message', recieveMessage, false);
function recieveMessage(event)
{
if( event.data && event.data.msg === ('close-iframe') && event.data.id )
{
var iframe = document.querySelector('iframe[name="' + event.data.id + '"]');
clog("close iframe: ", iframe);
setTimeout(function(){
remove(iframe);
}, userOptions.val('delay') );
}
}
})();