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での作動が確認できませんでした
ご報告致します