GYAO!の動画ダウンロードをサポートする、user.jsの配布ページです。GYAO!のサイトを使いやすくする機能も多数搭載しています。TVerとFODにも対応。
対応ブラウザ
Google Chrome
Microsoft Edge
Firefox
バージョンは、スクリプト更新時点のリリースバージョンです。これよりも古いブラウザについては、動作対象外となります。
必要な拡張機能・アドオン
Tampermonkey、またはViolentmonkeyが必要です。推奨はTampermonkeyです。
動作するページ
GYAO!の動画ページ
GYAO!のタイトル一覧ページ
GYAO!の新着ページ
TVerの動画ページ
FODの見逃し無料ページ
FODの動画ページ
機能
別のページで解説しています。
スクリプトの使い方の前に、動画の保存方法がわからないという場合はこちらをご覧ください。
スクリプト
このスクリプトは自動更新されません。更新するときは、直接コードを貼り付けてください。
// ==UserScript== // @name GYAO! Plus (Fork) // @namespace https://ssbsblg.blogspot.com/2020/10/gyao-download-userjs.html // @version 2.3.1 // @description GYAO!、Tver、FODの動画をダウンロードするコマンドを提供します。 // @author Sasabee // @match *://gyao.yahoo.co.jp/titles* // @match *://gyao.yahoo.co.jp/arrivals* // @match *://gyao.yahoo.co.jp/p/* // @match *://gyao.yahoo.co.jp/title/* // @match *://gyao.yahoo.co.jp/episode/* // @match *://gyao.yahoo.co.jp/player/* // @match *://tver.jp/episode/* // @match *://tver.jp/corner/* // @match *://tver.jp/feature/* // @match *://fod.fujitv.co.jp/s/plus7/ // @match *://fod.fujitv.co.jp/s/genre/*/ser*/*/ // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @grant GM_notification // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @run-at document-end // @noframes // ==/UserScript== const init = {}; init.command_type = 'youtube-dl'; init.ytdl_command = `youtube-dl -w --no-mtime --hls-prefer-native --fragment-retries "10" --socket-timeout "60" --user-agent "#UA#" -o "#TITLE#.mp4" "#M3U8#"`; init.ffmpeg_command = `ffmpeg -loglevel "error" -timeout "60000" -user_agent "#UA#" -i "#M3U8#" -codec "copy" "#TITLE#.mp4"`; init.user_command = '-'; init.ytdlAll = true; init.ytdlAll_commandB = `youtube-dl -w --no-mtime --hls-prefer-native --fragment-retries "10" --socket-timeout "60" --user-agent "#UA#" -f "best" -o "%%(title)s.%%(ext)s" "#URL#"`; init.ytdlAll_commandW = `youtube-dl -w --no-mtime --hls-prefer-native --fragment-retries "10" --socket-timeout "60" --user-agent "#UA#" -f "worst" -o "%%(title)s.%%(ext)s" "#URL#"`; init.sjif = false; init.first_load_count = '15'; init.default_load_count = '15'; init.load_point = '10'; init.days_left_count = '-1'; init.days_left_strings = ' 🚩'; init.modal_height = '70%'; init.modal_width = '70%'; init.resize = false; init.reverse = false; init.commandStore = []; init.m3u8Store = []; ((arr)=>{ for(let i = 0, l = arr.length; i < l; i++) { (GM_getValue(arr[i][0]) === '' || GM_getValue(arr[i][0]) === undefined) && GM_setValue(arr[i][0], arr[i][1]); } })(Object.entries(init)); const commandStore = (v)=>{ GM_getValue('commandStore',[]) === undefined && GM_setValue('commandStore', []); const arr = [].concat(GM_getValue('commandStore',[])); arr.push(v); GM_setValue('commandStore',arr); }; const m3u8Store = (v)=>{ GM_getValue('m3u8Store',[]) === undefined && GM_setValue('m3u8Store', []); const arr = [].concat(GM_getValue('m3u8Store',[])); arr.push(v); GM_setValue('m3u8Store',arr); }; const deleteStore = (name)=>{ GM_setValue(name, []); GM_notification({text: '削除しました', title: 'GYAO! Plus', timeout: 5000}); }; const checkStore = (name)=>{ if(GM_getValue(name,[]).length === 0 || !GM_getValue(name,[])) { GM_notification({text: 'ストックはありません', title: 'GYAO! Plus', timeout: 5000}); return true; } }; const mergeCopy = ()=>{ if(checkStore('commandStore')) { return; } const store = GM_getValue('commandStore',[]); const arr = []; for(let i = 0, l = store.length; i <l; i++) { arr.push(store[i].command); } GM_setClipboard(arr.join('')); GM_notification({text: 'コピーしました', title: 'GYAO! Plus', timeout: 5000}); }; const Vdata = class { constructor(type, obj=null) { this.type = type; this.code = null; this.obj = obj; this.page = 0; this.pp = GM_getValue('first_load_count'); this.drm = false; this.title = ''; } fetch(url, headers={Accept:'*/*'}) { return fetch(url, { cache: 'no-cache', credentials: 'omit', headers: headers, referrerPolicy: 'strict-origin-when-cross-origin' }); } xhr(url, headers={Accept:'*/*'}) { return new Promise((onFulfilled, onRejected)=>{ GM_xmlhttpRequest({ method: 'GET', url: url, headers: headers, onload: (response)=>{ if(response.status === 200) { return onFulfilled(response); } else { GM_notification({text: '通信エラー', title: 'GYAO! Plus', timeout: 5000}); console.log(`Error: GM_XHR | ${url}`); return onRejected(response); } } }); }); } mo() { return new Promise((onFulfilled)=>{ new MutationObserver((mutations, self)=>{ for(const mutation of mutations) { if(![mutation.target.dataset.videoId,mutation.target.dataset.account].includes(undefined)) { self.disconnect(); return onFulfilled(mutation.target.dataset); } } }).observe(document.querySelector('div'), {childList: true, subtree: true, attributes: true, attributeFilter: ['videoId','account']}); }); } async av() { const ds = await this.mo(); this.a = ds.account; this.v = ds.videoId; } pk() { const obj = { 3971130137001: '1F2YPxbuFJzWtohXjxdgDgIJcsnWacQKaAuaf0gyu8yxCQUlca9Dh7V0Uu_8Rt5JUWZTpgcqzD_IT5hRVde8JIR7r1UYR73ne8S9iLSroqTOA2P-jtl2EUw_OrSMAtenvuaXRF', 4031511847001: '2N0e6IdrmQn-kEZJ0jRi-Dlm0aUZ9mVF2lcadunJzMVYD6j_51UZzQ3mXuIeV8Zx_UUvbGeeJn73SSrpm0xD7qtiKULPP2NEsp_rgKoVxVWTNZAHN-JAHcuIpFJT7PvUj6gpZv', 4235717419001: '1O4pwi3SZ75b8DE1c2l78PZ418NByBa33h737rWv6uhPJHYkaZ6xHINTj5oOqa0-zarOEvQ6e1EqKhBcCppkAUWuo5QSKWVC4HZjY2z-Lo_ptwEK3hxfKuvZXkdNuyOM5nNSWy', 4394098882001: '1l5pA4XtMLusHj72LGzFewqKZzldpmNYTUQdoKnFL_GHhN3dg5FRnNQ5V7SOUKBl-tYFMt8CpSzuSzFAPhIHtVwmMz6F52VnMfu2UjDmeYfvvUqk0CWon46Yh-CZwIVp5vfXrZ', 4394098883001: '2XqfdZX45o9xMUoyUbUrkEjt-dMFupSdYwCw6YH7Dgd_Aj4epNSPEGgyBOFGHmLa_IPqbf8qv8CWSZaI_8Cd8xkpoMSNkyZrzzX7_TGRmVjAmZ_q_KxemVvC2gsMyfCqCzRrRx', 5102072603001: '3ZdH8iYjCnmIpuIRqzCn12gVrtpk_qOePK3J9B6h7MuqOw5T_qIqdzpLvuvb_hTvu7hs-7NsvXnPTYKd9Cgw7YiwI9kFfOOCDDEr20WDEYMjGiLptzWouXXdfE996WWM8myP3Z', 5102072605001: '0_rzsjsYbC1k1wlJLU4HiAtfzjxdUmfvvLUQB-Ax6VA-p-9wOEZbCEm3u95qq2Y1CQQW1K9tPaMma9iAqUqhpISCmyXrgnlpx9soEmoVNuQpiyGsTpePGumWxSs1YoKziYB6Wz', 5330942432001: '0kGrWxZoXJvJj5Uv6Lypjp4Nrwzz1ktDAuEbD1r_pj0oR1900CRG04FFkxo0ikc1_KmAlB4uvq_GnFwF4IsG_v9jhYOMajC9MkdVQ-QrpboS7vFV8RvK20V5v-St5WGPfXotPx', 5718741494001: '1iwEgtqPGpn00-wo0b6i9Ki0TLO2j1xrVJmqm2G5QkRf9pO95HjEdSmnbDVs0bJ1QvKSxBNgI5efql9-BDLiipQjW-GaFw4_QPFuo5SZnUocYV8vch17gYtoS9dEhkldDS8Z41', 5990430581001: '1ZUjiKsbH8c5efHSPEVRBhD7efI1cUvQC2lIT3-dzBx4quqsxvAFHz5TQl7_92OkaIaaDnd-ljUtowKtaLV5f9uQCRT55ckcoWaSFR-ffz8q5_yPCuzMorK42afx6eN0DyDqyP', 5990430582001: '3wVMX7hDjYLSlc0uxbZxE3LlwGx5OJ12TpQUZEg_ecEmVLvI9VchsSOPqpaqWCKobHHOkR08_-RjxxI0b3Gu6TYx0muJ6Fbbc2S3QiHvwP4t61xjIMs3ZEDaEkTXJxY5lOOIxd', 6191645753001: '3lNRkJh7lLX0-pEexRvKdHKVbysv2BVsiDm-Sgi2br_by23E-JvP7YviD7npaolSaQlR2yTYQChrOku8WvGhWeJ-cUEJMAJkDj6oPFeqe031ue8JWfsA0NeZ4GD0aTcwhnrplE' }; return obj[this.a]; } brightcove() { return [`https://edge.api.brightcove.com/playback/v1/accounts/${this.a}/videos/${this.v}`,{Accept:`application/json;pk=BCpkADawqM${this.pk()}`}]; } async getPl() { const json = await this.fetch(...this.brightcove()) .then(response=>response.json()) .catch(e=>{ GM_notification({text: '通信エラー', title: 'GYAO! Plus', timeout: 5000}); console.log('Error: fetch | brightcove'); console.log(e); }); if(json.sources[0].key_systems === undefined) { const master = json.sources[0].src.replace('p:','ps:'); return await this.fetch(master) .then(response=>response.text()) .catch(e=>{ GM_notification({text: '通信エラー', title: 'GYAO! Plus', timeout: 5000}); console.log('Error: fetch | master.m3u8'); console.log(e); }); } else { this.drm = true; return null; } } async player() { if(!this.title && document.querySelector('.video-jumbotron-undelivered')) { GM_notification({text: '配信待ち', title: 'GYAO! Plus', timeout: 5000}); return null; } else { this.title || (this.title = document.querySelector('h1').innerText); await this.av(); const m3u8 = await this.getPl(); return m3u8 ? this.sortPl(m3u8, location.href) : null; } } async tver() { this.title = `${document.querySelector('.title>.inner>h1').innerText} ${document.querySelector('.summary').innerText}${((a)=>a.indexOf(' ') > -1 ? ' '+/(?<= ).+/.exec(a)[0] : '')(document.querySelector('span.tv').innerText)}`; return this.player(); } async titles() { this.page += 1; this.a = '4235717419001'; const json = await this.fetch(`https://gyao.yahoo.co.jp/api/programs/${this.obj.id}/videos?page=${this.page}&serviceId=gy&perPage=${this.pp}`) .then(response=>response.json()) .catch(e=>{ GM_notification({text: '通信エラー', title: 'GYAO! Plus', timeout: 5000}); console.log('Error: fetch | API | Videos'); console.log(e); }); if(json.videos.length) { const arr = []; const fn = async(i)=>{ const obj = json.videos[i]; if(obj.streamingAvailability === 'available') { const itArr = await this.g_api(obj.id); this.v = itArr[0]; if(this.v) { const m3u8 = await this.getPl(); m3u8 && (obj.commands = this.sortPl(m3u8, itArr[2], itArr[1])); arr[i] = obj; } } }; if(json.videos.length === 1) { await fn(0); } else { const itr = []; for(let i = 0, l = json.videos.length; i < l; i++) { itr.push(fn(i)); } await Promise.all(itr); } return {ended:json.ended, videos:arr}; } else { if(1 < this.page) { return {ended:true,videos:[]}; } else { GM_notification({text: '動画が見つかりません', title: 'GYAO! Plus', timeout: 5000}); return null; } } } async arrivals() { this.a = '4235717419001'; const itArr = await this.g_api(this.obj.id); this.v = itArr[0]; this.title = itArr[1]; const m3u8 = await this.getPl(); return m3u8 ? this.sortPl(m3u8, itArr[2]) : null; } async cx() { this.title = this.obj.title; const url = `https://i.fod.fujitv.co.jp/abr/pc_html5/${this.obj.id}.m3u8`; const m3u8 = await this.fetch(url) .then(response=>response.text()) .catch(e=>{ GM_notification({text: '通信エラー', title: 'GYAO! Plus', timeout: 5000}); console.log('Error: fetch | FOD | master.m3u8'); console.log(e); }); return this.sortPl(m3u8, this.obj.url); } async g_api(vid) { const obj = await this.fetch(`https://gyao.yahoo.co.jp/apis/playback/graphql?appId=dj00aiZpPUNJeDh2cU1RazU3UCZzPWNvbnN1bWVyc2VjcmV0Jng9NTk-&query=query Playback($videoId:ID!,$logicaAgent:LogicaAgent!,$clientSpaceId:String!,$os:Os!,$device:Device!){content(parameter:{contentId:$videoId logicaAgent:$logicaAgent clientSpaceId:$clientSpaceId os:$os device:$device view:WEB}){video{id title delivery{id drm}duration images{url width height}cpId playableAge maxPixel embeddingPermission playableAgents gyaoUrl}}}&variables={"videoId":"${vid}","logicaAgent":"PC_WEB","clientSpaceId":"${document.querySelector('script[data-spaceId]').dataset.spaceid}","os":"UNKNOWN","device":"PC"}`,{Accept:'*/*','Content-Type':'application/json'}) .then(response=>response.json()) .catch(e=>{ GM_notification({text: '通信エラー', title: 'GYAO! Plus', timeout: 5000}); console.log('Error: fetch | API | GraphQL'); console.log(e); }); return obj.data.content ? [obj.data.content.video.delivery.id, obj.data.content.video.title, obj.data.content.video.gyaoUrl] : [null]; } command(arr) { const o = ['\\', '/', ':', '*', '?', '\"', '<', '>', '|', ' ']; const n = ['¥', '/', ':', '*', '?', '”', '<', '>', '|', ' ']; const fileName = (str)=>{ for(let i = 0, l = o.length; i < l; i++) { str = str.split(o[i]).join(n[i]); } return str; }; const repVar = (a)=>`${a.replace(/#TITLE#/g, fileName(arr[0])).replace(/#M3U8#/g, arr[1]).replace(/#URL#/g, arr[2]).replace(/#UA#/g, navigator.userAgent)}\r\n`; const obj = { 'youtube-dl': repVar(GM_getValue('ytdl_command')), 'ffmpeg': repVar(GM_getValue('ffmpeg_command')), 'user': repVar(GM_getValue('user_command')) } return obj[arr[3]]; } sortPl(m3u8, burl, title = this.title) { (m3u8.indexOf('#EXT-X-MEDIA:TYPE=SUBTITLES') !== -1) && (m3u8 = m3u8.replace(/#EXT-X-MEDIA:TYPE=SUBTITLES.+\n/g, '')); const urlArr = this.type === 'cx' ? m3u8.match(/https?.+\.m3u8/g) : /https?:/.test(m3u8) ? m3u8.match(/https?:\/\/.+%3D%3D/g) : m3u8.match(/\d{13}\/\d{13}_\d{13}_\d{13}\.m3u8/g).map(v=>`https://vod01-gyao.c.yimg.jp/${this.a}/${v}`); const qualityArr = m3u8.match(/(?<=#EXT-X-STREAM-INF:PROGRAM-ID=.+RESOLUTION=\d{3,4}x)\d{3,4}/g); const bandwidthArr = m3u8.match(/(?<=#EXT-X-STREAM-INF:PROGRAM-ID=.+,BANDWIDTH=)\d+/g); const arr = []; for(let i = 0, l = urlArr.length; i < l; i++) { arr.push({ url: urlArr[i], quality: `${qualityArr[i]}p`, bandwidth:`00000000${bandwidthArr[i]}`.slice(-8), command: this.command([title, urlArr[i], burl, GM_getValue('command_type')]) }); } return arr.sort((a, b)=>parseInt(b.bandwidth,10) - parseInt(a.bandwidth,10)); } get getter() { return this[this.type](); } }; const Node = class { static settings() { const temp = document.createDocumentFragment(); const div = document.createElement('div'); div.classList.add('x-settings'); const createRadio = (value, key)=>{ const label = document.createElement('label'); label.classList.add('x-radio'); label.innerText = value; const input = document.createElement('input'); input.type = 'radio'; input.value = value; input.checked = init[key] === input.value; label.addEventListener('change',()=>{ GM_setValue(key,input.value); const arr = document.querySelectorAll('.x-settings input[type="radio"]'); for(let i = 0, l = arr.length; i < l; i++) { arr[i].checked = GM_getValue(key) === arr[i].value; } }); label.insertBefore(input,label.firstChild); return label; }; const createText = (key)=>{ const currentValue = GM_getValue(key); const label = document.createElement('label'); label.classList.add('x-text'); const input = document.createElement('input'); input.type = 'text'; input.value = currentValue; label.addEventListener('change',()=>GM_setValue(key,input.value)); label.appendChild(input); return label; }; const createCheck = (key)=>{ const currentValue = GM_getValue(key); const div = document.createElement('div'); div.insertAdjacentHTML('afterbegin', currentValue ? Html.check() : Html.blank()); div.addEventListener('click',()=>{ const v = GM_getValue(key); GM_setValue(key, v ? false : true); div.firstChild.remove(); div.insertAdjacentHTML('afterbegin', v ? Html.blank() : Html.check()); }); return div; }; const createH2 = (str)=>{ const h2 = document.createElement('h2'); h2.innerText = str; return h2; }; div.appendChild(createH2('コマンドの種類')); div.appendChild(createRadio('youtube-dl', 'command_type')); div.appendChild(createRadio('ffmpeg', 'command_type')); div.appendChild(createRadio('user', 'command_type')); div.appendChild(createH2('youtube-dlコマンド(半角英数字・#M3U8# #URL# #UA# #TITLE# が使用可)')); div.appendChild(createText('ytdl_command')); div.appendChild(createH2('FFmpegコマンド(半角英数字・#M3U8# #URL# #UA# #TITLE# が使用可)')); div.appendChild(createText('ffmpeg_command')); div.appendChild(createH2('ユーザーコマンド(半角英数字・#M3U8# #URL# #UA# #TITLE# が使用可)')); div.appendChild(createText('user_command')); div.appendChild(createH2('youtube-dl一括コマンド取得メニュー表示')); div.appendChild(createCheck('ytdlAll')); div.appendChild(createH2('一括コマンド Best(半角英数字・#UA# #URL# が使用可)')); div.appendChild(createText('ytdlAll_commandB')); div.appendChild(createH2('一括コマンド Worst(半角英数字・#UA# #URL# が使用可)')); div.appendChild(createText('ytdlAll_commandW')); div.appendChild(createH2('バッチファイルの文字化け対策')); div.appendChild(createCheck('sjif')); div.appendChild(createH2('初回読み込み数(半角数字)')); div.appendChild(createText('first_load_count')); div.appendChild(createH2('基本読み込み数(半角数字)')); div.appendChild(createText('default_load_count')); div.appendChild(createH2('次を読み込むタイミング(半角数字)')); div.appendChild(createText('load_point')); div.appendChild(createH2('もうすぐ配信終了のマーク(半角数字・日数・-1で無効)')); div.appendChild(createText('days_left_count')); div.appendChild(createH2('マークの文字列')); div.appendChild(createText('days_left_strings')); div.appendChild(createH2('ウィンドウの幅(半角英数字・CSS)')); div.appendChild(createText('modal_width')); div.appendChild(createH2('ウィンドウの高さ(半角英数字・CSS)')); div.appendChild(createText('modal_height')); div.appendChild(createH2('ウィンドウのリサイズを有効にする')); div.appendChild(createCheck('resize')); div.appendChild(createH2('ストックリストの並びを逆にする')); div.appendChild(createCheck('reverse')); temp.appendChild(div); return temp; } static menu(obj, str) { const temp = document.createDocumentFragment(); const nav = document.createElement('nav'); nav.classList.add('x-menu'); nav.dataset.label = str; nav.addEventListener('mouseenter', ()=>{ nav.classList.remove('vanish'); }); const ul = document.createElement('ul'); ul.classList.add('x-list'); const arr = obj.commands; const fn = (e)=>{ const el = e.target; const wh = Math.max(el.clientWidth, el.clientHeight); const half = wh / 2; const span = document.createElement('span'); span.style = `width:${wh}px;height:${wh}px;left:${e.layerX - half}px;top:${e.layerY - half}px`; span.classList.add('x-ripple'); el.appendChild(span); }; for(let i = 0, l = arr.length; i < l; ++i) { const li = document.createElement('li'); const span = document.createElement('span'); span.classList.add('x-list-item'); span.textContent = `${arr[i].quality}${arr[i].bandwidth ? ` (${arr[i].bandwidth})` : ''}`; span.addEventListener('click', (e)=>{ fn(e); GM_setClipboard(arr[i].command); commandStore({title: obj.title, quality: arr[i].quality,command: arr[i].command}); }); arr[i].url && span.addEventListener('contextmenu', (e)=>{ e.preventDefault(); if(arr[i].url) { fn(e); GM_setClipboard(arr[i].url); m3u8Store({title: obj.title, quality: arr[i].quality, url: arr[i].url}); } }); span.addEventListener('animationend', (e)=>{ e.currentTarget.querySelector('.x-ripple').remove(); nav.classList.add('vanish'); }); li.appendChild(span); ul.appendChild(li); } nav.appendChild(ul); temp.appendChild(nav); return temp; } static modal() { const temp = document.createDocumentFragment(); const parent = document.createElement('div'); parent.classList.add('x-mask'); parent.addEventListener('click', ()=>{ document.body.removeChild(parent); }); const child = document.createElement('div'); child.style.width = GM_getValue('modal_width'); child.style.height = GM_getValue('modal_height'); if(GM_getValue('resize')) { child.style.resize = 'both'; new ResizeObserver((entries)=>{ GM_setValue('modal_width', entries[0].target.style.width); GM_setValue('modal_height', entries[0].target.style.height); }).observe(child); } child.classList.add('x-dialog'); child.addEventListener('click', (e)=>{ e.stopPropagation(); }); parent.appendChild(child); temp.appendChild(parent); return temp; } static ytdlAll(title, url) { const temp = document.createDocumentFragment(); const div = document.createElement('div'); const repVar = (a)=>`${a.replace(/#UA#/g, navigator.userAgent).replace(/#URL#/g, url)}\r\n`; div.classList.add('x-all'); div.appendChild(this.menu({title: title, commands:[ {quality: 'best', command: repVar(GM_getValue('ytdlAll_commandB'))}, {quality: 'worst', command: repVar(GM_getValue('ytdlAll_commandW'))} ]},'コマンドを取得(一括)')); temp.appendChild(div); temp.appendChild(document.createElement('hr')); return temp; } static list(arr, store) { const temp = document.createDocumentFragment(); const div = document.createElement('div'); const delcop = document.createElement('div'); delcop.classList.add('x-delcop'); const swc = document.createElement('span'); swc.addEventListener('click',()=>{ document.querySelector('.x-store-wrap').classList.toggle('x-fd-toggle'); }); swc.insertAdjacentHTML('afterbegin', Html.switch()); delcop.appendChild(swc); store === 'commandStore' && (()=>{ const cop = document.createElement('span'); cop.innerText = '全てコピー'; cop.addEventListener('click',()=>mergeCopy()); delcop.appendChild(cop); })(); const del = document.createElement('span'); del.innerText = '全て削除' del.addEventListener('click',()=>{ deleteStore(store); document.querySelector('.x-mask').remove(); }); delcop.appendChild(del); div.appendChild(delcop); const ul = document.createElement('ul'); ul.classList.add('x-store-wrap'); for(let i = 0, l = arr.length; i < l; i++) { const li = document.createElement('li'); li.classList.add('x-store-item'); const span = document.createElement('span'); span.insertAdjacentHTML('afterbegin', Html.close()); span.addEventListener('click',()=>{ GM_setValue(store, arr.filter(v=>v !== arr[i])); const dialog = document.querySelector('.x-dialog'); dialog.firstChild.remove(); dialog.appendChild(this.list(GM_getValue(store,[]), store)); }); const a = document.createElement('a'); a.innerText = `[ ${arr[i].quality} ] ${arr[i].title}`; a.title = `[ ${arr[i].quality} ] ${arr[i].title}`; a.addEventListener('click',()=>{ GM_setClipboard(store === 'commandStore' ? arr[i].command : arr[i].url); GM_notification({text: 'コピーしました', title: 'GYAO! Plus', timeout: 5000}); }); li.appendChild(span); li.appendChild(a); ul.appendChild(li); } div.appendChild(ul); temp.appendChild(div); return temp; } static article(arr, drm) { const temp = document.createDocumentFragment(); for(let i = 0, l = arr.length; i < l; i++) { const article = document.createElement('article'); const h2 = document.createElement('h2'); const a = document.createElement('a'); article.classList.add('x-article'); a.innerText = arr[i].title; a.href = arr[i].webUrl; a.target = '_blank'; const time = document.createElement('time'); time.classList.add('x-date'); time.datetime = arr[i].endDate; time.innerText = arr[i].endDateElements ? `終了: ${arr[i].endDateElements.month}月${arr[i].endDateElements.day}日(${arr[i].endDateElements.week}) ${arr[i].endDateElements.hour}:${arr[i].endDateElements.minute}${arr[i].endDateElements.countDown <= Number(GM_getValue('days_left_count','-1')) ? GM_getValue('days_left_strings',' 🚩') : ''}` : '終了: 未設定'; const sec = document.createElement('section'); sec.appendChild(time); drm || sec.appendChild(this.menu(arr[i], 'コマンドを取得')); h2.appendChild(a); article.appendChild(h2); article.appendChild(sec); temp.appendChild(article); temp.appendChild(document.createElement('hr')); } return temp; } static batchBtn() { const commands = ((arr)=>{ let str = GM_getValue('sjif') ? 'chcp 65001\r\n' : ''; for(let i = 0, l = arr.length; i < l; i++) { str += arr[i].command; } return str + 'pause'; })(GM_getValue('commandStore')); const temp = document.createDocumentFragment(); const span = document.createElement('span'); span.classList.add('x-batch','x-fi'); const a = document.createElement('a'); a.innerText = 'バッチファイルを保存'; a.download = 'Download.bat'; a.addEventListener('click', (e)=>{ const blob = new Blob([commands], {type: 'application/x-bat'}); a.href = window.URL.createObjectURL(blob); span.addEventListener('animationend',()=>{ document.querySelector('.x-batch').remove(); }); span.classList.replace('x-fi','x-fo'); }); span.appendChild(a); temp.appendChild(span); document.body.appendChild(temp); } }; const Html = class { static style() { const str = '<style>' +'@keyframes x-ripple{from{transform:scale(0);opacity:1}to{transform:scale(2);opacity:0}}' +'@keyframes x-fadein{0%{opacity:0}100%{opacity:1}}' +'@keyframes x-line{0%{left:-100%}50%{left:0}100%{left:100%}}' +'.x-mask{display:flex;justify-content:center;align-items:center;height:100%;width:100%;position:fixed;top:0;left:0;background-color:rgba(0,0,0,.5);z-index:100001}' +'.x-dialog{position:relative;color:#4C375A;display:block;padding:22px 22px 0 22px;overflow-y:auto;background-color:#EEEFE3;animation:.3s x-fadein linear}' +'.x-dialog::after{content:"";display:block;height:22px}' +'.x-all{display:flex;justify-content:flex-end}' +'.x-loading-wrap{position:absolute;top:0;left:0;z-index:1}' +'.x-loading-fixed{display:inline-block;height:4px;position:fixed;overflow:hidden}' +'.x-loading-fixed>span{display:inline-block;width:100%;height:100%;position:absolute}' +'.x-loading-base{background-color:rgba(229,0,100,.1)}' +'.x-loading-line{background-color:#E50064;animation:x-line 1s infinite}' +'.x-dialog hr{margin:2rem 0;border:none;width:100%;height:16px;position:relative;background:linear-gradient(-135deg,#DFD5E6 3px,transparent 0),linear-gradient(135deg,#DFD5E6 3px,transparent 0);background-color:transparent;background-position:left bottom;background-repeat:repeat-x;background-size:8px 8px}' +'.x-dialog hr::after{content:"";position:absolute;top:-3px;left:0;width:100%;height:100%;background:linear-gradient(-45deg,#DFD5E6 3px,transparent 0),linear-gradient(45deg,#DFD5E6 3px,transparent 0);background-color:transparent;background-position:left bottom;background-repeat:repeat-x;background-size:8px 8px}' +'.x-article{display:flex;flex-direction:column}' +'.x-article section{display:flex;justify-content:space-between;align-items:end;margin-bottom:1rem}' +'.x-article section:last-of-type{margin:0}' +'.x-article a{text-decoration:none;color:inherit}' +'.x-article a:hover{text-decoration:underline}' +'.x-article h2{font-size:1.8rem;margin-bottom:3rem}' +'.x-article h2{font-weight:600}' +'.x-date{font-size:1.4rem;background-color:#ccc}' +'.x-menu{width:200px;height:28px;line-height:28px;font-size:14px;color:#F6F7F1;position:relative}' +'.x-menu::before{content:attr(data-label);display:block;text-align:center;box-shadow:4px 4px 3px #4c375b;background-color:#8B64A5}' +'.x-menu:hover::before{box-shadow:3px 3px 2px #4c375b;background-color:#9774ae}' +'.x-list{z-index:2;position:absolute;top:28px;left:-28px;width:100%;transition:all .3s cubic-bezier(.86,0,.07,1);visibility:hidden;opacity:0}' +'.x-menu:not(.vanish):hover>.x-list{left:0px;visibility:visible;opacity:1}' +'.x-list-item{position:relative;overflow:hidden;cursor:pointer;display:block;width:100%;text-align:center;background-color:#27253C;border-bottom:1px solid #0A090F;height:28px}' +'.x-list>li:last-child>.list-item{border:none}' +'.x-list-item:hover{background-color:#14121E}' +'.x-ripple{position:absolute;border-radius:50%;animation:x-ripple .3s ease-out;background-color:rgba(255,255,255,.7)}' +'.x-last-article{display:flex;justify-content:flex-end}' +'.x-batch{position:fixed;top:24px;right:24px;font-size:24px;z-index:100001}' +'.x-batch a{display:inline-block;padding:12px;border-radius:12px;box-shadow:0px 3px 1px #4c375b;background-color:#8B64A5;color:#F6F7F1;cursor:pointer}' +'.x-batch a:hover:not(:active){background-color:#9774ae}' +'.x-batch a:active{box-shadow:0px 1px 1px #4c375b;background-color:#7d5a95}' +'@keyframes x-fi{0%{opacity:0;top:-50px}100%{opacity:1}}' +'@keyframes x-fo{0%{opacity:1}100%{opacity:0;top:-50px}}' +'.x-fi{animation:.3s x-fi ease-in}' +'.x-fo{animation:.3s x-fo ease-out}' +'@keyframes x-radio{to{transform:rotate(1turn)}}' +'.x-settings>h2{font-size:18px;font-weight:600;margin-bottom:12px;line-height:1}' +'.x-settings>h2:not(:nth-of-type(1)){margin-top:24px}' +'.x-settings>label{font-size:16px;font-weight:400}' +'.x-radio{cursor:pointer}' +'.x-settings>.x-radio:not(:nth-of-type(1)){padding-left:8px}' +'.x-radio>input{appearance:none;display:inline-block;margin-right:4px;width:16px;height:16px;padding:1px;background-clip:content-box;border:1px dashed #0D090F;background-color:#888;border-radius:50%;vertical-align:sub;cursor:pointer}' +'.x-radio>input:checked{animation:x-radio 1s ease;background-color:#E50064}' +'.x-text>input{appearance:none;border:none;font-size:16px;background-color:#F6F7F1;width:100%}' +'.x-settings svg{height:24px;width:24px;cursor:pointer;fill:#4C375A}' +`.x-store-wrap{display:flex;flex-direction:${GM_getValue('reverse')?'column-reverse':'column'}}` +`.x-fd-toggle{flex-direction:${GM_getValue('reverse')?'column':'column-reverse'}}` +'.x-store-item{margin-bottom:8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +'.x-store-item>span{display:inline-block;width:24px;height:24px;border:1px solid #bfbfbf;margin-right:18px;cursor:pointer;vertical-align:middle}' +'.x-store-item>span:active{transform:translateY(1px)}' +'.x-store-item svg{fill:#4C375A}' +'.x-store-item>a{cursor:pointer;color:#4C375A;font-size:16px;line-height:2}' +'.x-store-item>a:hover{text-decoration:underline}' +'.x-delcop{display:flex;align-content:center;column-gap:18px;margin-bottom:18px}' +'.x-delcop>span{border:1px solid #bfbfbf;cursor:pointer}' +'.x-delcop>span:first-of-type{display:flex;justify-content:center;align-items:center;width:34px;height:34px}' +'.x-delcop>span:not(:first-of-type){flex-basis:50%;font-size:16px;line-height:2;text-align:center}' +'.x-delcop>span:active{transform:translateY(1px)}' +'.x-delcop svg{height:24px;width:24px;fill:#4C375A}' +'.x-mt15{margin-top:15px}' +'.x-w296{width:296px}' +'#vd_sns>.section>:not(:first-child){margin-left:1rem}' +'div#r15{display:none}' +'</style>'; return str; } static line() { const str = `<div class="x-loading-wrap"><span class="x-loading-fixed" style="width:${GM_getValue('modal_width')}"><span class="x-loading-base"></span><span class="x-loading-line"></span></div>`; return str; } static check() { const str = '<svg xmlns="http://www.w3.org/2000/svg">' +'<path d="M0 0h24v24H0V0z" fill="none"/>' +'<path d="M21 3H3v18h18V3zM10 17l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>' +'</svg>'; return str; } static blank() { const str = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +'<path d="M0 0h24v24H0V0z" fill="none"/>' +'<path d="M19 5v14H5V5h14m2-2H3v18h18V3z"/>' +'</svg>'; return str; } static close() { const str = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +'<path d="M0 0h24v24H0V0z" fill="none"/>' +'<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>' +'</svg>'; return str; } static switch() { const str = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +'<path d="M6 7h2.5L5 3.5 1.5 7H4v10H1.5L5 20.5 8.5 17H6V7zm4-2v2h12V5H10zm0 14h12v-2H10v2zm0-6h12v-2H10v2z"/>' +'</svg>'; return str; } }; (async()=>{ const autoClick = (arr, option)=>{ for(let i = 0, l = arr.length; i < l; i++) { new IntersectionObserver((entries,self)=>{ for(const entry of entries) { entry.clientHeight === 0 && self.disconnect(); entry.intersectionRatio && entry.target.click(); } }).observe(arr[i], option); } }; const getList = async(url, globalName)=>{ document.body.appendChild(Node.modal()); const dialog = document.querySelector('.x-dialog'); dialog.insertAdjacentHTML('afterbegin', Html.line()); const ins = new Vdata('titles', { id: url.slice(url.lastIndexOf('/')+1) }); let obj = await ins.getter; ins.drm && GM_notification({text: '暗号化された動画', title: 'GYAO! Plus', timeout: 5000}); if(obj && obj.videos.length) { let loadPoint = GM_getValue('load_point'); (obj.videos.length < loadPoint) && (loadPoint = obj.videos.length); dialog.removeChild(dialog.firstChild); GM_getValue('ytdlAll') && !ins.drm && dialog.appendChild(Node.ytdlAll(globalName, url)); dialog.appendChild(Node.article(obj.videos, ins.drm)); if(obj.ended) { GM_notification({text: '読み込み完了', title: 'GYAO! Plus', timeout: 5000}); } else { new IntersectionObserver(async (entries, self)=>{ for(const entry of entries) { if(entry.intersectionRatio) { self.disconnect(); dialog.insertAdjacentHTML('afterbegin', Html.line()); ins.pp = GM_getValue('default_load_count'); obj = await ins.getter; ins.drm && GM_notification({text: '暗号化された動画', title: 'GYAO! Plus', timeout: 5000}); dialog.removeChild(dialog.firstChild); obj.videos.length && dialog.appendChild(Node.article(obj.videos, ins.drm)); if(obj.ended) { GM_notification({text: '読み込み完了', title: 'GYAO! Plus', timeout: 5000}); return; } else { self.observe(document.querySelector(`.x-dialog>.x-article:nth-last-of-type(${loadPoint})`)); } } } }).observe(document.querySelector(`.x-dialog>.x-article:nth-last-of-type(${loadPoint})`)); } } else { document.querySelector('.x-mask').remove(); } }; const g_titles = async()=>{ autoClick(document.querySelectorAll('div.more-link.program-list-more-link'), {rootMargin: "300px 0px 0px"}); document.body.addEventListener('contextmenu', async(e)=>{ if(e.target.className === 'image-lazy is-responsive' && e.target.parentNode.className === 'program-list-item-link') { e.preventDefault(); const url = e.target.parentNode.href; getList(url, e.target.offsetParent.nextElementSibling.firstChild.firstChild.innerText); } }); }; const g_arrivals = async()=>{ autoClick(document.querySelectorAll('div.more-link.video-list-more-link'), {rootMargin: "300px 0px 0px"}); document.body.addEventListener('contextmenu', async(e)=>{ if(e.target.className === 'image-lazy is-responsive' && e.target.parentNode.className === 'video-list-item-link') { e.preventDefault(); if(e.target.offsetParent.parentNode.previousSibling) { e.target.offsetParent.parentNode.previousSibling.remove(); return; } const ins = new Vdata('arrivals', { id: e.target.parentNode.parentNode.parentNode.parentNode.dataset.itemId }); const arr = await ins.getter; ins.drm && GM_notification({text: '暗号化された動画', title: 'GYAO! Plus', timeout: 5000}); if(arr && !ins.drm) { const menu = Node.menu({title: ins.title, commands: arr}, 'コマンドを取得'); e.target.offsetParent.parentNode.parentNode.insertBefore(menu, e.target.offsetParent.parentNode); e.target.offsetParent.parentNode.previousSibling.style = `margin-top:${(e.target.offsetWidth - e.target.offsetHeight) / 2}px`; } } }); }; const g_player = async()=>{ const ins = new Vdata('player'); const arr = await ins.getter; ins.drm && GM_notification({text: '暗号化された動画', title: 'GYAO! Plus', timeout: 5000}); if(arr && !ins.drm) { const menu = Node.menu({title: ins.title, commands: arr}, 'コマンドを取得'); const el = document.querySelector('#vd_sns>.section'); el.style.display = 'flex'; el.appendChild(menu); } GM_registerMenuCommand('動画リストを表示', ()=>{ const url = document.querySelector('.program-description a').href; getList(url.replace(/.+\//,''), url); }); document.body.addEventListener('contextmenu', async(e)=>{ if(e.target.className === 'image-lazy is-responsive' && e.target.parentNode.className === 'item-tile-item-thumbnail') { e.preventDefault(); const url = e.target.parentNode.parentNode.href; getList(url.slice(url.lastIndexOf('/')+1), url); } }); }; const t_player = async()=>{ const ins = new Vdata('tver'); const arr = await ins.getter; ins.drm && GM_notification({text: '暗号化された動画', title: 'GYAO! Plus', timeout: 5000}); document.querySelector('.title>.inner').appendChild(Node.menu({title: ins.title, commands: arr}, 'コマンドを取得')); document.querySelector('.x-menu').classList.add('x-mt15'); }; const f_player = async()=>{ const ins = new Vdata('cx', { id: ((a)=>a.slice(a.lastIndexOf('/')+1))(location.href.slice(0,-1)), title: document.querySelector('meta[name="fod_page_title"]').content, url: location.href }); const arr = await ins.getter; document.querySelector('ul.streaming.btnsBox').insertAdjacentHTML('beforeend', '<li style="margin-top:20px;"></li>'); document.querySelector('ul.streaming.btnsBox').lastChild.appendChild(Node.menu({title: ins.title, commands: arr}, 'コマンドを取得')); document.querySelector('.x-menu').classList.add('x-w296'); }; const f_plus7 = async()=>{ document.body.addEventListener('contextmenu', async(e)=>{ if(e.target.className === 'clGif-thum') { e.preventDefault(); if(e.target.offsetParent.previousElementSibling) { e.target.offsetParent.previousElementSibling.remove(); return; } const ins = new Vdata('cx', { id: ((a)=>a.slice(a.lastIndexOf('/')+1))(e.target.parentNode.href.slice(0,-1)), title: ((a)=>`${a[0].innerText} ${a[1].innerText}`)(e.target.nextElementSibling.children), url: e.target.parentNode.href }); const arr = await ins.getter; const menu = Node.menu({title: ins.title, commands: arr}, 'コマンドを取得'); e.target.offsetParent.parentNode.insertBefore(menu, e.target.offsetParent); e.target.offsetParent.previousSibling.style = 'margin-bottom:8px;width:184px;'; } }); }; document.head.insertAdjacentHTML('beforeend', Html.style()); switch(true) { case /tver\.jp/.test(location.host): t_player(); break; case location.host === 'gyao.yahoo.co.jp' && /^\/titles(.+?)?/.test(location.pathname): g_titles(); break; case location.host === 'gyao.yahoo.co.jp' && /^\/arrivals(.+?)?/.test(location.pathname): g_arrivals(); break; case location.host === 'gyao.yahoo.co.jp' && /^\/(title|episode|p|player)\//.test(location.pathname): g_player(); break; case location.href === 'https://fod.fujitv.co.jp/s/plus7/': f_plus7(); break; case /^\/s\/genre\//.test(location.pathname): f_player(); break; } })(); GM_registerMenuCommand('ストックしているコマンドを全てコピー', ()=>{ if(checkStore('commandStore')) { return; } mergeCopy(); }); GM_registerMenuCommand('ストックしているコマンドを表示', ()=>{ if(checkStore('commandStore')) { return; } const el = document.querySelector('.x-mask'); el && el.remove(); document.body.appendChild(Node.modal()); const dialog = document.querySelector('.x-dialog'); dialog.appendChild(Node.list(GM_getValue('commandStore',[]), 'commandStore')); }); GM_registerMenuCommand('ストックしているm3u8を表示', ()=>{ if(checkStore('m3u8Store')) { return; } const el = document.querySelector('.x-mask'); el && el.remove(); document.body.appendChild(Node.modal()); const dialog = document.querySelector('.x-dialog'); dialog.appendChild(Node.list(GM_getValue('m3u8Store',[]), 'm3u8Store')); }); GM_registerMenuCommand('バッチファイル作成', ()=>{ if(checkStore('commandStore')) { return; } Node.batchBtn(); }); GM_registerMenuCommand('設定', ()=>{ const el = document.querySelector('.x-mask'); el && el.remove(); document.body.appendChild(Node.modal()); const dialog = document.querySelector('.x-dialog'); dialog.appendChild(Node.settings()); }); GM_registerMenuCommand('ストックしているコマンドを全て削除', ()=>deleteStore('commandStore')); GM_registerMenuCommand('ストックしているm3u8を全て削除', ()=>deleteStore('m3u8Store')); GM_registerMenuCommand('旧バージョンのゴミを削除', ()=>{ const keys = Object.keys(init); const arr = GM_listValues(); let count = 0; const del = (a)=>{ GM_deleteValue(a); console.log(`旧設定値: ${a} を削除しました。`); count += 1; }; for(let i = 0, l = arr.length; i < l; i++) { const v = arr[i]; !keys.includes(v) && del(v); } GM_notification({text: count ? `削除完了(${count})` : 'ゴミはありません' , title: 'GYAO! Plus', timeout: 5000}); });
スポンサードリンク
スポンサードリンク
いつもお世話になっております
返信削除本日04/13
Tverでの作動が確認できませんでした
ご報告致します