AbemaTV Force HD

Force lock AbemaTV's video quality to the highest

// ==UserScript==
// @name                 AbemaTV Force HD
// @name:ja              AbemaTV 高画質のみ
// @name:zh-CN           AbemaTV 强制高画质
// @name:zh-TW           AbemaTV 強制高畫質
// @namespace            http://tampermonkey.net/
// @version              1.0.1
// @description          Force lock AbemaTV's video quality to the highest
// @description:ja       AbemaTVで再生中の動画の画質を最高画質に設定する
// @description:zh-CN    强制将AbemaTV锁定到最高画质
// @description:zh-TW    強制將AbemaTV鎖定到最高畫質
// @author               TGSAN
// @match                https://abema.tv/*
// @icon                 https://www.google.com/s2/favicons?sz=64&domain=abema.tv
// @inject-into          page
// @run-at               document-start
// @grant                unsafeWindow
// ==/UserScript==

(function () {
    'use strict';

    let windowCtx = self.window;
    if (self.unsafeWindow) {
        console.log("[AbemaTV Force HD] use unsafeWindow mode");
        windowCtx = self.unsafeWindow;
    } else {
        console.log("[AbemaTV Force HD] use window mode (your userscript extensions not support unsafeWindow)");
    }

    const originFetchAbemaTVDash = windowCtx.fetch;
    windowCtx.fetch = (...arg) => {
        let url = "";
        let isRequest = false;
        switch (typeof arg[0]) {
            case "object":
                url = arg[0].url;
                isRequest = true;
                break;
            case "string":
                url = arg[0];
                break;
            default:
                break;
        }

        if (url.indexOf('.mpd') > -1) {
            return new Promise((resolve, reject) => {
                originFetchAbemaTVDash(...arg).then(res => {
                    res.text().then(text => {
                        const parser = new DOMParser();
                        const mpdDoc = parser.parseFromString(text, "application/xml");
                        const adaptationSets = mpdDoc.getElementsByTagName("AdaptationSet");
                        for (let i = 0; i < adaptationSets.length; i++) {
                            let adaptationSet = adaptationSets[i];
                            let representations = adaptationSet.getElementsByTagName("Representation");
                            let representationsCopped = [];
                            let maxBandwidth = -1;
                            for (let repi = 0; repi < representations.length; repi++) {
                                const curBandwidth = Number.parseFloat(representations[repi].getAttribute("bandwidth"));
                                // console.log(curBandwidth);
                                if (curBandwidth >= maxBandwidth) {
                                    maxBandwidth = curBandwidth;
                                }
                                representationsCopped.push(representations[repi]);
                            }
                            for (let repi = 0; repi < representationsCopped.length; repi++) {
                                const curBandwidth = Number.parseFloat(representationsCopped[repi].getAttribute("bandwidth"));
                                if (curBandwidth != maxBandwidth) {
                                    adaptationSet.removeChild(representationsCopped[repi]);
                                }
                            }
                            // console.log("set max bitrate: " + maxBandwidth);
                        }
                        const serializer = new XMLSerializer();
                        const newBody = serializer.serializeToString(mpdDoc);
                        let body = newBody;
                        // console.log(body);
                        let newRes = new Response(body, {
                            status: res.status,
                            statusText: res.statusText,
                            headers: res.headers
                        })
                        resolve(newRes);
                    }).catch(err => {
                        reject(err);
                    });
                }).catch(err => {
                    reject(err);
                });
            });
        } else {
            if (url.indexOf('playlist.m3u8') > -1) {
                const regex = /\/\d+\/playlist\.m3u8/
                if (typeof url === 'string' && url.match(regex)) {
                    url = url.replace(regex, '/2160/playlist.m3u8');
                    if (isRequest) {
                        arg[0].url = url;
                    } else {
                        arg[0] = url;
                    }
                }
            }
            return originFetchAbemaTVDash(...arg);
        }
    }
})();