// ==UserScript== // @name Viu More // @namespace http://tampermonkey.net/ // @version 0.1 // @description 显示已过期的集数,尝试提供下载功能 // @author You // @match http*://viu.tv/encore/* // @icon https://www.viu.com/ott/hk/v1/images/web_loading_icon.gif // @require https://cdn.jsdelivr.net/npm/toastify-js // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_download // @grant GM_setClipboard // @connect viu.tv // @connect now.com // @connect nowe.com // @run-at document-end // @downloadURL none // ==/UserScript== (function() { 'use strict'; let totalCount, isAsc, episodeList=[], seasonTitle, subtitles=[]; const seasonName = location.href.split('/')[4]; const cookie = 'b13b2e6a06a230f8b2'; // f7da2aac5e3df01adc const notificationAvailable = "Notification" in window; addStyle(); const msgBox = document.createElement('div'); msgBox.id = 'msg-box'; document.querySelector('#page-wrap').append(msgBox); const observer = new MutationObserver(getSeasonEposideList); observer.observe(document.querySelector('#outer-container'), { attributes: false, childList: true, subtree: false }); function getSeasonEposideList(){ if(typeof document.querySelector('#page-wrap') == "undefined"){ return; } observer.disconnect(); GM_xmlhttpRequest({ method:'GET', url: `https://api.viu.tv/production/programmes/${seasonName}`, responseType: 'json', onerror:e=>{showMsg(`请求发生错误:${e}`,0)}, onload:res=>{ res = res.response.programme; totalCount = res.programmeMeta.totalEpisodeNo; seasonTitle = res.programmeMeta.seriesTitle; if(totalCount !== res.episodes.length){ // 只显示一集,说明是最后一集 // 这里一定是总集数和显示集数不一致才会被调用的 for(let i=0;iupdateUiEpisodeList(res.episodes), 1000); } } }); } function updateUiEpisodeList(list){ // 先操作已显示的列表 const listBox = document.querySelector('.Episodes'); const shownEpisode = listBox.querySelectorAll('.VideoItem.undefined'); shownEpisode.forEach((item, index)=>{ let div = document.createElement('div'); div.className = 'floating-div'; div.innerText = '下载字幕,并复制MPD文件的url'; div.addEventListener('click', ev=>{ window.event? window.event.cancelBubble = true : ev.stopPropagation(); const productId = list[index].productId; getSubtitleWithProductId(productId, episodeList[index].episodeNum); }); item.append(div); }); // 添加因过期而未能显示的列表 let len2add, firstProductId,firstEpisodeNum; if(isAsc){ len2add = list[0].episodeNum>15?15:(list[0].episodeNum-1); firstProductId = parseInt(list[0].productId); firstEpisodeNum = list[0].episodeNum; let prevDiv; for(let i=0;i15?15:(list[list.length -1].episodeNum-1); firstProductId = parseInt(list[list.length-1].productId); firstEpisodeNum = list[list.length-1].episodeNum; for(let i=0;i{ getSubtitleWithProductId(id, num); }); return div; } function getSubtitleWithProductId(id, episodeNum){ GM_xmlhttpRequest({ method:'POST', url: 'https://api.viu.now.com/p8/3/getVodURL', headers: { 'accept':'*/*', 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62'}, data: JSON.stringify({"callerReferenceNo":getTimeStamp(new Date()),"productId":id,"contentId":id, "contentType":"Vod","mode":"prod","PIN":"password","cookie":cookie,"deviceId":"U5e83045b551442088","deviceType":"ANDROID_WEB","format":"HLS"}), onerror:e=>{showMsg('获取字幕时出错:'+e,0)}, onload:res=>{ res = JSON.parse(res.responseText); switch(res.responseCode){ case "MISSING_INPUT": showMsg('输入的参数不对',0); break; case "GEO_CHECK_FAIL": showMsg('IP不是香港的',0); break; case "INTERNAL_ERROR": showMsg("发生错误,该视频可能已经永久下架了",0); break; case "SUCCESS": GM_setClipboard(res.asset[0]); showMsg("MPD文件的url已复制成功",1); GM_xmlhttpRequest({ method:'GET', url: res.asset[0], onload:r=>{ r = r.responseXML; if(typeof r.childNodes[0].childNodes[1].childNodes !='undefined'){ r = r.childNodes[0].childNodes[1].childNodes; r.forEach(item=>{ if(item.tagName == 'AdaptationSet' && ("text/vtt" ==item.getAttribute('mimeType'))){ subtitles.push(item.getAttribute('lang')); } }); if(subtitles.length>0){ downloadSubtitles(id,episodeNum); } }else{ showMsg('该视频没有字幕',1); } } }); break; } }, ontimeout:e=>showMsg('呵呵,超时了',0) }); } function getTimeStamp(date){ const timeZone = date.getTimezoneOffset() / 60; date.setTime(date.getTime() - timeZone * 3600 * 1000); return date.toISOString().replaceAll(/[-T:Z.]/g,'').substr(0,14); } const langList = {yue:"TRD", eng:"GBR"}; const langName = {yue:"zh", eng:"en"}; function downloadSubtitles(id,episodeNum){ subtitles.forEach(item=>{ GM_download({ // https://static.viu.tv/subtitle/202104211351468/202104211351468-TRD.srt url: `https://static.viu.tv/subtitle/${id}/${id}-${langList[item]}.srt`, name:`${sn(episodeNum,2)}.${langName[item]}.srt`, onerror:e=>showMsg(`${sn(episodeNum,2)}.${langName[item]}.srt\nhttps://static.viu.tv/subtitle/${id}/${id}-${langList[item]}.srt下载失败`,0) }) }); } function showMsg(msg,type){ if(!notificationAvailable){ alert(msg); }else{ msgBox.innerText = msg; msgBox.className=type?'showing':'err'; setTimeout(()=>{msgBox.className='';}, 3000); } } function sn(num,length){ return num.toString().padStart(length, '0'); } function addStyle(){ GM_addStyle(`.floating-div{ position:relative; background: #0a7deb; text-align: center; color:white; cursor: pointer; border-radius:10px; border:solid #0a7deb 1px; padding:6px; display:none; } .VideoItem.undefined:hover .floating-div{ display:block; } #msg-box{ transition:all 0.5s ease-in-out; font-size:15px; position:fixed; right:30px; top:10px; background: #0a7deb; color:white; border-radius:7px; padding:10px; opacity:0; box-shadow:#0a7deb 2px 2px 6px, #0a7deb 6px 6px 19px; } #msg-box.showing{ opacity:1; top:130px; } #msg-box.err{ background:red; box-shadow:red 2px 2px 6px, red 6px 6px 19px; opacity:1; top:130px; } .VideoItem.outdated_episode{ text-align: center; background:#0a7deb; border-radius:10px; border:solid #0a7deb 1px ; padding:6px; margin:10px; color:white; cursor: pointer; } `); } })();