Greasy Fork

Greasy Fork is available in English.

Plus7 VOD FFMPEG download details

Plus7 VOD FFMPEG download detailss

目前为 2015-05-08 提交的版本,查看 最新版本

// ==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.2
// @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 videoElem = document.querySelector('object[id^="y7module"]');
  let vidObjectKey;

  if(videoElem){
    vidObjectKey = videoElem.id;
  }

  if(!videoElem || !videoData[vidObjectKey] || !videoData[vidObjectKey].videoID){
    setTimeout(checkIfVideoDataReady, 1000);
  }
  else{
    let sanitizedTitle = sanitizeTitle(videoData[vidObjectKey].videoTitle);
    getM3U(videoData[vidObjectKey].videoID, sanitizedTitle);
  }
}

function createInitButton(){

    let menuTab = document.createElement('li');
    menuTab.setAttribute('class', 'plus7-navigation-item');

    let menuItem = document.createElement('a');
    menuItem.setAttribute('class', 'plus7-navigation-item-link');
    menuItem.setAttribute('href', '#');
    menuItem.innerHTML = "FFMPEG";

    menuTab.appendChild(menuItem);

    let vMenu = document.querySelector('.plus7-navigation-list');

    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 = '&#10005';

    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);