Good-MITM
Good-MITM copied to clipboard
关于youtube的广告拦截问题
尊敬的开发者您好
我将statsh的复写设置打算放入服务器上的mitm中,规则如下
#!desc =支持pip,后台播放
#!author = Maasea
#!homepage=https://whatshub.top
#!icon = https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/YouTube.png
name: YouTubequguanggao
desc: 支持pip,后台播放
http:
mitm:
- "-redirector*.googlevideo.com"
- "*.googlevideo.com"
- "www.youtube.com"
- "s.youtube.com"
- "youtubei.googleapis.com"
script:
- match: ^https?:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|search|reel\/reel_watch_sequence|guide|account\/get_setting|get_watch)
name: YouTubequguanggao1
type: response
require-body: true
binary-mode: true
timeout: 10
rewrite:
- (^https?:\/\/[\w-]+\.googlevideo\.com\/(?!dclk_video_ads).+?)&ctier=L(&.+?),ctier,(.+) $1$2$3 302
- ^https?:\/\/[\w-]+\.googlevideo\.com\/(?!(dclk_video_ads|videoplayback\?)).+&oad - reject-200
- ^https?:\/\/(www|s)\.youtube\.com\/api\/stats\/ads - reject-200
- ^https?:\/\/(www|s)\.youtube\.com\/(pagead|ptracking) - reject-200
- ^https?:\/\/s\.youtube\.com\/api\/stats\/qoe\?adcontext - reject-200
script-providers:
YouTubequguanggao1:
url: https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/dist/youtube.response.preview.js
interval: 86400
拦截工作可以进行,但是画中画(后台播放)没法实现
我参考了文档,没有发现可以插入js的效果,是否可以考虑增加实现?
谢谢,以上
以下是大模型重写后的js脚本,但是本项目目前对js对支持还非常初级,还需要改进后才能用
(() => {
const youtube = D.getInstance("YouTube");
class YouTubeHandler {
constructor(messageType, handlerName) {
this.handlerName = handlerName;
this.messageType = messageType;
this.arguments = this.decodeArguments();
youtube.isDebug = Boolean(this.arguments.debug);
youtube.debug(this.handlerName);
const config = youtube.getJSON("YouTubeAdvertiseInfo");
youtube.debug(`currentVersion: ${this.version}`);
youtube.debug(`storedVersion: ${config?.version}`);
if (config?.version === this.version) Object.assign(this, config);
}
decodeArguments() {
const defaultArgs = {
lyricLang: "zh-Hans",
captionLang: "zh-Hans",
blockUpload: true,
blockImmersive: true,
debug: false
};
return youtube.decodeParams(defaultArgs);
}
fromBinary(data) {
if (data instanceof Uint8Array) {
this.message = this.messageType.fromBinary(data);
youtube.debug(`raw: ${Math.floor(data.length / 1024)} kb`);
return this;
} else {
youtube.log("YouTube cannot get binaryBody");
youtube.exit();
return this;
}
}
async modify() {
const result = this.pure();
return result instanceof Promise ? await result : result;
}
toBinary() {
return this.message.toBinary();
}
listUnknownFields(message) {
return message instanceof E ? message.getType().runtime.bin.listUnknownFields(message) : [];
}
saveConfig() {
if (this.needSave) {
youtube.debug("Update Config");
const config = {
version: this.version,
whiteNo: this.whiteNo,
blackNo: this.blackNo,
whiteEml: this.whiteEml,
blackEml: this.blackEml
};
youtube.debug(config);
youtube.setJSON(config, "YouTubeAdvertiseInfo");
}
}
finish() {
this.saveConfig();
if (this.needProcess) {
youtube.timeStart("toBinary");
const binaryData = this.toBinary();
youtube.timeEnd("toBinary");
youtube.debug(`modify: ${Math.floor(binaryData.length / 1024)} kb`);
youtube.done({ bodyBytes: binaryData });
}
youtube.debug("use $done({})");
youtube.exit();
}
iterateObject(obj, targetKey, callback) {
const stack = typeof obj === "object" ? [obj] : [];
while (stack.length) {
const current = stack.pop();
const keys = Object.keys(current);
for (const key of keys) {
if (key === targetKey) {
callback(current, stack);
} else if (typeof current[key] === "object") {
stack.push(current[key]);
}
}
}
}
isAdvertise(message) {
const field = this.listUnknownFields(message)[0];
return field ? this.handleFieldNumber(field) : this.handleFieldEml(message);
}
handleFieldNumber(field) {
const fieldNumber = field.no;
if (this.whiteNo.includes(fieldNumber)) return false;
if (this.blackNo.includes(fieldNumber)) return true;
const isAd = this.checkBufferForAd(field);
if (isAd) this.blackNo.push(fieldNumber);
else this.whiteNo.push(fieldNumber);
this.needSave = true;
return isAd;
}
handleFieldEml(message) {
let isAd = false;
let eml = "";
this.iterateObject(message, "renderInfo", (obj, stack) => {
eml = obj.renderInfo.layoutRender.eml.split("|")[0];
if (this.whiteEml.includes(eml)) isAd = false;
else if (this.blackEml.includes(eml) || /shorts(?!_pivot_item)/.test(eml)) isAd = true;
else {
const videoContent = obj?.videoInfo?.videoContext?.videoContent;
if (videoContent) {
isAd = this.checkUnknownField(videoContent);
if (isAd) this.blackEml.push(eml);
else this.whiteEml.push(eml);
this.needSave = true;
}
}
stack.length = 0;
});
return isAd;
}
checkBufferForAd(field) {
return !field || field.data.length < 1000 ? false : this.decoder.decode(field.data).includes("pagead");
}
checkUnknownField(message) {
return message ? this.listUnknownFields(message)?.some(field => this.checkBufferForAd(field)) ?? false : false;
}
isShorts(message) {
let isShorts = false;
this.iterateObject(message, "eml", (obj, stack) => {
isShorts = /shorts(?!_pivot_item)/.test(obj.eml);
stack.length = 0;
});
return isShorts;
}
}
class BrowseHandler extends YouTubeHandler {
constructor(messageType = Mt, handlerName = "Browse") {
super(messageType, handlerName);
}
async pure() {
this.iterateObject(this.message, "sectionListSupportedRenderers", section => {
for (let i = section.sectionListSupportedRenderers.length - 1; i >= 0; i--) {
this.removeCommonAd(section, i);
this.removeShorts(section, i);
}
});
await this.translate();
return this;
}
removeCommonAd(section, index) {
const items = section.sectionListSupportedRenderers[index]?.itemSectionRenderer?.richItemContent;
for (let i = items?.length - 1; i >= 0; i--) {
if (this.isAdvertise(items[i])) {
items.splice(i, 1);
this.needProcess = true;
}
}
}
removeShorts(section, index) {
const shelf = section.sectionListSupportedRenderers[index]?.shelfRenderer;
if (this.isShorts(shelf)) {
section.sectionListSupportedRenderers.splice(index, 1);
this.needProcess = true;
}
}
getBrowseId() {
let browseId = "";
this.iterateObject(this.message?.responseContext, "key", (obj, stack) => {
if (obj.key === "browse_id") {
browseId = obj.value;
stack.length = 0;
}
});
return browseId;
}
async translate() {
const lang = this.arguments.lyricLang?.trim();
if (!(this.handlerName === "Browse" && this.getBrowseId().startsWith("MPLYt")) || lang === "off") return;
let text = "";
let target;
let hasContent = false;
this.iterateObject(this.message, "timedLyricsContent", (obj, stack) => {
target = obj.timedLyricsContent;
text = obj.timedLyricsContent.runs.map(run => run.text).join("\n");
hasContent = true;
stack.length = 0;
});
if (!hasContent) {
this.iterateObject(this.message, "description", (obj, stack) => {
target = obj.description.runs[0];
text = obj.description.runs[0].text;
stack.length = 0;
hasContent = true;
});
}
if (!hasContent) return;
const langCode = lang.split("-")[0];
const url = Yt(text, lang);
const response = await youtube.fetch({ method: "GET", url });
if (response.status === 200 && response.body) {
const data = JSON.parse(response.body);
const translatedText = data[0].map(line => line[0]).join("\r\n");
target.text = translatedText;
this.iterateObject(this.message, "footer", (obj, stack) => {
obj.footer.runs[0].text += " & Translated by Google";
stack.length = 0;
});
this.needProcess = true;
}
}
}
const handlerMap = new Map([
["browse", BrowseHandler]
]);
function getHandler(url) {
for (const [key, HandlerClass] of handlerMap.entries()) {
if (url.includes(key)) return new HandlerClass();
}
return null;
}
async function main() {
const handler = getHandler(youtube.request.url);
if (handler) {
const data = youtube.response.bodyBytes;
youtube.timeStart("fromBinary");
handler.fromBinary(data);
youtube.timeEnd("fromBinary");
youtube.timeStart("modify");
await handler.modify();
youtube.timeEnd("modify");
handler.finish();
} else {
youtube.msg("YouTube Enhance", "脚本需要更新", "外部资源 -> 全部更新");
youtube.exit();
}
}
main().catch(err => {
youtube.log(err.toString());
}).finally(() => {
youtube.exit();
});
})();
以下是大模型重写后的js脚本,但是本项目目前对js对支持还非常初级,还需要改进后才能用
(() => { const youtube = D.getInstance("YouTube");
class YouTubeHandler { constructor(messageType, handlerName) { this.handlerName = handlerName; this.messageType = messageType; this.arguments = this.decodeArguments(); youtube.isDebug = Boolean(this.arguments.debug); youtube.debug(this.handlerName); const config = youtube.getJSON("YouTubeAdvertiseInfo"); youtube.debug(`currentVersion: ${this.version}`); youtube.debug(`storedVersion: ${config?.version}`); if (config?.version === this.version) Object.assign(this, config); } decodeArguments() { const defaultArgs = { lyricLang: "zh-Hans", captionLang: "zh-Hans", blockUpload: true, blockImmersive: true, debug: false }; return youtube.decodeParams(defaultArgs); } fromBinary(data) { if (data instanceof Uint8Array) { this.message = this.messageType.fromBinary(data); youtube.debug(`raw: ${Math.floor(data.length / 1024)} kb`); return this; } else { youtube.log("YouTube cannot get binaryBody"); youtube.exit(); return this; } } async modify() { const result = this.pure(); return result instanceof Promise ? await result : result; } toBinary() { return this.message.toBinary(); } listUnknownFields(message) { return message instanceof E ? message.getType().runtime.bin.listUnknownFields(message) : []; } saveConfig() { if (this.needSave) { youtube.debug("Update Config"); const config = { version: this.version, whiteNo: this.whiteNo, blackNo: this.blackNo, whiteEml: this.whiteEml, blackEml: this.blackEml }; youtube.debug(config); youtube.setJSON(config, "YouTubeAdvertiseInfo"); } } finish() { this.saveConfig(); if (this.needProcess) { youtube.timeStart("toBinary"); const binaryData = this.toBinary(); youtube.timeEnd("toBinary"); youtube.debug(`modify: ${Math.floor(binaryData.length / 1024)} kb`); youtube.done({ bodyBytes: binaryData }); } youtube.debug("use $done({})"); youtube.exit(); } iterateObject(obj, targetKey, callback) { const stack = typeof obj === "object" ? [obj] : []; while (stack.length) { const current = stack.pop(); const keys = Object.keys(current); for (const key of keys) { if (key === targetKey) { callback(current, stack); } else if (typeof current[key] === "object") { stack.push(current[key]); } } } } isAdvertise(message) { const field = this.listUnknownFields(message)[0]; return field ? this.handleFieldNumber(field) : this.handleFieldEml(message); } handleFieldNumber(field) { const fieldNumber = field.no; if (this.whiteNo.includes(fieldNumber)) return false; if (this.blackNo.includes(fieldNumber)) return true; const isAd = this.checkBufferForAd(field); if (isAd) this.blackNo.push(fieldNumber); else this.whiteNo.push(fieldNumber); this.needSave = true; return isAd; } handleFieldEml(message) { let isAd = false; let eml = ""; this.iterateObject(message, "renderInfo", (obj, stack) => { eml = obj.renderInfo.layoutRender.eml.split("|")[0]; if (this.whiteEml.includes(eml)) isAd = false; else if (this.blackEml.includes(eml) || /shorts(?!_pivot_item)/.test(eml)) isAd = true; else { const videoContent = obj?.videoInfo?.videoContext?.videoContent; if (videoContent) { isAd = this.checkUnknownField(videoContent); if (isAd) this.blackEml.push(eml); else this.whiteEml.push(eml); this.needSave = true; } } stack.length = 0; }); return isAd; } checkBufferForAd(field) { return !field || field.data.length < 1000 ? false : this.decoder.decode(field.data).includes("pagead"); } checkUnknownField(message) { return message ? this.listUnknownFields(message)?.some(field => this.checkBufferForAd(field)) ?? false : false; } isShorts(message) { let isShorts = false; this.iterateObject(message, "eml", (obj, stack) => { isShorts = /shorts(?!_pivot_item)/.test(obj.eml); stack.length = 0; }); return isShorts; } } class BrowseHandler extends YouTubeHandler { constructor(messageType = Mt, handlerName = "Browse") { super(messageType, handlerName); } async pure() { this.iterateObject(this.message, "sectionListSupportedRenderers", section => { for (let i = section.sectionListSupportedRenderers.length - 1; i >= 0; i--) { this.removeCommonAd(section, i); this.removeShorts(section, i); } }); await this.translate(); return this; } removeCommonAd(section, index) { const items = section.sectionListSupportedRenderers[index]?.itemSectionRenderer?.richItemContent; for (let i = items?.length - 1; i >= 0; i--) { if (this.isAdvertise(items[i])) { items.splice(i, 1); this.needProcess = true; } } } removeShorts(section, index) { const shelf = section.sectionListSupportedRenderers[index]?.shelfRenderer; if (this.isShorts(shelf)) { section.sectionListSupportedRenderers.splice(index, 1); this.needProcess = true; } } getBrowseId() { let browseId = ""; this.iterateObject(this.message?.responseContext, "key", (obj, stack) => { if (obj.key === "browse_id") { browseId = obj.value; stack.length = 0; } }); return browseId; } async translate() { const lang = this.arguments.lyricLang?.trim(); if (!(this.handlerName === "Browse" && this.getBrowseId().startsWith("MPLYt")) || lang === "off") return; let text = ""; let target; let hasContent = false; this.iterateObject(this.message, "timedLyricsContent", (obj, stack) => { target = obj.timedLyricsContent; text = obj.timedLyricsContent.runs.map(run => run.text).join("\n"); hasContent = true; stack.length = 0; }); if (!hasContent) { this.iterateObject(this.message, "description", (obj, stack) => { target = obj.description.runs[0]; text = obj.description.runs[0].text; stack.length = 0; hasContent = true; }); } if (!hasContent) return; const langCode = lang.split("-")[0]; const url = Yt(text, lang); const response = await youtube.fetch({ method: "GET", url }); if (response.status === 200 && response.body) { const data = JSON.parse(response.body); const translatedText = data[0].map(line => line[0]).join("\r\n"); target.text = translatedText; this.iterateObject(this.message, "footer", (obj, stack) => { obj.footer.runs[0].text += " & Translated by Google"; stack.length = 0; }); this.needProcess = true; } } } const handlerMap = new Map([ ["browse", BrowseHandler] ]); function getHandler(url) { for (const [key, HandlerClass] of handlerMap.entries()) { if (url.includes(key)) return new HandlerClass(); } return null; } async function main() { const handler = getHandler(youtube.request.url); if (handler) { const data = youtube.response.bodyBytes; youtube.timeStart("fromBinary"); handler.fromBinary(data); youtube.timeEnd("fromBinary"); youtube.timeStart("modify"); await handler.modify(); youtube.timeEnd("modify"); handler.finish(); } else { youtube.msg("YouTube Enhance", "脚本需要更新", "外部资源 -> 全部更新"); youtube.exit(); } } main().catch(err => { youtube.log(err.toString()); }).finally(() => { youtube.exit(); });})()
感谢您的回复,希望后续有所更新,新年快乐