/* @since 2024-03-05 NicoCache_nl システムファイル. Domand用にURLインジェクションする. * 2023年11月に部分導入され2024年2月から本格導入されたの動画配信仕様(Domand,CMAF,DMS)では、 * 要求URLに動画ID(smXXX)が乗らないし各種jsonにも現れない. リファラにも乗らない. * nicocache_nlのサーバー側に伝えるために、要求APIを乗っ取ってURLに動画IDなど情報を乗せる. * searchに乗った情報はニコ動サーバーに伝わる前にDefaultRequestFilter.javaで消す. * 各onRequestではrequestHeader.getParameter("nicocachenl_XXX")の形式で値を取れる. */ /* * このコードは NicoCache_nl.watch, NicoCache_nl.watch.apiData.video.id を使うこ * とを考慮していない. 使った方が綺麗かも知れない. そのための検証をしていない. */ (function() { 'use strict'; /* * - threadId: コメントスレッドidの意味. /watch/以降に現れることがある数字の羅列id. * - videoId: sm,nm,soで始まるようなid. * - 他JSがこれを読みたい場合あるかも. */ const threadIdToVideoId = {}; /* jsonから上記連想配列を追加. */ function initThreadIdToVideoId() { /* 「お探しの動画は視聴できません」ではnull. */ const watch_data = document.getElementById("js-initial-watch-data"); if (watch_data === null) { return; }; const api_data = watch_data.getAttribute("data-api-data"); if (api_data == null) { return; /* 異常. */ }; let api_json; try { api_json = JSON.parse(api_data); } catch (e) { return; /* 異常. */ }; if (api_json === null || api_json.comment === undefined || api_json.comment === null || api_json.comment.threads === undefined || api_json.comment.threads === null || !(api_json.comment.threads instanceof Array)) { return; /* 正常 | 異常. */ }; for (const thread of api_json.comment.threads) { if (thread.id !== undefined && thread.videoId !== undefined) { threadIdToVideoId[thread.id] = thread.videoId; }; }; }; function log_bottom(s) { const div = document.createElement("div"); const br = document.createElement("br"); div.textContent = s; if (document.body != null) { document.body.appendChild(div); document.body.appendChild(br); }; }; /* * - [NicoCacheのjava側]に伝える情報を付け足したurlを返す. * - 動作非対象のurlはそのまま返す. */ function might_add_video_info(url) { /* * if (url.includes(".key")) {log_bottom(url);}; */ /* インジェクション動作対象URL. 必須なのはマスターm3u8が配信されるURL. */ if (! ['https://delivery.domand.nicovideo.jp/', '//delivery.domand.nicovideo.jp/'] .some(x => url.startsWith(x))) { return url; }; if (!(location.pathname.startsWith("/watch/"))) { return url; }; const [video_type, video_id] = (() => { let rest = location.pathname.substring("/watch/".length); if (rest.match(/^[0-9]/)) { /* - restはコメントスレッドID. */ /* - それをsmXXXへ. */ if (undefined === threadIdToVideoId[rest]) { return [null, null]; /* 失敗. 異常. */ }; rest = threadIdToVideoId[rest]; }; /* 英字部と数字部に分ける. */ const m = rest.match(/^(nm|sm|so)([0-9]+)/); if (m === null) { return [null, null]; }; return [m[1], m[2]]; })(); if (video_id === null) { return url; }; const asearch = 'nicocachenl_video_id=' + video_id + '&nicocachenl_video_type=' + video_type; if (url.includes('?')) { return url + '&' + asearch; }; return url + '?' + asearch; }; /* 関数バックアップ. */ const original_fetch = window.fetch; const fetch = function fetch(input, init) { if (input instanceof Request) { /* TODO: ニコニコ動画はfetch(Request object)形式を使っていない. * 必要になったら書く. * それまでは do nothing. */ /* console.log("fetch req url " + input.url); */ } else { /* 2024年2月時点でここにも来ないはずだが処理を書いておく */ /* console.log("fetch txt url " + input); */ input = might_add_video_info(`${input}`); }; return original_fetch(input, init); }; window.fetch = fetch; const original_open = XMLHttpRequest.prototype.open; const open = function open(method, url, ...rest) { /* * - 2024年2月時点での本命. * - ここを通ってdelivery.domand.nicovideo.jpへのリクエストは実行される. */ /* console.log(`xml url ${url}`); */ url = might_add_video_info(url); return original_open.call(this, method, url, ...rest); }; XMLHttpRequest.prototype.open = open; /* const original_Request = window.Request; const Request = function Request(url, ...rest) { * * - 2024-03-31: ニコ動はこれを使っていないはず. * - インジェクション失敗する通信がjava側に来るため追加してみる. * url = might_add_video_info(url); return original_Request(url, ...rest); }; window.Request = Request; */ if (document.readyState === 'complete') { initThreadIdToVideoId(); } else { document.addEventListener("DOMContentLoaded", initThreadIdToVideoId); }; })();