// ==UserScript==
// @name Plus7 VOD FFMPEG download details
// @namespace plsu7
// @description Plus7 VOD FFMPEG download detailss
// @include https://au.tv.yahoo.com/plus7/*/-/watch/*
// @version 0.1
// @noframes
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// ==/UserScript==
// partly inspired by https://gist.github.com/adammw/6484147
let videoData = unsafeWindow.videoData;
GM_registerMenuCommand("Get FFMPEG details", function(e) {
checkIfVideoDataReady();
});
function sanitizeTitle(vTitle){
//try to sanitize the title a bit
let sanitizedTitle = '';
for (let i = 0, len = vTitle.length; i < len; i++) {
sanitizedTitle +=vTitle[i].replace(/[^a-zA-Z0-9]/,"_");
}
return sanitizedTitle;
}
function checkIfVideoDataReady(){
document.querySelector('object').style.display='none';
let metaVidID = document.querySelector('meta[property="og:url"]')
.getAttribute('content').split('/watch/')[1].split('/')[0];
let vidPlayerCssID = "brightcove_player"+metaVidID+"_plyr";
if(!videoData[vidPlayerCssID] || !videoData[vidPlayerCssID].videoID){
setTimeout(checkIfVideoDataReady, 1000);
}
else{
let sanitizedTitle = sanitizeTitle(videoData[vidPlayerCssID].videoTitle);
getM3U(videoData[vidPlayerCssID].videoID, sanitizedTitle);
}
}
function createInitButton(){
let menuTab = document.createElement('li');
menuTab.setAttribute('class', 'vod_menu_tab');
let menuItem = document.createElement('a');
menuItem.setAttribute('class', 'vod_menu_item');
menuItem.setAttribute('href', '#');
menuItem.innerHTML = "FFMPEG";
menuTab.appendChild(menuItem);
let vMenu = document.querySelector('.navigation .nav0 .last ul');
if(vMenu){
vMenu.appendChild(menuTab);
//vMenu.insertBefore(menuTab, vMenu.querySelector('.last'));
menuTab.addEventListener('mouseup', function(e) {
checkIfVideoDataReady();
}, false);
}
}
function getM3U(videoID, sanitizedTitle){
GM_xmlhttpRequest({
method: 'GET',
url: 'http://c.brightcove.com/services/mobile/streaming/index/master.m3u8?videoId='+videoID,
onload: function(res) {
let xhrResp = res.responseText;
let streamURLS = [];
let availableVideoBandwidth = [];
let availableVideoResolution = [];
xhrResp.split('\n').forEach(function(item, index, arr){
let textTrim = item.trim();
//return if it's an empty string or the #EXTM3U
if(!textTrim.length || textTrim.startsWith('#EXTM3U')){
return;
}
if(textTrim.startsWith('#EXT-X-STREAM-INF')){
availableVideoBandwidth.push(textTrim.split('BANDWIDTH=')[1].split(',')[0]);
if(textTrim.indexOf('RESOLUTION=')>-1){ //sometimes there's no resolution details
availableVideoResolution.push(textTrim.split('RESOLUTION=')[1].split(',')[0]);
}
else{
availableVideoResolution.push('N/A');
}
}
if(textTrim.startsWith('https')){
streamURLS.push(item);
}
});
createModal(sanitizedTitle, streamURLS, availableVideoBandwidth, availableVideoResolution);
},
onerror: function(res) {
console.log(msg);
}
});
}
function createModal(sanitizedTitle, streamURLS, availableVideoBandwidth, availableVideoResolution){
let modalStyles = `
#gm_modalContainer {
height: 100%;
width: 100%;
overflow: auto;
margin: auto;
position: absolute;
z-index: 1000;
background: rgba(0,0,0,.5);
top: 0;
left: 0;
bottom: 0;
right: 0;
}
#gm_modal{
background-color: grey;
border: 10px solid grey;
bottom: 0;
display: flex;
flex-direction: row;
height: 360px;
left: 0;
margin: auto;
overflow: auto;
position: relative;
right: 0;
top: 150px;
width: 800px;
}
#gm_button_containers {
display: flex;
flex-direction: column;
}
#gm_textArea {
margin-left: 20px;
width: 680px;
}
#gm_closeModal {
color: black;
font-size: 5em;
height: 20px;
margin-left: 25px;
margin-top: -18px;
}
#gm_closeModal:hover {
text-decoration: none;
cursor: pointer;
}
.gm_modalButtons {
cursor: pointer;
margin-bottom: 10px;
margin-right: 10px;
padding: 5px;
text-align: left;
border: 2px solid white;
color: white;
font-size: 1.2em;
}
.gm_modalButtons:hover {
border: 2px solid blue;
}
.gm_vidDownLink{
color: white;
font-size: 2em;
margin-bottom: 10px;
text-decoration: none;
margin-right: 10px;
}`;
GM_addStyle(modalStyles);
let modalContainer = document.createElement('div');
modalContainer.setAttribute('id', 'gm_modalContainer');
let gmModal = document.createElement('div');
gmModal.setAttribute('id', 'gm_modal');
let gmButtonCon = document.createElement('div');
gmButtonCon.setAttribute('id', 'gm_button_containers');
gmModal.appendChild(gmButtonCon);
let gmcloseModal = document.createElement('a');
gmcloseModal.setAttribute('id', 'gm_closeModal');
gmcloseModal.innerHTML = '✕';
modalContainer.appendChild(gmModal);
let gmTextArea = document.createElement('textarea');
gmTextArea.setAttribute('id', 'gm_textArea');
gmModal.appendChild(gmTextArea);
//sort them, sometimes the highest bandwidth one isn't the first
let videos = {};
availableVideoBandwidth.forEach(function(item, index, arr){
videos[Number(item)] = {
bandwidth: item,
url: streamURLS[index],
resolution: availableVideoResolution[index]
}
});
let vidBandNumberArr = availableVideoBandwidth.map(function(item, index, arr){
return Number(item);
}).sort(function(a, b) {
return b - a;
});
vidBandNumberArr.forEach(function(item, index, arr){
let buttonDiv = document.createElement('div');
buttonDiv.setAttribute('class', 'gm_modalButtons');
buttonDiv.setAttribute('data-streamurl', videos[item].url);
let bandwidthDetails = document.createElement('div');
bandwidthDetails.innerHTML = 'Video Bandwidth: '+videos[item].bandwidth;
let resolutionDetails = document.createElement('div');
resolutionDetails.innerHTML = 'Resolution: '+videos[item].resolution;
buttonDiv.appendChild(bandwidthDetails);
buttonDiv.appendChild(resolutionDetails);
gmButtonCon.appendChild(buttonDiv);
buttonDiv.addEventListener('mouseup', function(e) {
let streamAtt = decodeURIComponent(e.currentTarget.getAttribute('data-streamurl'));
gmTextArea.value = 'ffmpeg -i "'+streamAtt+'" -c copy '+sanitizedTitle+'.ts';
}, false);
});
gmModal.appendChild(gmcloseModal);
gmcloseModal.addEventListener('click', function(e) {
e.preventDefault();
document.body.removeChild(modalContainer);
}, false);
document.body.appendChild(modalContainer);
}
createInitButton();
/**
Other idea of listening for object change
***/
// window.addEventListener("message", function(event){
// // console.log('received a post message====+++++++');
// // console.log('event.data');
// // console.log(event.data);
// // console.log('event.origin');
// // console.log(event.origin);
// // console.log('event.source');
// // console.log(event.source);
// let winLocation = window.location;
// let winUrlWithNoQueries = winLocation.origin+winLocation.pathname;
// let eventWinLocation = event.source.location;
// let eventWinUrlWithNoQueries = eventWinLocation.origin+eventWinLocation.pathname;
// if(event.origin === 'https://au.tv.yahoo.com' && eventWinUrlWithNoQueries === winUrlWithNoQueries){
// let messageJSON;
// try{
// messageJSON = JSON.parse(event.data);
// }
// catch(e){
// console.log('error parsing JSON in greasemonkey script ==> '+e);
// }
// console.log('messageJSON');
// console.log(messageJSON);
// if(!messageJSON || !messageJSON.videoID){
// return;
// }
// let sanitizedTitle = sanitizeTitle(messageJSON.videoTitle);
// }
// }, false);
// let insertedScript = document.createElement('script');
// insertedScript.innerHTML = `
// var metaVidID = document.querySelector('meta[property="og:url"]')
// .getAttribute('content').split('/watch/')[1].split('/')[0];
// var vidPlayerCssID = "brightcove_player"+metaVidID+"_plyr";
// //Object.observe is not yet supported in Firefox, so using Object.watch
// videoData.watch(vidPlayerCssID, function (id, oldval, newval) {
// if(id === vidPlayerCssID){
// videoData.unwatch(vidPlayerCssID);
// var message = JSON.stringify(newval);
// window.postMessage(message, "https://au.tv.yahoo.com");
// return newval;
// }
// });
// `;
// document.body.appendChild(insertedScript);