Files
tvbox_collect/jaychouqq/yingshi/js6/PPnix.js
T
2026-06-29 03:41:14 +00:00

202 lines
6.8 KiB
JavaScript

//@name:[直] PPnix
//@version:6
//@webSite:https://www.ppnix.com
//@remark:白猫出品 (增强逻辑版)
//@order:A04
//@codeID:
//@env:
//@isAV:0
//@deprecated:0
const appConfig = {
_webSite: 'https://www.ppnix.com',
get webSite() {
return this._webSite
},
set webSite(value) {
this._webSite = value
},
// 统一 UA 和 请求头,参考 PPnix (1).js
headers(referer) {
return {
"Referer": referer || this._webSite + '/cn/',
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
},
_uzTag: '',
get uzTag() {
return this._uzTag
},
set uzTag(value) {
this._uzTag = value
},
}
/**
* 分类列表
*/
async function getClassList(args) {
var backData = new RepVideoClassList()
try {
backData.data = [
{ type_id: 'movie', type_name: '电影', hasSubclass: false },
{ type_id: 'tv', type_name: '电视剧', hasSubclass: false },
]
} catch (error) {
backData.error = error.toString()
}
return JSON.stringify(backData)
}
/**
* 获取分类视频列表
*/
async function getVideoList(args) {
var backData = new RepVideoList()
try {
// 遵循 PPnix (1).js 的路径规则:/cn/movie/---1-newstime.html
const page = args.page > 1 ? args.page - 1 : ''
const url = `${appConfig.webSite}/cn/${args.url}/---${page}-newstime.html`
const response = await req(url, { headers: appConfig.headers() })
const $ = cheerio.load(response.data)
$('.lists-content ul li').each((_, li) => {
const video = new VideoDetail()
const $a = $(li).find('a').first()
video.vod_id = $a.attr('href')
video.vod_name = $a.find('img').attr('alt')
video.vod_pic = $a.find('img').attr('src')
video.vod_remarks = $(li).find('.orange, footer').first().text().trim()
const scores = $(li).find('.rate').text().trim()
if (scores && scores !== '0.0') {
video.topRightRemarks = '评分 ' + scores
}
backData.data.push(video)
})
} catch (error) {
backData.error = error.toString()
}
return JSON.stringify(backData)
}
/**
* 获取视频详情 - 移植 PPnix (1).js 的解析逻辑
*/
async function getVideoDetail(args) {
var backData = new RepVideoDetail()
try {
const url = appConfig.webSite + args.url
const response = await req(url, { headers: appConfig.headers() })
const $ = cheerio.load(response.data)
const video = new VideoDetail()
video.vod_id = args.url
const titleRaw = $('h1.product-title').text().trim()
video.vod_name = titleRaw.replace(/\s*\([^)]*\)\s*$/, "")
video.vod_pic = $('.product-header img').attr('src')
// 提取演员、导演、年份
video.vod_year = (titleRaw.match(/\((\d{4})\)/) || [])[1] || ""
video.vod_director = $(".product-excerpt:contains('导演:') span").text().trim()
video.vod_actor = $(".product-excerpt:contains('主演:') span").text().trim().replace(/\s*\/\s*/g, ",")
video.vod_content = $(".product-excerpt:contains('简介:')").text().replace('简介:', '').trim()
// --- 移植 PPnix (1).js 的 m3u8 提取逻辑 ---
const html = response.data
const infoId = (html.match(/infoid\s*=\s*(\d+)/) || [])[1] || (args.url.match(/(\d+)\.html/) || [])[1]
const m3u8Match = html.match(/m3u8\s*=\s*\[(.*?)\]/s)
const episodes = []
if (m3u8Match) {
const re = /'([^']*)'|"([^"]*)"/g
let mm
while ((mm = re.exec(m3u8Match[1])) !== null) {
const epName = mm[1] || mm[2]
if (epName) {
// 存储格式:名称$ID|参数|来源URL
// 注意:这里将关键参数封装在播放ID中,供 getVideoPlayUrl 使用
episodes.push(`${epName}$${infoId}|${encodeURIComponent(epName)}|${encodeURIComponent(url)}`)
}
}
}
if (episodes.length > 0) {
video.vod_play_from = "PPnix"
video.vod_play_url = episodes.join('#')
}
backData.data = video
} catch (error) {
backData.error = error.toString()
}
return JSON.stringify(backData)
}
/**
* 获取播放地址 - 移植 PPnix (1).js 的 M3U8 构造
*/
async function getVideoPlayUrl(args) {
var backData = new RepVideoPlayUrl()
try {
// 解码 getVideoDetail 中封装的参数
const parts = args.url.split('|')
const infoId = parts[0]
const param = decodeURIComponent(parts[1])
const referer = decodeURIComponent(parts[2])
// 构造 PPnix (1).js 中的 sourceUrl
const sourceUrl = `${appConfig.webSite}/info/m3u8/${infoId}/${encodeURIComponent(param)}.m3u8`
backData.url = sourceUrl
backData.headers = appConfig.headers(referer)
// 优先尝试直接播放,如果失效,UZ 会自动尝试嗅探
backData.parse = 0
} catch (error) {
backData.error = error.toString()
}
return JSON.stringify(backData)
}
/**
* 搜索视频 - 优化选择器
*/
async function searchVideo(args) {
var backData = new RepVideoList()
try {
const encoded = encodeURIComponent(args.searchWord)
const pagePart = args.page > 1 ? `-page-${args.page}` : ""
const url = `${appConfig.webSite}/cn/search/${encoded}--.html${pagePart}`
const response = await req(url, { headers: appConfig.headers() })
const $ = cheerio.load(response.data)
$('.lists-content ul li').each((_, e) => {
const video = new VideoDetail()
const $link = $(e).find('a').first()
video.vod_id = $link.attr('href')
video.vod_name = $link.find('img').attr('alt') || $link.attr('title')
video.vod_pic = $link.find('img').attr('src')
video.vod_remarks = $(e).find('footer').text().trim()
if (video.vod_id && video.vod_name) {
backData.data.push(video)
}
})
} catch (error) {
backData.error = error.toString()
}
return JSON.stringify(backData)
}
// 保持原样
async function getSubclassList(args) { return JSON.stringify(new RepVideoSubclassList()) }
async function getSubclassVideoList(args) { return JSON.stringify(new RepVideoList()) }