添加体育

This commit is contained in:
test
2025-12-10 10:43:18 +08:00
parent 31f9c9ffae
commit dcd069b4c4
15 changed files with 299 additions and 1419 deletions

View File

@@ -10,7 +10,5 @@ RUN apk add --no-cache tzdata \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone
RUN npm install
CMD [ "node", "app.js" ]

View File

@@ -2,7 +2,7 @@
账号🐔了。~~只有标清..~~ 高清为主😅 gitee仓库被改私有了..
gitee经常请求超时,仓库链接已经很不稳定,有条件还是搞本地吧。。。
gitee ip被ban仓库链接已失效
访问地址(可回看当天内容)
@@ -28,15 +28,15 @@ https://gh-proxy.com/https://raw.githubusercontent.com/develop202/migu_video/ref
## 配置
配置信息如下,默认本机和局域网可用未登录720p已失效
配置信息如下,默认本机和局域网可用
| 变量名 | 默认值 | 类型 | 介绍 |
| --------- | ------ | ------ | ------------------------------------------------ |
| --------- | ------ | ------ | -------------------------------------------- |
| muserId | | string | 用户id可在网页端登录获取 |
| mtoken | | string | 用户token可在网页端登录获取 |
| mport | 1234 | number | 本地运行端口号 |
| mhost | | string | 公网/自定义访问地址 格式<http://你的ip:1234> |
| mrateType | 3 | number | 画质 2:标清 3:高清(需登录) 4:蓝光(需登录且有VIP) |
| mrateType | 3 | number | 画质 2:标清 3:高清 4:蓝光(需登录且有VIP) |
## node
@@ -49,7 +49,6 @@ https://gh-proxy.com/https://raw.githubusercontent.com/develop202/migu_video/ref
```shell
git clone git@github.com:develop202/migu_video.git
cd migu_video
npm i
```
### 运行

39
app.js
View File

@@ -68,6 +68,7 @@ const server = http.createServer(async (req, res) => {
loading = false
return
} catch (error) {
printRed(error)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("访问异常")
@@ -92,6 +93,7 @@ const server = http.createServer(async (req, res) => {
loading = false
return
} catch (error) {
printRed(error)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("访问异常")
@@ -138,10 +140,14 @@ const server = http.createServer(async (req, res) => {
let playURL = urlCache[pid].url
// 节目调整
if (playURL == "") {
printRed(`${pid} 节目调整,暂不提供服务`)
let msg = "节目调整,暂不提供服务"
if (urlCache[pid].content != null) {
msg = urlCache[pid].content.message
}
printRed(`${pid} ${msg}`)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("节目调整,暂不提供服务")
res.end(msg)
loading = false
return
}
@@ -173,6 +179,7 @@ const server = http.createServer(async (req, res) => {
resObj = await getAndroidURL(userId, token, pid, rateType)
}
} catch (error) {
printRed(error)
res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" })
res.end("链接请求出错,请稍后重试")
@@ -200,7 +207,7 @@ const server = http.createServer(async (req, res) => {
if (location == "" || location == undefined || location == null) {
continue
}
if (location.startsWith("http://hlsz")) {
if (location.startsWith("http://hlsz") || location.startsWith("http://mgsp") || location.startsWith("http://trial")) {
resObj.url = location
break
}
@@ -214,6 +221,7 @@ const server = http.createServer(async (req, res) => {
}
}
// printRed(resObj.url)
printGreen(`添加节目缓存 ${pid}`)
// 缓存有效时长
let addTime = 3 * 60 * 60 * 1000
@@ -229,15 +237,20 @@ const server = http.createServer(async (req, res) => {
urlCache[pid] = {
// 有效期3小时 节目调整时改为1分钟
valTime: Date.now() + addTime,
url: resObj.url
url: resObj.url,
content: resObj.content,
}
// console.log(resObj.url)
if (resObj.url == "") {
printRed(`${pid} 节目调整,暂不提供服务`)
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("节目调整,暂不提供服务")
res.end(msg)
loading = false
return
}
@@ -267,29 +280,31 @@ const server = http.createServer(async (req, res) => {
server.listen(port, async () => {
// 设置定时器,6小时更新一次
// 设置定时器,3小时更新一次
setInterval(async () => {
printBlue(`\n准备更新文件 ${getDateTimeStr(new Date())}\n`)
hours += 6
printBlue(`准备更新文件 ${getDateTimeStr(new Date())}`)
hours += 3
try {
await update(hours)
} catch (error) {
printRed(error)
printRed("更新失败")
console.log(error)
}
printBlue(`\n当前已运行${hours}小时`)
}, 6 * 60 * 60 * 1000);
printBlue(`当前已运行${hours}小时`)
}, 3 * 60 * 60 * 1000);
try {
// 初始化数据
await update(hours)
} catch (error) {
printRed(error)
printRed("更新失败")
console.log(error)
}
printYellow("每6小时更新一次")
printGreen("每3小时更新一次")
printGreen(`本地地址: http://localhost:${port}`)
if (host != "") {

291
package-lock.json generated
View File

@@ -4,296 +4,7 @@
"requires": true,
"packages": {
"": {
"name": "migu_video",
"dependencies": {
"axios": "^1.9.0",
"crypto-js": "^4.2.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.12.2",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.12.2.tgz",
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
"name": "migu_video"
}
}
}

View File

@@ -1,8 +1,5 @@
{
"name": "migu_video",
"type": "module",
"dependencies": {
"axios": "^1.9.0",
"crypto-js": "^4.2.0"
}
"dependencies": {}
}

View File

@@ -1,25 +1,26 @@
import { dataList } from "./utils/fetchList.js"
import { appendFile, appendFileSync, renameFileSync, writeFile } from "./utils/fileUtil.js"
import { appendFile, appendFileSync, copyFileSync, renameFileSync, writeFile } from "./utils/fileUtil.js"
import { updatePlaybackData } from "./utils/playback.js"
import { /* refreshToken as mrefreshToken, */ host, token, userId } from "./config.js"
import refreshToken from "./utils/refreshToken.js"
import { printGreen, printRed, printYellow } from "./utils/colorOut.js"
import { getDateString } from "./utils/time.js"
async function update(hours) {
/**
* @param {Number} hours -更新小时数
*/
async function updateTV(hours) {
const date = new Date()
const start = date.getTime()
let interfacePath = ""
// 获取数据
const datas = await dataList()
printGreen("数据获取成功!")
printGreen("TV数据获取成功!")
if (!(hours % 24)) {
// 必须绝对路径
interfacePath = process.cwd() + '/interface.txt.bak'
interfacePath = `${process.cwd()}/interface.txt.bak`
// 创建写入空内容
writeFile(interfacePath, "")
}
if (!(hours % 24)) {
// 每24小时刷新token
@@ -30,11 +31,11 @@ async function update(hours) {
// printGreen(`跳过token刷新`)
// }
}
appendFile(interfacePath, `#EXTM3U x-tvg-url="\${replace}/playback.xml" catchup="append" catchup-source="?playbackbegin=\${(b)yyyyMMddHHmmss}&playbackend=\${(e)yyyyMMddHHmmss}"\n`)
}
printYellow("正在更新...")
appendFile(interfacePath, `#EXTM3U x-tvg-url="\${replace}/playback.xml" catchup="append" catchup-source="?playbackbegin=\${(b)yyyyMMddHHmmss}&playbackend=\${(e)yyyyMMddHHmmss}"\n`)
printYellow("开始更新TV...")
// 回放
const playbackFile = process.cwd() + '/playback.xml.bak'
const playbackFile = `${process.cwd()}/playback.xml.bak`
writeFile(playbackFile,
`<?xml version="1.0" encoding="UTF-8"?>\n` +
`<tv generator-info-name="Tak" generator-info-url="${host}">\n`)
@@ -48,28 +49,112 @@ async function update(hours) {
await updatePlaybackData(data[j], playbackFile)
if (!(hours % 24)) {
// 写入节目
appendFile(interfacePath, `#EXTINF:-1 tvg-id="${data[j].name}" tvg-name="${data[j].name}" tvg-logo="${data[j].pics.highResolutionH}" group-title="${datas[i].name}",${data[j].name}\n\${replace}/${data[j].pID}\n`)
// printGreen(` 节目链接更新成功`)
}
}
if (!(hours % 24)) {
printGreen(`分类###:${datas[i].name} 更新完成!`)
}
}
appendFileSync(playbackFile, `</tv>\n`)
// 重命名
renameFileSync(playbackFile, playbackFile.replace(".bak", ""))
if (!(hours % 24)) {
renameFileSync(interfacePath, interfacePath.replace(".bak", ""))
}
printYellow("更新完成!")
printGreen("TV更新完成")
const end = Date.now()
printYellow(`本次耗时: ${(end - start) / 1000}`)
printYellow(`TV更新耗时: ${(end - start) / 1000}`)
}
/**
* @param {Number} hours -更新小时数
*/
async function updatePE(hours) {
const date = new Date()
const start = date.getTime()
// 获取PE数据
const datas = await fetch("http://v0-sc.miguvideo.com/vms-match/v6/staticcache/basic/match-list/normal-match-list/0/all/default/1/miguvideo").then(r => r.json())
printGreen("PE数据获取成功")
// console.dir(datas, { depth: null })
copyFileSync(`${process.cwd()}/interface.txt`, `${process.cwd()}/interface.txt.bak`, 0)
const interfacePath = `${process.cwd()}/interface.txt.bak`
printYellow("开始更新PE...")
for (let i = 1; i < 4; i++) {
// 日期
const date = datas.body?.days[i]
for (const data of datas.body?.matchList[date]) {
let relativeDate = "昨天"
const dateString = getDateString(new Date())
if (date == dateString) {
relativeDate = "今天"
} else if (parseInt(date.substring(6)) > parseInt(dateString.substring(6))) {
relativeDate = "明天"
}
let pkInfoTitle = data.pkInfoTitle
if (data.confrontTeams) {
pkInfoTitle = `${data.confrontTeams[0].name}VS${data.confrontTeams[1].name}`
}
// const peResult = await fetch(`http://app-sc.miguvideo.com/vms-match/v5/staticcache/basic/all-view-list/${data.mgdbId}/2/miguvideo`).then(r => r.json())
const peResult = await fetch(`https://vms-sc.miguvideo.com/vms-match/v6/staticcache/basic/basic-data/${data.mgdbId}/miguvideo`).then(r => r.json())
try {
// 比赛已结束
if (peResult.body.endTime < Date.now()) {
const replayResult = await fetch(`http://app-sc.miguvideo.com/vms-match/v5/staticcache/basic/all-view-list/${data.mgdbId}/2/miguvideo`).then(r => r.json())
let replayList = replayResult.body?.replayList
if (replayList == null || replayList == undefined) {
replayList = peResult.body.multiPlayList.replayList
}
if (replayList == null || replayList == undefined) {
printYellow(`${data.mgdbId} ${pkInfoTitle} 无回放`)
continue
}
for (const replay of replayList) {
if (replay.name.match(/.*集锦|训练.*/) != null) {
continue
}
if (replay.name.match(/.*回放|赛.*/) != null) {
// 写入赛事
appendFileSync(interfacePath, `#EXTINF:-1 tvg-id="${pkInfoTitle}" tvg-name="${pkInfoTitle}" tvg-logo="${data.competitionLogo}" group-title="体育-${relativeDate}",${data.competitionName} ${pkInfoTitle} ${replay.name} ${peResult.body.multiPlayList.preList[peResult.body.multiPlayList.preList.length - 1].startTimeStr.substring(11, 16)}\n\${replace}/${replay.pID}\n`)
}
}
continue
}
// 比赛未结束
const liveList = peResult.body.multiPlayList.liveList
for (const live of liveList) {
if (live.name.match(/.*集锦.*/) != null || live.startTimeStr == undefined) {
continue
}
// 写入赛事
appendFileSync(interfacePath, `#EXTINF:-1 tvg-id="${pkInfoTitle}" tvg-name="${pkInfoTitle}" tvg-logo="${data.competitionLogo}" group-title="体育-${relativeDate}",${data.competitionName} ${pkInfoTitle} ${live.name} ${live.startTimeStr.substring(11, 16)}\n\${replace}/${live.pID}\n`)
}
} catch (error) {
printRed(`${data.mgdbId} ${pkInfoTitle} 更新失败`)
printRed(error)
}
}
printGreen(`日期 ${date} 更新完成!`)
}
// 重命名
renameFileSync(interfacePath, interfacePath.replace(".bak", ""))
printGreen("PE更新完成")
const end = Date.now()
printYellow(`PE更新耗时: ${(end - start) / 1000}`)
}
/**
* @param {Number} hours - 更新小时数
*/
async function update(hours) {
await updateTV(hours)
await updatePE(hours)
}
export default update

View File

@@ -1,12 +1,15 @@
import crypto from 'crypto'
import CryptoJS from 'crypto-js';
const KEY_AES = "MQDUjI19MGe3BhaqTlpc9g==";
const IV = "abcdefghijklmnop";
const RSA_PRIVATE_KEY_PKCS8 = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOhvWsrglBpQGpjB\r8okxLUCaaiKKOytn9EtvytB5tKDchmgkSaXpreWcDy/9imsuOiVCSdBr6hHjrTN7\rQKkA4/QYS8ptiFv1ap61PiAyRFDI1b8wp2haJ6HF1rDShG2XdfWIhLk4Hj6efVZA\rSfa3taM7C8NseWoWh05Cp26g4hXZAgMBAAECgYBzqZXghsisH1hc04ZBRrth/nT6\rIxc2jlA+ia6+9xEvSw2HHSeY7COgsnvMQbpzg1lj2QyqLkkYBdfWWmrerpa/mb7j\rm6w95YKs5Ndii8NhFWvC0eGK8Ygt02DeLohmkQu3B+Yq8JszjB7tQJRR2kdG6cPt\rKp99ZTyyPom/9uD+AQJBAPxCwajHAkCuH4+aKdZhH6n7oDAxZoMH/mihDRxHZJof\rnT+K662QCCIx0kVCl64s/wZ4YMYbP8/PWDvLMNNWC7ECQQDr4V23KRT9fAPAN8vB\rq2NqjLAmEx+tVnd4maJ16Xjy5Q4PSRiAXYLSr9uGtneSPP2fd/tja0IyawlP5UPL\rl76pAkAeXqMWAK+CvfPKxBKZXqQDQOnuI2RmDgZQ7mK3rtirvXae+ciZ4qc4Bqt7\r7yJ3s68YRlHQR+OMzzeeKz47kzZhAkAPteH1ChJw06q4Sb8TdiPX++jbkFiCxgiN\rCsaMTfGVU/Y8xGSSYCgPelEHxu1t2wwVa/tdYs505zYmkSGT1NaJAkBCS5hymXsA\rB92Fx8eGW5WpLfnpvxl8nOcP+eNXobi8Sc6q1FmoHi8snbcmBhidcDdcieKn+DbX\rGG3BQE/OCOkM\r";
// MD5标准加密
/**
* MD5 加密
* @param {string} str -
* @returns {string} -
*/
function getStringMD5(str) {
// 创建 MD5 哈希对象
const md5 = crypto.createHash("md5");
@@ -17,100 +20,98 @@ function getStringMD5(str) {
}
/**
* base64加密
* base64 加密
* @param {string} str -
* @returns {string} -
*/
function Base64encrypt(str) {
// create a buffer
const buff = Buffer.from(str, 'utf-8');
// encode buffer as Base64
return buff.toString('base64')
}
/**
* base64解密
* base64 解密
* @param {string} str -
* @returns {string} -
*/
function Base64decrypt(str) {
// create a buffer
const buff = Buffer.from(str, 'base64');
// encode buffer as Base64
return buff.toString('utf-8')
}
// AES 解密方法
function AESdecrypt(encryptedText, base64Key) {
// 解码 Base64 密钥和密文
const keyBytes = CryptoJS.enc.Base64.parse(base64Key);
const encryptedBytes = CryptoJS.enc.Base64.parse(encryptedText);
/**
* AES 加密
* @param {string} data -
* @param {string} baseKey -
* @param {string} ivStr -
* @returns {string} -
*/
function AESencrypt(data, baseKey = KEY_AES, ivStr = IV) {
let key = Buffer.from(baseKey, "utf8")
let iv = Buffer.from(ivStr, "utf8")
// 处理 IV
const iv = CryptoJS.enc.Utf8.parse(IV);
// 配置解密参数
const decryptOptions = {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
// 执行 AES 解密
const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: encryptedBytes },
keyBytes,
decryptOptions
);
// 将解密结果转为 UTF-8 字符串
return decrypted.toString(CryptoJS.enc.Utf8);
}
// AES 加密方法
function AESencrypt(str, base64Key) {
// 解码 Base64 密钥
const keyBytes = CryptoJS.enc.Base64.parse(base64Key);
// 处理 IV 和明文
const iv = CryptoJS.enc.Utf8.parse(IV);
const plainBytes = CryptoJS.enc.Utf8.parse(str);
// 配置加密参数
const encryptOptions = {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
// 执行 AES 加密
const encrypted = CryptoJS.AES.encrypt(
plainBytes,
keyBytes,
encryptOptions
);
// 返回 Base64 编码的密文
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
// 填充长度
if (key.length < 32) {
const paddedKey = Buffer.alloc(32);
key.copy(paddedKey);
key = paddedKey;
}
if (iv.length < 16) {
const paddedIV = Buffer.alloc(16);
iv.copy(paddedIV);
iv = paddedIV;
} const cipher = crypto.createCipheriv("aes-256-cbc", key, iv)
const dest = cipher.update(data, "utf8", "base64") + cipher.final("base64")
return dest.toString()
}
/**
*RSA 加
* AES 解
* @param {string} baseData -
* @param {string} baseKey -
* @param {string} ivStr -
* @returns {string} -
*/
function RSAencrypt(str) {
// 处理密钥:移除 \r 并解码 Base64
const keyBytes = Buffer.from(RSA_PRIVATE_KEY_PKCS8.replace(/\r/g, ''), 'base64');
function AESdecrypt(baseData, baseKey = KEY_AES, ivStr = IV) {
let key = Buffer.from(baseKey, "utf8")
let iv = Buffer.from(ivStr, "utf8")
// 创建私钥对象
const privateKey = crypto.createPrivateKey({
key: keyBytes,
format: 'der',
type: 'pkcs8'
});
// 使用私钥加密
const encrypted = crypto.privateEncrypt(
{ key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING },
Buffer.from(str)
);
return encrypted.toString('base64');
// 填充长度
if (key.length < 32) {
const paddedKey = Buffer.alloc(32);
key.copy(paddedKey);
key = paddedKey;
} if (iv.length < 16) {
const paddedIV = Buffer.alloc(16);
iv.copy(paddedIV);
iv = paddedIV;
}
const data = Buffer.from(baseData, "utf8")
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv)
const dest = Buffer.concat([decipher.update(data), decipher.final()])
return dest.toString()
}
export { getStringMD5, KEY_AES, AESdecrypt, AESencrypt, Base64decrypt, Base64encrypt, RSAencrypt }
/**
* RSA 私钥签名加密
* @param {string} data -
* @param {string} publicKeyBase -
* @returns {string} -
*/
function RSAencrypt(data, publicKeyBase = RSA_PRIVATE_KEY_PKCS8) {
const clearKey = publicKeyBase.replace(/\r/g, "")
const keyBytes = Buffer.from(clearKey, "base64")
const privateKey = crypto.createPrivateKey({
key: keyBytes,
format: "der",
type: "pkcs8"
})
const dest = crypto.privateEncrypt({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING,
}, data)
return dest.toString("base64")
}
export { getStringMD5, AESdecrypt, AESencrypt, Base64decrypt, Base64encrypt, RSAencrypt }

View File

@@ -1,9 +1,17 @@
import axios from "axios";
import { getStringMD5 } from "./EncryUtils.js";
import { getddCalcuURL, getddCalcuURL720p, getEncryptURL } from "./ddCalcuURL.js";
import { changedDdCalcu } from "./datas.js";
import { getddCalcuURL, getddCalcuURL720p } from "./ddCalcuURL.js";
import { printYellow } from "./colorOut.js";
/**
* @typedef {object} SaltSign
* @property {string} salt 盐值
* @property {string} sign 签名
*/
/**
* @param {string} md5 - md5字符串
* @returns {SaltSign} -
*/
function getSaltAndSign(md5) {
const salt = 1230024
@@ -15,134 +23,20 @@ function getSaltAndSign(md5) {
}
}
function replaceChars(url, pid, rateType) {
// 参数为空或者不符条件
if (!url || rateType <= 1 || rateType >= 5 || !pid) {
return ""
}
const spl = url.split("&ddCalcu=")
const prefix = spl[0]
let suffix = spl[1]
suffix = suffix.replace("sv=10000&ct=www", "sv=10004&ct=android")
// 默认替换方式
let defaultChange = ["x", "a", "y", "a"]
let index = [5, 8, 11, 14]
// 一些标清不需要改
let noChangeStandard = false
// 替换自定义替换方式
if (changedDdCalcu[pid] != undefined) {
noChangeStandard = changedDdCalcu[pid].noChangeStandard
if (changedDdCalcu[pid]["all"] != undefined) {
defaultChange = changedDdCalcu[pid]["all"].data
index = changedDdCalcu[pid]["all"].index
}
if (changedDdCalcu[pid][rateType]) {
// 若相邻两个重复可以写另一个的rateType
if (!isNaN(changedDdCalcu[pid][rateType])) {
const rate = changedDdCalcu[pid][rateType]
defaultChange = changedDdCalcu[pid][rate].data
index = changedDdCalcu[pid][rate].index
} else {
defaultChange = changedDdCalcu[pid][rateType].data
index = changedDdCalcu[pid][rateType].index
}
}
}
// 一些标清需要改
if (rateType == 2 && !noChangeStandard) {
defaultChange[0] = "v"
}
// 替换
for (let i = 0; i < index.length; i++) {
suffix[index[i] - 1] = defaultChange[i]
}
return `${prefix}&ddCalcu=${suffix}`
}
async function getAndroidVideoURL(userId, token, exports, pid, rateType) {
if (rateType <= 1) {
return {
url: "",
rateType: 0
}
}
if (!exports) {
return {
url: "",
rateType: 0
}
}
// 获取url
const timestramp = Date.now()
const appVersion = "26000370"
let headers = {
AppVersion: 2600037000,
TerminalId: "android",
"X-UP-CLIENT-CHANNEL-ID": "2600037000-99000-200300220100002"
}
if (rateType != 2) {
headers.UserId = userId
headers.UserToken = token
}
// console.log(headers)
const str = timestramp + pid + appVersion
const md5 = getStringMD5(str)
const result = getSaltAndSign(md5)
// 请求
const baseURL = "https://play.miguvideo.com/playurl/v1/play/playurl"
const params = "?sign=" + result.sign + "&rateType=" + rateType
+ "&contId=" + pid + "&timestamp=" + timestramp + "&salt=" + result.salt
const respData = await axios.get(baseURL + params, {
headers: headers
}).then(r => r.data)
// console.log(respData)
const url = respData.body.urlInfo?.url
// console.log(rateType)
// console.log(url)
if (!url) {
return {
url: "",
rateType: 0
}
}
rateType = parseInt(respData.body.urlInfo?.rateType)
// 将URL加密
const encryURL = getEncryptURL(exports, url)
// console.log("加密后:" + encryURL)
// 替换字符,拼接结果
const resURL = replaceChars(encryURL, pid, rateType)
// console.log("app替换后的链接" + resURL)
// console.log("播放画质:" + rateType)
return {
url: resURL,
rateType: rateType
}
}
/**
* @param {string} userId - 用户ID
* @param {string} token - 用户token
* @param {string} pid - 节目ID
* @param {number} rateType - 清晰度
* @returns {} - url: 链接 rateType: 清晰度
* @returns {object} -
*/
async function getAndroidURL(userId, token, pid, rateType) {
if (rateType <= 1) {
return {
url: "",
rateType: 0
rateType: 0,
content: null
}
}
// 获取url
@@ -172,18 +66,18 @@ async function getAndroidURL(userId, token, pid, rateType) {
const baseURL = "https://play.miguvideo.com/playurl/v1/play/playurl"
let params = "?sign=" + result.sign + "&rateType=" + rateType
+ "&contId=" + pid + "&timestamp=" + timestramp + "&salt=" + result.salt
let respData = await axios.get(baseURL + params, {
let respData = await fetch(baseURL + params, {
headers: headers
}).then(r => r.data)
}).then(r => r.json())
if (respData.rid == 'TIPS_NEED_MEMBER') {
printYellow("该账号没有会员 正在降低画质")
params = "?sign=" + result.sign + "&rateType=" + (rateType - 1)
+ "&contId=" + pid + "&timestamp=" + timestramp + "&salt=" + result.salt
respData = await axios.get(baseURL + params, {
respData = await fetch(baseURL + params, {
headers: headers
}).then(r => r.data)
}).then(r => r.json())
}
// console.dir(respData, { depth: null })
@@ -194,7 +88,8 @@ async function getAndroidURL(userId, token, pid, rateType) {
if (!url) {
return {
url: "",
rateType: 0
rateType: 0,
content: respData
}
}
@@ -216,13 +111,13 @@ async function getAndroidURL(userId, token, pid, rateType) {
/**
* 旧版高清画质
* @param {string} pid - 节目ID
* @returns {} - url: 链接 rateType: 清晰度
* @returns {object} -
*/
async function getAndroidURL720p(pid) {
// 获取url
const timestramp = Math.round(Date.now()).toString()
const appVersion = "2600033500"
const appVersionID = appVersion + "-99000-201600010010027"
const appVersion = "2600034600"
const appVersionID = appVersion + "-99000-201600010010028"
let headers = {
AppVersion: `${appVersion}`,
TerminalId: "android",
@@ -232,8 +127,8 @@ async function getAndroidURL720p(pid) {
const str = timestramp + pid + appVersion.substring(0, 8)
const md5 = getStringMD5(str)
const salt = String( Math.floor(Math.random() * 1000000) ).padStart(6, '0') + '80'
const suffix = "16d4328df21a4138859388418bd252c2migu" + salt.substring(0, 4)
const salt = String(Math.floor(Math.random() * 1000000)).padStart(6, '0') + '25'
const suffix = "2cac4f2c6c3346a5b34e085725ef7e33migu" + salt.substring(0, 4)
const sign = getStringMD5(md5 + suffix)
let rateType = 3
@@ -245,18 +140,19 @@ async function getAndroidURL720p(pid) {
const baseURL = "https://play.miguvideo.com/playurl/v1/play/playurl"
const params = "?sign=" + sign + "&rateType=" + rateType
+ "&contId=" + pid + "&timestamp=" + timestramp + "&salt=" + salt
const respData = await axios.get(baseURL + params, {
const respData = await fetch(baseURL + params, {
headers: headers
}).then(r => r.data)
}).then(r => r.json())
// console.log(respData)
// console.dir(respData, { depth: null })
const url = respData.body.urlInfo?.url
// console.log(rateType)
// console.log(url)
if (!url) {
return {
url: "",
rateType: 0
rateType: 0,
content: respData
}
}
@@ -273,4 +169,4 @@ async function getAndroidURL720p(pid) {
}
export { getAndroidVideoURL, getAndroidURL, getAndroidURL720p }
export { getAndroidURL, getAndroidURL720p }

View File

@@ -17,7 +17,7 @@ function printYellow(msg) {
}
function printBlue(msg) {
basePrint("\x1B[33m", msg)
basePrint("\x1B[34m", msg)
}
function printMagenta(msg) {

View File

@@ -21,830 +21,4 @@ const cntvNames = {
"CCTV4美洲": "cctvamerica",
}
// 频道信息
const channelName = [
{
cateName: "央视",
data: [
{ name: "CCTV1综合", pid: "608807420", },
{ name: "CCTV2财经", pid: "631780532", },
{ name: "CCTV3综艺", pid: "624878271", },
{ name: "CCTV4中文国际", pid: "631780421", },
{ name: "CCTV5体育", pid: "641886683", },
{ name: "CCTV5+体育赛事", pid: "641886773", },
{ name: "CCTV6电影", pid: "624878396", },
{ name: "CCTV7国防军事", pid: "673168121", },
{ name: "CCTV8电视剧", pid: "624878356", },
{ name: "CCTV9纪录", pid: "673168140", },
{ name: "CCTV10科教", pid: "624878405", },
{ name: "CCTV11戏曲", pid: "667987558", },
{ name: "CCTV12社会与法", pid: "673168185", },
{ name: "CCTV13新闻", pid: "608807423", },
{ name: "CCTV14少儿", pid: "624878440", },
{ name: "CCTV15音乐", pid: "673168223", },
{ name: "CCTV17农业农村", pid: "673168256", },
{ name: "CCTV4欧洲", pid: "608807419", },
{ name: "CCTV4美洲", pid: "608807416", },
{ name: "CGTN外语记录", pid: "609006487", },
{ name: "CGTN阿拉伯语", pid: "609154345", },
{ name: "CGTN西班牙语", pid: "609006450", },
{ name: "CGTN法语", pid: "609006476", },
{ name: "CGTN俄语", pid: "609006446", },
{ name: "老故事", pid: "884121956", },
{ name: "发现之旅", pid: "624878970", },
{ name: "中学生", pid: "708869532", },
{ name: "CGTN", pid: "609017205", },
]
},
{
cateName: "卫视",
data: [
{ name: "东方卫视", pid: "651632648", },
{ name: "江苏卫视", pid: "623899368", },
{ name: "广东卫视", pid: "608831231", },
{ name: "江西卫视", pid: "783847495", },
{ name: "河南卫视", pid: "790187291", },
{ name: "陕西卫视", pid: "738910838", },
{ name: "大湾区卫视", pid: "608917627", },
{ name: "湖北卫视", pid: "947472496", },
{ name: "吉林卫视", pid: "947472500", },
{ name: "青海卫视", pid: "947472506", },
{ name: "东南卫视", pid: "849116810", },
{ name: "海南卫视", pid: "947472502", },
{ name: "海峡卫视", pid: "849119120", },]
},
{
cateName: "地方",
data: [
{ name: "上海新闻综合", pid: "651632657", },
{ name: "上视东方影视", pid: "617290047", },
{ name: "上海第一财经", pid: "608780988", },
{ name: "南京新闻综合频道", pid: "838109047", },
{ name: "南京教科频道", pid: "838153729", },
{ name: "南京十八频道", pid: "838151753", },
{ name: "体育休闲频道", pid: "626064707", },
{ name: "江苏城市频道", pid: "626064714", },
{ name: "江苏国际", pid: "626064674", },
{ name: "江苏教育", pid: "628008321", },
{ name: "江苏影视频道", pid: "626064697", },
{ name: "江苏综艺频道", pid: "626065193", },
{ name: "公共新闻频道", pid: "626064693", },
{ name: "淮安新闻综合", pid: "639731826", },
{ name: "泰州新闻综合", pid: "639731818", },
{ name: "连云港新闻综合", pid: "639731715", },
{ name: "宿迁新闻综合", pid: "639731832", },
{ name: "徐州新闻综合", pid: "639731747", },
{ name: "优漫卡通频道", pid: "626064703", },
],
},
{
cateName: "影视",
data: [
{ name: "中国航天“逆袭”之路", pid: "617432318", },
{ name: "新片放映厅", pid: "619495952", },
{ name: "CHC影迷电影", pid: "952383261", },
{ name: "CHC动作电影", pid: "644368714", },
{ name: "CHC家庭影院", pid: "644368373", },
{ name: "和美乡途轮播台", pid: "713591450", },
{ name: "高清大片", pid: "629943678", },
{ name: "南方影视", pid: "614961829", },
{ name: "红色轮播台", pid: "713600957", },
{ name: "华语乐坛最强音", pid: "707671890", },
{ name: "周杰伦现场", pid: "625133682", },
{ name: "最强综艺趴", pid: "629942228", },
{ name: "嘉佳卡通", pid: "614952364", },
{ name: "经典动画大集合", pid: "629942219", },
{ name: "深度对话", pid: "625703337", },
{ name: "CETV1", pid: "923287154", },
{ name: "CETV2", pid: "923287211", },
{ name: "CETV4", pid: "923287339", },
{ name: "新动解码在一线", pid: "713589837", },
],
},
{
cateName: "体育",
data: [
{ name: "赛事最经典", pid: "646596895", },
{ name: "掼蛋精英赛", pid: "631354620", },
{ name: "体坛名栏汇", pid: "629943305", },
{ name: "咪咕24小时体育台", pid: "654102378", },
]
},
{
cateName: "熊猫",
data: [
{ name: "熊猫频道1", pid: "608933610", },
{ name: "熊猫频道2", pid: "608933640", },
{ name: "熊猫频道3", pid: "608934619", },
{ name: "熊猫频道4", pid: "608934721", },
{ name: "熊猫频道5", pid: "608935104", },
{ name: "熊猫频道6", pid: "608935797", },
{ name: "熊猫频道7", pid: "609169286", },
{ name: "熊猫频道8", pid: "609169287", },
{ name: "熊猫频道9", pid: "609169226", },
{ name: "熊猫频道10", pid: "609169285", },
]
},
]
// 加密字符串替换规则
// index 替换字符串位置从1开始
// data 与index一一对应修改字符为
// noChangeStandard 标清下第一个字符是否不需要修改
const changedDdCalcu = {
// 分类:央视 频道名:CCTV1综合
// "608807420": {
// "all": {
// index: [5, 8, 11, 14],
// data: ["x", "a", "a", "a"]
// },
// },
// 分类:央视 频道名:CCTV2财经
"631780532": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "z", "a"]
},
},
// 分类:央视 频道名:CCTV3综艺
"624878271": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:央视 频道名:CCTV4中文国际
"631780421": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:央视 频道名:CCTV5体育
"641886683": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
},
// 分类:央视 频道名:CCTV5+体育赛事
"641886773": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
},
// 分类:央视 频道名:CCTV6电影
"624878396": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:央视 频道名:CCTV7国防军事
"673168121": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
},
// 分类:央视 频道名:CCTV8电视剧
"624878356": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:央视 频道名:CCTV9纪录
"673168140": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
},
// 分类:央视 频道名:CCTV10科教
"624878405": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:央视 频道名:CCTV11戏曲
"667987558": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "z", "a"]
},
},
// 分类:央视 频道名:CCTV12社会与法
"673168185": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
},
// 分类:央视 频道名:CCTV13新闻
"608807423": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:央视 频道名:CCTV14少儿
"624878440": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:央视 频道名:CCTV15音乐
"673168223": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:央视 频道名:CCTV17农业农村
"673168256": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:央视 频道名:CCTV4欧洲
"608807419": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:央视 频道名:CCTV4美洲
"608807416": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:央视 频道名:CGTN外语记录
"609006487": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
noChangeStandard: true,
},
// 分类:央视 频道名:CGTN阿拉伯语
"609154345": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
noChangeStandard: true,
},
// 分类:央视 频道名:CGTN西班牙语
"609006450": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
noChangeStandard: true,
},
// 分类:央视 频道名:CGTN法语
"609006476": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
noChangeStandard: true,
},
// 分类:央视 频道名:CGTN俄语
"609006446": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
noChangeStandard: true,
},
// 分类:央视 频道名:老故事
"884121956": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "l", "a"]
},
noChangeStandard: true,
},
// 分类:央视 频道名:发现之旅
"624878970": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "l", "a"]
},
},
// 分类:央视 频道名:中学生
"708869532": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "z", "a"]
},
},
// 分类:央视 频道名:CGTN
"609017205": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
noChangeStandard: true,
},
// 分类:卫视 频道名:东方卫视
"651632648": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
},
// 分类:卫视 频道名:江苏卫视
"623899368": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:卫视 频道名:广东卫视
"608831231": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:卫视 频道名:江西卫视
"783847495": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:卫视 频道名:河南卫视
"790187291": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
noChangeStandard: true,
},
// 分类:卫视 频道名:陕西卫视
"738910838": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
},
// 分类:卫视 频道名:大湾区卫视
"608917627": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:卫视 频道名:湖北卫视
"947472496": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:卫视 频道名:吉林卫视
"947472500": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "z", "a"]
},
noChangeStandard: true,
},
// 分类:卫视 频道名:青海卫视
"947472506": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "z", "a"]
},
noChangeStandard: true,
},
// 分类:卫视 频道名:东南卫视
"849116810": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
},
// 分类:卫视 频道名:海南卫视
"947472502": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "z", "a"]
},
noChangeStandard: true,
},
// 分类:卫视 频道名:海峡卫视
"849119120": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
},
// 分类:地方 频道名:上海新闻综合
"651632657": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
},
// 分类:地方 频道名:上视东方影视
"617290047": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "c", "a"]
},
},
// 分类:地方 频道名:上海第一财经
"608780988": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "l", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:南京新闻综合频道
"838109047": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "c", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:南京教科频道
"838153729": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:南京十八频道
"838151753": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:体育休闲频道
"626064707": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:江苏城市频道
"626064714": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:江苏国际
"626064674": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:江苏教育
"628008321": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:江苏影视频道
"626064697": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:江苏综艺频道
"626065193": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:公共新闻频道
"626064693": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:淮安新闻综合
"639731826": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:泰州新闻综合
"639731818": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:连云港新闻综合
"639731715": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:宿迁新闻综合
"639731832": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:徐州新闻综合
"639731747": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:地方 频道名:优漫卡通频道
"626064703": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:影视 频道名:中国航天“逆袭”之路
"617432318": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:影视 频道名:新片放映厅
"619495952": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "l", "a"]
},
},
// 分类:影视 频道名:CHC影迷电影
"952383261": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:影视 频道名:CHC动作电影
"644368714": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
},
// 分类:影视 频道名:CHC家庭影院
"644368373": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:影视 频道名:和美乡途轮播台
"713591450": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "y", "a"]
},
},
// 分类:影视 频道名:高清大片
"629943678": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
},
// 分类:影视 频道名:南方影视
"614961829": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
noChangeStandard: true,
},
// 分类:影视 频道名:红色轮播台
"713600957": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "l", "a"]
},
},
// 分类:影视 频道名:华语乐坛最强音
"707671890": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
},
// 分类:影视 频道名:周杰伦现场
"625133682": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
},
// 分类:影视 频道名:最强综艺趴
"629942228": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:影视 频道名:嘉佳卡通
"614952364": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
noChangeStandard: true,
},
// 分类:影视 频道名:经典动画大集合
"629942219": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:影视 频道名:深度对话
"625703337": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:影视 频道名:CETV1
"923287154": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
},
// 分类:影视 频道名:CETV2
"923287211": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
},
// 分类:影视 频道名:CETV4
"923287339": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:影视 频道名:新动解码在一线
"713589837": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
},
// 分类:体育 频道名:赛事最经典
"646596895": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "k", "a"]
},
},
// 分类:体育 频道名:掼蛋精英赛
"631354620": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
},
// 分类:体育 频道名:体坛名栏汇
"629943305": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:体育 频道名:咪咕24小时体育台
"654102378": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "b", "a"]
},
},
// 分类:熊猫 频道名:熊猫频道1
"608933610": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道2
"608933640": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道3
"608934619": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "w", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道4
"608934721": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道5
"608935104": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "d", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道6
"608935797": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "x", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道7
"609169286": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道8
"609169287": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道9
"609169226": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
noChangeStandard: true,
},
// 分类:熊猫 频道名:熊猫频道10
"609169285": {
"all": {
index: [5, 8, 11, 14],
data: ["x", "a", "a", "a"]
},
noChangeStandard: true,
},
}
export { changedDdCalcu, channelName, cntvNames }
export { cntvNames }

View File

@@ -98,14 +98,6 @@ function getEncryptURL(exports, videoURL) {
/**
* h5端现已失效
* 获取ddCalcu
* 大致思路:把puData最后一个字符和第一个字符拼接然后拼接倒数第二个跟第二个一直循环
* 当第1 2 3 4次(从0开始)循环时需要插入特殊标识字符
* 特殊字符:
* 都是根据一些数字字符串的某一位的值对应某数组的值确定的,形如数组[数字字符串[第几位]],具体根据第几位每个版本都不一样
* 第1次是根据userid确定的未登录时为固定字母
* 第2位是根据时间戳确定(需要yyyyMMddhhmmss格式)
* 第3根据节目id
* 第4是根据平台确定的
* @param {string} puData - 服务器返回的那个东东
* @param {string} programId - 节目ID
* @param {string} clientType - 平台类型 h5 android
@@ -130,7 +122,6 @@ function getddCalcu(puData, programId, clientType, rateType) {
return ""
}
// words第1位是根据userId的第7位(从0开始)数字对应keys里的字母生成的
// 不登录标清是默认v
const id = userId ? userId : process.env.USERID
if (id) {
@@ -219,8 +210,7 @@ function getddCalcu720p(puData, programId) {
if (programId == null || programId == undefined) {
return ""
}
const keys = "0123456789"
const keys = "cdabyzwxkl"
let ddCalcu = []
for (let i = 0; i < puData.length / 2; i++) {
@@ -230,16 +220,16 @@ function getddCalcu720p(puData, programId) {
switch (i) {
case 1:
// ddCalcu.push(token=="" ?"e":keys[] )
ddCalcu.push("e")
ddCalcu.push("v")
break;
case 2:
ddCalcu.push(keys[parseInt(getDateString(new Date())[6])])
ddCalcu.push(keys[parseInt(getDateString(new Date())[2])])
break;
case 3:
ddCalcu.push(keys[programId[2]])
ddCalcu.push(keys[programId[6]])
break;
case 4:
ddCalcu.push("0")
ddCalcu.push("a")
break;
}
}
@@ -265,7 +255,7 @@ function getddCalcuURL720p(puDataURL, programId) {
const puData = puDataURL.split("&puData=")[1]
const ddCalcu = getddCalcu720p(puData, programId)
return `${puDataURL}&ddCalcu=${ddCalcu}`
return `${puDataURL}&ddCalcu=${ddCalcu}&sv=10004&ct=android`
}
export { initWasm, getEncryptURL, getddCalcuURL, getddCalcuURL720p }

View File

@@ -1,4 +1,3 @@
import axios from "axios"
// 睡眠
function delay(ms) {
@@ -9,10 +8,10 @@ function delay(ms) {
// 获取分类集合
async function cateList() {
const resp = await axios.get("https://program-sc.miguvideo.com/live/v2/tv-data/1ff892f2b5ab4a79be6e25b69d2f5d05")
let liveList = resp.data.body.liveList
const resp = await fetch("https://program-sc.miguvideo.com/live/v2/tv-data/1ff892f2b5ab4a79be6e25b69d2f5d05").then(r => r.json())
let liveList = resp.body.liveList
// 热门内容重复
liveList = liveList.filter((item) => {
liveList = liveList.filter(item => {
return item.name != "热门"
})
@@ -32,8 +31,8 @@ async function dataList() {
for (let cate in cates) {
try {
const resp = await axios.get("https://program-sc.miguvideo.com/live/v2/tv-data/" + cates[cate].vomsID)
cates[cate].dataList = resp.data.body.dataList
const resp = await fetch("https://program-sc.miguvideo.com/live/v2/tv-data/" + cates[cate].vomsID).then(r => r.json())
cates[cate].dataList = resp.body.dataList
} catch (error) {
cates[cate].dataList = [];
}

View File

@@ -40,5 +40,12 @@ function renameFileSync(oldFilePath, newFilePath) {
}
})
}
function copyFileSync(filePath, newFilePath, mode) {
fs.copyFileSync(filePath, newFilePath, mode, err => {
if (err) {
throw new Error(`文件复制失败${filePath} -> ${newFilePath}`)
}
})
}
export { createFile, writeFile, appendFile, appendFileSync, readFileSync, renameFileSync }
export { createFile, writeFile, appendFile, appendFileSync, readFileSync, renameFileSync, copyFileSync }

View File

@@ -1,4 +1,3 @@
import axios from "axios"
import { getDateString, getDateTimeString } from "./time.js"
import { appendFileSync } from "./fileUtil.js"
import { cntvNames } from "./datas.js"
@@ -7,7 +6,7 @@ import { cntvNames } from "./datas.js"
async function getPlaybackData(programId) {
const date = new Date()
const today = getDateString(date)
const resp = await axios.get(`https://program-sc.miguvideo.com/live/v2/tv-programs-data/${programId}/${today}`).then(r => r.data)
const resp = await fetch(`https://program-sc.miguvideo.com/live/v2/tv-programs-data/${programId}/${today}`).then(r => r.json())
return resp.body?.program[0]?.content
}
@@ -43,7 +42,7 @@ async function updatePlaybackDataByCntv(program, filePath) {
const date = new Date()
const today = getDateString(date)
const cntvName = cntvNames[program.name]
const resp = await axios.get(`https://api.cntv.cn/epg/epginfo3?serviceId=shiyi&d=${today}&c=${cntvName}`).then(r => r.data)
const resp = await fetch(`https://api.cntv.cn/epg/epginfo3?serviceId=shiyi&d=${today}&c=${cntvName}`).then(r => r.json())
const playbackData = resp[cntvName]?.program
if (!playbackData) {

View File

@@ -1,7 +1,9 @@
import axios from "axios"
import { AESencrypt, getStringMD5, KEY_AES, RSAencrypt } from "./EncryUtils.js"
import { AESencrypt, getStringMD5, RSAencrypt } from "./EncryUtils.js"
// 类似URLEncoder.encode()的效果
/**
* @param {string} str -
* @returns {string} -
*/
function encodeURLEncoder(str) {
return encodeURIComponent(str)
.replace(/[!'()*]/g, (c) =>
@@ -10,7 +12,12 @@ function encodeURLEncoder(str) {
.replace(/%20/g, '+');
}
// 刷新token
/**
* 刷新token
* @param {string} userId - 用户ID
* @param {string} token - 用户token
* @returns {boolean} - 是否成功
*/
async function refreshToken(userId, token) {
if (userId == null || userId == undefined || token == null || token == undefined) {
@@ -22,8 +29,8 @@ async function refreshToken(userId, token) {
const baseData = `{"userToken":"${token}","autoDelay":true,"deviceId":"","userId":"${userId}","timestamp":"${time}"}`
// 请求体加密
const encryData = AESencrypt(baseData, KEY_AES)
const data = '{"data":"' + encryData.substring(0, encryData.length - 2) + '\\u003d\\u003d"}'
const encryData = AESencrypt(baseData)
const data = '{"data":"' + encryData + '"}'
// 签名
const str = getStringMD5(data)
@@ -39,9 +46,11 @@ async function refreshToken(userId, token) {
const params = `?clientId=27fb3129-5a54-45bc-8af1-7dc8f1155501&sign=${sign}&signType=RSA`
// 发送请求
const respResult = await axios.post(baseURL + params, data, {
headers: headers
}).then(r => r.data)
const respResult = await fetch(baseURL + params, {
headers: headers,
method: "post",
body: data
}).then(r => r.json())
// 处理响应结果
if (respResult.resultCode == "REFRESH_TOKEN_SUCCESS") {