网易云课堂
登录网易云课堂,打开视频页面。视频业的链接以 https://study.163.com/
开头
1.安装 篡改猴 ,篡改猴下载看这里
2.添加新脚本

3. 输入如下内容
// ==UserScript==
// @name 有道精品课|网易云课堂 - ts下载
// @namespace http://tampermonkey.net/
// @version 2025-09-14
// @description try to take over the world!
// @author You
// @match https://live.youdao.com/*
// @match https://study.163.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=deepseek.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 交互界面 =======================================================================
function createStyles() {
const style = document.createElement('style');
style.textContent = `
.container {
background: red;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 200px;
position: fixed;
top: 0px;
right: 0px;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.user-input {
color: #28a745;
font-weight: bold;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
width: 100%;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
`;
document.head.appendChild(style);
}
// 创建页面结构
function createPage() {
// 创建容器
const container = document.createElement('div');
container.className = 'container';
// 创建输入组 - 提示信息
const messageGroup = document.createElement('div');
messageGroup.className = 'input-group';
const messageLabel = document.createElement('label');
messageLabel.textContent = '课程序号:';
messageLabel.htmlFor = 'promptMessage';
const messageInput = document.createElement('input');
messageInput.type = 'text';
messageInput.id = 'promptMessage';
messageInput.placeholder = '输入课程序号,建议使用2位';
// 创建按钮
const button = document.createElement('button');
button.id = 'demoBtn';
button.textContent = '下载';
messageGroup.appendChild(messageLabel);
messageGroup.appendChild(messageInput);
container.appendChild(messageGroup);
container.appendChild(button);
document.body.appendChild(container);
// 添加事件监听
button.addEventListener('click', function() {
window.localStorage.setItem('promptMessage', messageInput.value);
saveM3U8(window.contextM3U8)
saveKEY(window.contextKEY)
});
}
function setIntpuValue(value){
const messageInput = document.getElementById('promptMessage');
messageInput.value = value;
}
function init() {
// 创建样式
createStyles();
// 创建页面元素
const elements = createPage();
// 默认值
let promptMessageValue = window.localStorage.getItem('promptMessage');
if(!promptMessageValue){
promptMessageValue = '01';
}else{
let v = parseInt(promptMessageValue)+1;
// 不足2位,补0
if(v<10){
promptMessageValue = '0'+v;
}else{
promptMessageValue = v;
}
}
setIntpuValue(promptMessageValue);
}
//document.addEventListener('DOMContentLoaded', init);
init()
// 下载处理 =======================================================================
// https://jdvodluwytr3t.stu.126.net/nos/ept/hls/2024/05/20/669/638b5f66-39b6-4581-9f41-c744d35df7ae_e7-209.ts
// https://jdvodluwytr3t.stu.126.net/nos/ept/hls/2024/05/20/669/638b5f66-39b6-4581-9f41-c744d35df7ae_e7.m3u8?..........
window.fileName = ''// 存储文件的名称
window.tspath = '' // https://jdvodluwytr3t.stu.126.net/nos/ept/hls/2024/05/20/669/638b5f66-39b6-4581-9f41-c744d35df7ae_e7
window.contextM3U8 = null;
window.contextKEY = null;
const getFileName = function(url){
const regex = /\/([^\/?]+\.m3u8)(?=\?|$)/;
const match = url.match(regex);
const fileName = match ? match[1] : null;
const temp = url.split('.m3u8');
const tspath = temp[0];
window.tspath = tspath
//console.log('tspath:', tspath);
//window.fileName = fileName.replace('.m3u8','')
window.fileName = document.title.replace(' - 网易云课堂','')
//console.log('文件名称:', window.fileName);
}
const saveM3U8 = function(context){
const fileName = document.getElementById('promptMessage').value +"."+ window.fileName + '.m3u8';
//console.log('saveM3U8 文件名称:', fileName);
// 处理ArrayBuffer响应
try {
// 替换 638b5f66-39b6-4581-9f41-c744d35df7ae_e7-209.ts 中的 638b5f66-39b6-4581-9f41-c744d35df7ae_e7 为 https://jdvodluwytr3t.stu.126.net/nos/ept/hls/2024/05/20/669/638b5f66-39b6-4581-9f41-c744d35df7ae_e7
let con = context.responseText
let temp = window.tspath.split('/');
let regex = new RegExp(temp[temp.length - 1], 'g');
con = con.replace(regex, window.tspath)
const blob = new Blob([con], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
console.log('二进制内容已保存:', fileName);
} catch (e) {
console.error('处理二进制响应时出错:', e);
}
}
const saveKEY = function(context){
const fileName = document.getElementById('promptMessage').value +"."+ window.fileName + '.key';
//console.log('saveKEY 文件名称:', fileName);
// 处理ArrayBuffer响应
try {
const blob = new Blob([context.response], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
console.log('二进制内容已保存:', fileName);
} catch (e) {
console.error('处理二进制响应时出错:', e);
}
}
// 使用正则表达式定义要匹配的URL模式
const urlPatterns = [
/.*?\.m3u8.*?/i ,
/.*?\/key.*?/i ,
];
// 保存原始方法
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
// 重写open方法
XMLHttpRequest.prototype.open = function(method, url) {
this._method = method;
this._url = url;
return originalOpen.apply(this, arguments);
};
// 重写send方法
XMLHttpRequest.prototype.send = function(body) {
this._requestBody = body;
// 检查URL是否匹配任何模式
const shouldIntercept = urlPatterns.some(pattern => pattern.test(this._url));
if (shouldIntercept) {
console.log('拦截到匹配的请求:', this._method, this._url);
// 获取文件名称
if(this._url.indexOf('.m3u8')!= -1){
getFileName(this._url);
}
// 添加readystatechange事件监听器
this.addEventListener('readystatechange', function() {
console.log('this.readyState', this.readyState);
if (this.readyState === 4) {
console.log('请求完成:', this.status, this._url);
if(this._url.indexOf('.m3u8')!= -1){
//console.log('响应内容:', this.responseText);
//saveM3U8(this)
window.contextM3U8 = this;
}
if(this._url.indexOf('/key')!= -1){
//console.log('响应内容:', this.response);
//saveKEY(this)
window.contextKEY = this;
}
}
});
}
return originalSend.apply(this, arguments);
};
})();
4.刷新视频页面,就会提示保存 .m3u8 和 key 文件。
有道精品课
登录有道精品课,打开视频页面。视频页的链接以 https://live.youdao.com/
开头
流程同上,篡改猴的代码也是相同的。但ts文件下载需要配置请求头。在m3u8批量下载工具中配置即可。
拦截特定URL的返回内容的通用方法
方法一:拦截XMLHttpRequest(传统AJAX请求)
// ==UserScript==
// @name 拦截特定URL的返回内容
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 拦截指定URL的响应内容
// @author You
// @match *://*.example.com/* // 请修改为目标网站
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 要拦截的URL(可以是完整URL或部分匹配)
const targetUrl = 'https://api.example.com/data'; // 替换为你的目标URL
// 保存原始XMLHttpRequest.open方法
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
// 重写open方法
XMLHttpRequest.prototype.open = function(method, url) {
// 存储请求信息,供send方法使用
this._requestMethod = method;
this._requestUrl = url;
// 调用原始open方法
return originalOpen.apply(this, arguments);
};
// 重写send方法
XMLHttpRequest.prototype.send = function(body) {
// 存储请求体
this._requestBody = body;
// 如果是要拦截的URL
if (this._requestUrl && this._requestUrl.includes(targetUrl)) {
// 监听readystatechange事件
this.addEventListener('readystatechange', function() {
if (this.readyState === 4) { // 请求完成
console.log('拦截到请求:', this._requestUrl);
console.log('状态码:', this.status);
console.log('响应内容:', this.responseText);
// 在这里你可以处理响应内容
// 例如保存到变量、修改内容等
// 如果需要修改响应内容(注意:某些属性可能是只读的)
try {
// 创建一个可写的响应副本
const modifiedResponse = this.responseText;
// 对modifiedResponse进行处理...
// 如果可能,尝试修改原始响应
// Object.defineProperty(this, 'responseText', {
// value: modifiedResponse,
// writable: true
// });
} catch (e) {
console.warn('无法修改响应:', e);
}
}
});
}
// 调用原始send方法
return originalSend.apply(this, arguments);
};
})();
方法二:拦截Fetch API(现代请求方式)
// ==UserScript==
// @name 拦截Fetch请求
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 拦截指定URL的Fetch请求
// @author You
// @match *://*.example.com/* // 请修改为目标网站
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 要拦截的URL
const targetUrl = 'https://api.example.com/data'; // 替换为你的目标URL
// 保存原始fetch方法
const originalFetch = window.fetch;
// 重写fetch方法
window.fetch = function() {
const fetchArgs = arguments;
const input = arguments[0];
let url = '';
// 提取URL
if (typeof input === 'string') {
url = input;
} else if (input instanceof Request) {
url = input.url;
}
// 如果是要拦截的URL
if (url.includes(targetUrl)) {
console.log('拦截到Fetch请求:', url);
// 调用原始fetch并处理响应
return originalFetch.apply(this, arguments)
.then(response => {
// 克隆响应以便读取后还能继续使用
return response.clone().text().then(text => {
console.log('Fetch响应内容:', text);
// 在这里处理响应内容
// 你可以修改text然后创建新的响应
// 返回原始响应或修改后的响应
return response;
});
})
.catch(error => {
console.error('Fetch请求错误:', error);
throw error;
});
}
// 对于不需要拦截的请求,直接调用原始fetch
return originalFetch.apply(this, arguments);
};
})();
方法三:增强版通用拦截器
// ==UserScript==
// @name 增强版请求拦截器
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 拦截并处理特定URL的请求和响应
// @author You
// @match *://*.example.com/* // 请修改为目标网站
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_notification
// ==/UserScript==
(function() {
'use strict';
// 配置
const config = {
targetUrls: [
'https://api.example.com/data',
'https://another-api.example.com/info'
// 添加更多需要拦截的URL
],
logRequests: true,
saveResponses: false,
notifyOnIntercept: false
};
// 拦截XMLHttpRequest
if (typeof XMLHttpRequest !== 'undefined') {
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function() {
this._method = arguments[0];
this._url = arguments[1];
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
const url = this._url;
if (config.targetUrls.some(target => url.includes(target))) {
if (config.logRequests) {
console.log(`拦截到XHR请求: ${this._method} ${url}`);
}
if (config.notifyOnIntercept) {
GM_notification({
text: `拦截到请求: ${url}`,
title: '请求拦截器',
timeout: 2000
});
}
this.addEventListener('readystatechange', function() {
if (this.readyState === 4) {
console.log(`XHR响应 [${this.status}]:`, url);
console.log('响应内容:', this.responseText);
if (config.saveResponses) {
const timestamp = new Date().toISOString();
const key = `response_${timestamp}_${url.substring(url.lastIndexOf('/') + 1)}`;
GM_setValue(key, {
url: url,
status: this.status,
response: this.responseText,
timestamp: timestamp
});
}
}
});
}
return originalSend.apply(this, arguments);
};
}
// 拦截Fetch
if (typeof window.fetch !== 'undefined') {
const originalFetch = window.fetch;
window.fetch = function() {
const input = arguments[0];
let url = '';
if (typeof input === 'string') {
url = input;
} else if (input instanceof Request) {
url = input.url;
}
if (config.targetUrls.some(target => url.includes(target))) {
if (config.logRequests) {
console.log(`拦截到Fetch请求: ${url}`);
}
return originalFetch.apply(this, arguments)
.then(response => {
return response.clone().text().then(text => {
console.log(`Fetch响应 [${response.status}]:`, url);
console.log('响应内容:', text);
if (config.saveResponses) {
const timestamp = new Date().toISOString();
const key = `fetch_${timestamp}_${url.substring(url.lastIndexOf('/') + 1)}`;
GM_setValue(key, {
url: url,
status: response.status,
response: text,
timestamp: timestamp
});
}
return response;
});
});
}
return originalFetch.apply(this, arguments);
};
}
console.log('请求拦截器已启动');
})();