181 lines
7.5 KiB
JavaScript
181 lines
7.5 KiB
JavaScript
|
/*
|
||
|
* @File : danmuSpider.js
|
||
|
* @Author : jade
|
||
|
* @Date : 2024/3/13 13:39
|
||
|
* @Email : jadehh@1ive.com
|
||
|
* @Software : Samples
|
||
|
* @Desc :
|
||
|
*/
|
||
|
|
||
|
import {_, load, Uri} from "./cat.js";
|
||
|
import * as Utils from "./utils.js";
|
||
|
import {JadeLogging} from "./log.js";
|
||
|
import {VodDetail, VodShort} from "./vod.js";
|
||
|
import {parseXML} from "./bilibili_ASS_Danmaku_Downloader.js";
|
||
|
|
||
|
class DanmuSpider {
|
||
|
constructor() {
|
||
|
this.siteUrl = "https://search.youku.com"
|
||
|
this.reconnectTimes = 0
|
||
|
this.maxReconnectTimes = 5
|
||
|
this.jadeLog = new JadeLogging(this.getAppName(), "DEBUG")
|
||
|
}
|
||
|
|
||
|
getAppName() {
|
||
|
return "弹幕"
|
||
|
}
|
||
|
|
||
|
getHeader() {
|
||
|
return {"User-Agent": Utils.CHROME, "Referer": this.siteUrl + "/"};
|
||
|
}
|
||
|
|
||
|
async reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer) {
|
||
|
await this.jadeLog.error("请求失败,请检查url:" + reqUrl + ",两秒后重试")
|
||
|
Utils.sleep(2)
|
||
|
if (this.reconnectTimes < this.maxReconnectTimes) {
|
||
|
this.reconnectTimes = this.reconnectTimes + 1
|
||
|
return await this.fetch(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||
|
} else {
|
||
|
await this.jadeLog.error("请求失败,重连失败")
|
||
|
return null
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response) {
|
||
|
{
|
||
|
if (response.headers["location"] !== undefined) {
|
||
|
if (redirect_url) {
|
||
|
await this.jadeLog.debug(`返回重定向连接:${response.headers["location"]}`)
|
||
|
return response.headers["location"]
|
||
|
} else {
|
||
|
return this.fetch(response.headers["location"], params, headers, redirect_url, return_cookie, buffer)
|
||
|
}
|
||
|
} else if (response.content.length > 0) {
|
||
|
this.reconnectTimes = 0
|
||
|
if (return_cookie) {
|
||
|
return {"cookie": response.headers["set-cookie"], "content": response.content}
|
||
|
} else {
|
||
|
return response.content
|
||
|
}
|
||
|
} else if (buffer === 1) {
|
||
|
this.reconnectTimes = 0
|
||
|
return response.content
|
||
|
} else {
|
||
|
await this.jadeLog.error(`请求失败,请求url为:${reqUrl},回复内容为:${JSON.stringify(response)}`)
|
||
|
return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
async fetch(reqUrl, params, headers, redirect_url = false, return_cookie = false, buffer = 0) {
|
||
|
let data = Utils.objectToStr(params)
|
||
|
let url = reqUrl
|
||
|
if (!_.isEmpty(data)) {
|
||
|
url = reqUrl + "?" + data
|
||
|
}
|
||
|
let uri = new Uri(url);
|
||
|
let response;
|
||
|
response = await req(uri.toString(), {method: "get", headers: headers, buffer: buffer, data: null})
|
||
|
if (response.code === 200 || response.code === 302 || response.code === 301 || return_cookie) {
|
||
|
return await this.getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response)
|
||
|
} else {
|
||
|
await this.jadeLog.error(`请求失败,失败原因为:状态码出错,请求url为:${uri},回复内容为:${JSON.stringify(response)}`)
|
||
|
return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async getHtml(url = this.siteUrl, headers = this.getHeader()) {
|
||
|
let html = await this.fetch(url, null, headers)
|
||
|
if (!_.isEmpty(html)) {
|
||
|
return load(html)
|
||
|
} else {
|
||
|
await this.jadeLog.error(`html获取失败`, true)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async parseVodShortListFromJson(obj, vodDetail) {
|
||
|
for (const componentObj of obj["pageComponentList"]) {
|
||
|
if (componentObj["commonData"] !== undefined) {
|
||
|
let searchVodDetail = new VodDetail()
|
||
|
let commonData = componentObj["commonData"]
|
||
|
searchVodDetail.type_name = commonData["feature"]
|
||
|
if (commonData["notice"] !== undefined) {
|
||
|
searchVodDetail.vod_actor = commonData["notice"].replaceAll("演员:", "").replaceAll(" ", "")
|
||
|
}
|
||
|
if (commonData["director"] !== undefined) {
|
||
|
searchVodDetail.vod_director = commonData["director"].replaceAll("导演:", "").replaceAll(" ", "")
|
||
|
}
|
||
|
if (vodDetail.type_name === "电影") {
|
||
|
searchVodDetail.vod_id = commonData["leftButtonDTO"]["action"]["value"]
|
||
|
} else {
|
||
|
searchVodDetail.vod_id = commonData["showId"]
|
||
|
}
|
||
|
searchVodDetail.vod_name = commonData["titleDTO"]["displayName"]
|
||
|
if ( searchVodDetail.vod_name === vodDetail.vod_name || searchVodDetail.type_name.indexOf(vodDetail.vod_year) > -1 || searchVodDetail.type_name.indexOf(vodDetail.type_name) > -1 || searchVodDetail.vod_director === vodDetail.vod_director) {
|
||
|
await this.jadeLog.debug(`匹配视频网站成功,名称为:${searchVodDetail.vod_name},类型为:${searchVodDetail.type_name},导演为:${searchVodDetail.vod_director}`, true)
|
||
|
return searchVodDetail
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
await this.jadeLog.warning("没有匹配到弹幕网站")
|
||
|
return null
|
||
|
}
|
||
|
|
||
|
async parseVodUrlFromJsonByEpisodeId(obj, episodeId) {
|
||
|
for (const serises of obj["serisesList"]) {
|
||
|
if (Utils.isNumeric(episodeId["episodeId"])) {
|
||
|
if (parseInt(episodeId["episodeId"]).toString() === serises["displayName"]) {
|
||
|
return serises["action"]["value"]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
await this.jadeLog.error("没有找到匹配的集数")
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
async downloadDanmu(url) {
|
||
|
let json = JSON.parse(await this.fetch(url, null, this.getHeader()))
|
||
|
let xml = parseXML(json)
|
||
|
let params = {"do": "set", "key": "danmu", "value": xml}
|
||
|
await req("http://127.0.0.1:9978/cache", {method: "post", data: params, postType: "form-data"});
|
||
|
return "http://127.0.0.1:9978/cache?do=get&key=danmu"
|
||
|
}
|
||
|
|
||
|
async search(vodDetail, episodeId) {
|
||
|
let params = {"pg": "1", "keyword": vodDetail.vod_name}
|
||
|
let searchObj = JSON.parse(await this.fetch(this.siteUrl + "/api/search", params, this.getHeader()))
|
||
|
let searchDetail = await this.parseVodShortListFromJson(searchObj, vodDetail)
|
||
|
if (!_.isEmpty(searchDetail)){
|
||
|
return await this.getVideoUrl(searchDetail.vod_id, episodeId)
|
||
|
}else{
|
||
|
return ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async getVideoUrl(showId, episodeId) {
|
||
|
let url = "";
|
||
|
if (!_.isEmpty(showId)) {
|
||
|
if (showId.startsWith("http")) {
|
||
|
url = showId
|
||
|
} else {
|
||
|
let params = {"appScene": "show_episode", "showIds": showId}
|
||
|
let matchObj = JSON.parse(await this.fetch(this.siteUrl + "/api/search", params, this.getHeader()))
|
||
|
url = await this.parseVodUrlFromJsonByEpisodeId(matchObj, episodeId)
|
||
|
}
|
||
|
if (!_.isEmpty(url)) {
|
||
|
await this.jadeLog.debug(`弹幕视频播放连接为:${url}`)
|
||
|
return await this.downloadDanmu("https://dmku.thefilehosting.com/?ac=dm&url=" + url)
|
||
|
}
|
||
|
}
|
||
|
return url
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
async getDammu(voddetail, episodeId) {
|
||
|
return await this.search(voddetail, episodeId)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export {DanmuSpider}
|