Files
develop202-migu_video/app.js
2025-12-15 20:16:37 +08:00

325 lines
8.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import http from "node:http"
import { getAndroidURL, getAndroidURL720p } from "./utils/androidURL.js";
import { readFileSync } from "./utils/fileUtil.js";
import { host, port, rateType, token, userId } from "./config.js";
import { getDateTimeStr } from "./utils/time.js";
import update from "./updateData.js";
import { printBlue, printDebug, printGreen, printGrey, printMagenta, printRed, printYellow } from "./utils/colorOut.js";
import { delay } from "./utils/fetchList.js";
// 运行时长
var hours = 0
// url缓存 降低请求频率
const urlCache = {}
let loading = false
const server = http.createServer(async (req, res) => {
while (loading) {
await delay(50)
}
loading = true
// 获取请求方法、URL 和请求头
const { method, url, headers } = req;
// printGreen("")
printMagenta("请求地址:" + url)
if (method != "GET") {
res.writeHead(200, { 'Content-Type': 'application/json;charset=UTF-8' });
res.end(JSON.stringify({
data: '请使用GET请求',
}));
printRed(`使用非GET请求:${method}`)
loading = false
return
}
// 响应接口内容
if (url == "/" || url == "/interface.txt" || url == "/m3u" || url == "/txt") {
try {
let data
// 读取文件内容
if (url == "/txt") {
data = readFileSync(process.cwd() + "/interfaceTXT.txt");
} else {
data = readFileSync(process.cwd() + "/interface.txt");
}
let replaceHost = `http://${headers.host}`
if (host != "" && (headers["x-real-ip"] || headers["x-forwarded-for"] || host.indexOf(headers.host) != -1)) {
replaceHost = host
}
data = `${data}`.replaceAll("${replace}", replaceHost);
let contentType = 'text/plain;charset=UTF-8'
if (url == "/m3u") {
// contentType = "audio/mpegurl;charset=UTF-8"
contentType = "audio/x-mpegurl; charset=utf-8"
res.setHeader('content-disposition', "inline; filename=\"interface.m3u\"");
}
// 设置响应头
res.setHeader('Content-Type', contentType);
res.statusCode = 200;
res.end(data); // 发送文件内容
loading = false
return
} catch (error) {
printRed(error)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("访问异常")
printRed("接口文件响应异常")
loading = false
return
}
}
// 回放
if (url == "/playback.xml") {
try {
// 读取文件内容
const data = readFileSync(process.cwd() + "/playback.xml");
// 设置响应头
res.setHeader('Content-Type', 'text/xml;charset=UTF-8');
res.statusCode = 200;
res.end(data); // 发送文件内容
loading = false
return
} catch (error) {
printRed(error)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("访问异常")
printRed("回放文件响应异常")
loading = false
return
}
}
let urlSplit = url.split("/")[1]
let pid = urlSplit
let params = ""
if (urlSplit.match(/\?/)) {
// 回放
printGreen("处理传入参数")
const urlSplit1 = urlSplit.split("?")
pid = urlSplit1[0]
params = urlSplit1[1]
} else {
printGrey("无参数传入")
}
if (isNaN(pid)) {
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("地址错误")
printRed("地址格式错误")
loading = false
return
}
printYellow("频道ID " + pid)
// 是否存在缓存
if (typeof urlCache[pid] === "object") {
const valTime = urlCache[pid].valTime - Date.now()
// 缓存是否有效
if (valTime >= 0) {
let playURL = urlCache[pid].url
let msg = "节目调整,暂不提供服务"
if (urlCache[pid].content != null) {
if (urlCache[pid].content.body.auth.logined) {
printGreen("登录认证成功")
if (urlCache[pid].content.body.auth.authResult == "FAIL") {
printRed(`认证失败 视频内容不完整 可能缺少相关VIP: ${urlCache[pid].content.body.auth.resultDesc}`)
}
} else {
printYellow("未登录")
}
msg = urlCache[pid].content.message
}
printGreen(`缓存有效,使用缓存数据`)
// 节目调整
if (playURL == "") {
printRed(`${pid} ${msg}`)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end(msg)
loading = false
return
}
// 添加回放参数
if (params != "") {
const resultParams = new URLSearchParams(params);
for (const [key, value] of resultParams) {
playURL = `${playURL}&${key}=${value}`
}
}
res.writeHead(302, {
'Content-Type': 'application/json;charset=UTF-8',
location: playURL
});
res.end()
loading = false
return
}
}
let resObj = {}
try {
// 未登录请求720p
if (rateType >= 3 && (userId == "" || token == "")) {
resObj = await getAndroidURL720p(pid)
} else {
resObj = await getAndroidURL(userId, token, pid, rateType)
}
} catch (error) {
printRed(error)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("链接请求出错,请稍后重试")
printRed("链接请求出错")
loading = false
return
}
printDebug(`添加加密字段后链接 ${resObj.url}`)
let changeFailed = false
if (resObj.url != "") {
let z = 1
while (z <= 6) {
if (z >= 2) {
printYellow(`获取失败,正在第${z - 1}次重试`)
}
const obj = await fetch(`${resObj.url}`, {
method: "GET",
redirect: "manual"
})
const location = obj.headers.get("Location")
if (location != "" && location != undefined && location != null) {
if (!location.startsWith("http://bofang")) {
resObj.url = location
break
}
}
if (z == 6) {
printYellow(`获取失败,返回原链接`)
changeFailed = true
} else {
await delay(150)
}
z++
}
}
if (resObj.content.body.auth.logined) {
printGreen("登录认证成功")
if (resObj.content.body.auth.authResult == "FAIL") {
printRed(`认证失败 视频内容不完整 可能缺少相关VIP: ${resObj.content.body.auth.resultDesc}`)
}
} else {
printYellow("未登录")
}
// printRed(resObj.url)
printGreen(`添加节目缓存 ${pid}`)
// 缓存有效时长
let addTime = 3 * 60 * 60 * 1000
// 节目调整时改为1分钟
if (resObj.url == "") {
addTime = 1 * 60 * 1000
}
// 尝试失败后原地址改为1小时
if (changeFailed) {
addTime = 1 * 60 * 60 * 1000
}
// 加入缓存
urlCache[pid] = {
// 有效期3小时 节目调整时改为1分钟
valTime: Date.now() + addTime,
url: resObj.url,
content: resObj.content,
}
if (resObj.url == "") {
let msg = "节目调整,暂不提供服务"
if (resObj.content != null) {
msg = resObj.content.message
}
printRed(`${pid} ${msg}`)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end(msg)
loading = false
return
}
let playURL = resObj.url
// 添加回放参数
if (params != "") {
const resultParams = new URLSearchParams(params);
for (const [key, value] of resultParams) {
playURL = `${playURL}&${key}=${value}`
}
}
printGreen("链接获取成功")
res.writeHead(302, {
'Content-Type': 'application/json;charset=UTF-8',
location: playURL
});
res.end()
loading = false
})
server.listen(port, async () => {
// 设置定时器3小时更新一次
setInterval(async () => {
printBlue(`准备更新文件 ${getDateTimeStr(new Date())}`)
hours += 3
try {
await update(hours)
} catch (error) {
printRed(error)
printRed("更新失败")
}
printBlue(`当前已运行${hours}小时`)
}, 3 * 60 * 60 * 1000);
try {
// 初始化数据
await update(hours)
} catch (error) {
printRed(error)
printRed("更新失败")
}
printGreen("每3小时更新一次")
printGreen(`本地地址: http://localhost:${port}`)
if (host != "") {
printGreen(`自定义地址: ${host}`)
}
})