This commit is contained in:
Liu committed 2026-06-29 00:31:27 +08:00
1 parent 8284dab46d
commit 3f10d7691e
57 files changed
+32224 -11646

No files matched your search

+187
View File
@@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
# 本资源来源于互联网公开渠道,仅可用于个人学习及爬虫技术交流。
# 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
"""
{
"key": "xxx",
"name": "xxx",
"type": 3,
"api": "./ApptoV5无加密.py",
"ext": "http://domain.com"
}
"""
import re,sys,uuid
from base.spider import Spider
sys.path.append('..')
class Spider(Spider):
host,config,local_uuid,parsing_config = '','','',[]
headers = {
'User-Agent': "Dart/2.19 (dart:io)",
'Accept-Encoding': "gzip",
'appto-local-uuid': local_uuid
}
def init(self, extend=''):
try:
host = extend.strip()
if not host.startswith('http'):
return {}
if not re.match(r'^https?://[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(:\d+)?/?$', host):
host_=self.fetch(host).json()
self.host = host_['domain']
else:
self.host = host
self.local_uuid = str(uuid.uuid4())
response = self.fetch(f'{self.host}/apptov5/v1/config/get?p=android&__platform=android', headers=self.headers).json()
config = response['data']
self.config = config
parsing_conf = config['get_parsing']['lists']
parsing_config = {}
for i in parsing_conf:
if len(i['config']) != 0:
label = []
for j in i['config']:
if j['type'] == 'json':
label.append(j['label'])
parsing_config.update({i['key']:label})
self.parsing_config = parsing_config
return None
except Exception as e:
print(f'初始化异常:{e}')
return {}
def detailContent(self, ids):
response = self.fetch(f"{self.host}/apptov5/v1/vod/getVod?id={ids[0]}",headers=self.headers).json()
data3 = response['data']
videos = []
vod_play_url = ''
vod_play_from = ''
for i in data3['vod_play_list']:
play_url = ''
for j in i['urls']:
play_url += f"{j['name']}${i['player_info']['from']}@{j['url']}#"
vod_play_from += i['player_info']['show'] + '$$$'
vod_play_url += play_url.rstrip('#') + '$$$'
vod_play_url = vod_play_url.rstrip('$$$')
vod_play_from = vod_play_from.rstrip('$$$')
videos.append({
'vod_id': data3.get('vod_id'),
'vod_name': data3.get('vod_name'),
'vod_content': data3.get('vod_content'),
'vod_remarks': data3.get('vod_remarks'),
'vod_director': data3.get('vod_director'),
'vod_actor': data3.get('vod_actor'),
'vod_year': data3.get('vod_year'),
'vod_area': data3.get('vod_area'),
'vod_play_from': vod_play_from,
'vod_play_url': vod_play_url
})
return {'list': videos}
def searchContent(self, key, quick, pg='1'):
url = f"{self.host}/apptov5/v1/search/lists?wd={key}&page={pg}&type=&__platform=android"
response = self.fetch(url, headers=self.headers).json()
data = response['data']['data']
for i in data:
if i.get('vod_pic').startswith('mac://'):
i['vod_pic'] = i['vod_pic'].replace('mac://', 'http://', 1)
return {'list': data, 'page': pg, 'total': response['data']['total']}
def playerContent(self, flag, id, vipflags):
default_ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
parsing_config = self.parsing_config
parts = id.split('@')
if len(parts) != 2:
return {'parse': 0, 'url': id, 'header': {'User-Agent': default_ua}}
playfrom, rawurl = parts
label_list = parsing_config.get(playfrom)
if not label_list:
return {'parse': 0, 'url': rawurl, 'header': {'User-Agent': default_ua}}
result = {'parse': 1, 'url': rawurl, 'header': {'User-Agent': default_ua}}
for label in label_list:
payload = {
'play_url': rawurl,
'label': label,
'key': playfrom
}
try:
response = self.post(
f"{self.host}/apptov5/v1/parsing/proxy?__platform=android",
data=payload,
headers=self.headers
).json()
except Exception as e:
print(f"请求异常: {e}")
continue
if not isinstance(response, dict):
continue
if response.get('code') == 422:
continue
data = response.get('data')
if not isinstance(data, dict):
continue
url = data.get('url')
if not url:
continue
ua = data.get('UA') or data.get('UserAgent') or default_ua
result = {
'parse': 0,
'url': url,
'header': {'User-Agent': ua}
}
break
return result
def homeContent(self, filter):
config = self.config
if not config:
return {}
home_cate = config['get_home_cate']
classes = []
for i in home_cate:
if isinstance(i.get('extend', []),dict):
classes.append({'type_id': i['cate'], 'type_name': i['title']})
return {'class': classes}
def homeVideoContent(self):
response = self.fetch(f'{self.host}/apptov5/v1/home/data?id=1&mold=1&__platform=android',headers=self.headers).json()
data = response['data']
vod_list = []
for i in data['sections']:
for j in i['items']:
vod_pic = j.get('vod_pic')
if vod_pic.startswith('mac://'):
vod_pic = vod_pic.replace('mac://', 'http://', 1)
vod_list.append({
"vod_id": j.get('vod_id'),
"vod_name": j.get('vod_name'),
"vod_pic": vod_pic,
"vod_remarks": j.get('vod_remarks')
})
return {'list': vod_list}
def categoryContent(self, tid, pg, filter, extend):
response = self.fetch(f"{self.host}/apptov5/v1/vod/lists?area={extend.get('area','')}&lang={extend.get('lang','')}&year={extend.get('year','')}&order={extend.get('sort','time')}&type_id={tid}&type_name=&page={pg}&pageSize=21&__platform=android", headers=self.headers).json()
data = response['data']
data2 = data['data']
for i in data['data']:
if i.get('vod_pic','').startswith('mac://'):
i['vod_pic'] = i['vod_pic'].replace('mac://', 'http://', 1)
return {'list': data2, 'page': pg, 'total': data['total']}
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
def localProxy(self, param):
pass
+3
View File
@@ -0,0 +1,3 @@
// 本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。
// 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
import{Crypto as t,_ as e}from"assets://js/lib/cat.js";let n="https://api.dbokutv.com";const o={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)",Connection:"Keep-Alive",Referer:"https://www.duboku.tv/"};async function r(t){}async function a(t){const e=[{type_id:2,type_name:"连续剧"},{type_id:3,type_name:"综艺"},{type_id:1,type_name:"电影"},{type_id:4,type_name:"动漫"}],r={},a=e.map(async t=>{const e=t.type_id,a=f(`${n}/vodshow/${e}-----------`);try{const t=await req(a,{headers:o}),n=JSON.parse(t.content);if(n&&n.FilterList){const t=[];for(const e of n.FilterList){let n="";switch(e.Class){case"剧情":n="class";break;case"地区":n="area";break;case"年份":n="year";break;case"语言":n="lang";break;case"字母":n="letter";break;case"排序":n="sort";break;case"类型":n="scat"}if(n&&e.OptionList&&e.OptionList.length>0){const o=[];let r="";"sort"===n?(e.OptionList.forEach(t=>{o.push({n:t.Option,v:t.Option})}),r="时间"):(o.push({n:"全部",v:""}),e.OptionList.forEach(t=>{"全部"!==t.Option&&o.push({n:t.Option,v:t.Option})}),r=""),t.push({key:n,name:e.Class,value:o,init:r})}}t.length>0&&(r[e]=t)}}catch(t){}});return await Promise.all(a),JSON.stringify({class:e,filters:r})}async function s(){const t=f(`${n}/home`),e=await req(t,{headers:o}),r=JSON.parse(e.content);let a=[];if(r&&Array.isArray(r))for(const t of r){const e=t.VodList||[];for(const t of e)a.push({vod_id:l(t.DId||t.DuId),vod_name:t.Name,vod_pic:l(t.TnId),vod_remarks:t.Tag})}return JSON.stringify({list:a})}async function i(t,e,r,a){const s=new Array(12).fill("");s[0]=t,s[1]=a.area||"",s[2]=a.sort||"时间",s[3]=a.class||"",s[4]=a.lang||"",s[5]=a.letter||"",s[6]=a.scat||"",s[8]="1"===e.toString()?"":e,s[11]=a.year||"";const i=f(`${n}/vodshow/${s.join("-")}`),c=await req(i,{headers:o}),p=JSON.parse(c.content);let d=[];if(p.VodList)for(const t of p.VodList)d.push({vod_id:l(t.DId||t.DuId),vod_name:t.Name,vod_pic:l(t.TnId),vod_remarks:t.Tag});let u=0;try{if(p.PaginationList){for(const t of p.PaginationList)if("StartEnd"===t.Type){const e=l(t.PId||t.PuId).split("-");if(e.length>8){const t=parseInt(e[8]);t&&(u=t)}}if(0===u)for(const t of p.PaginationList)if("ShortPage"===t.Type){const e=t.Name.split("/");if(e.length>1){const t=parseInt(e[1]);t&&(u=t)}}}}catch(t){u=1}return 0===u&&(u=1),JSON.stringify({list:d,page:parseInt(e),pagecount:u})}async function c(t,e,r=1){let a=f(`${n}/vodsearch`);a+=`&wd=${encodeURIComponent(t)}`;const s=await req(a,{headers:o}),i=JSON.parse(s.content);let c=[];if(i&&Array.isArray(i))for(const t of i)c.push({vod_id:l(t.DId||t.DuId),vod_name:t.Name,vod_pic:l(t.TnId),vod_remarks:t.Tag,vod_actor:t.Actor,vod_score:t.Rating});return JSON.stringify({list:c,page:parseInt(r)})}async function p(t){const e=f(`${n}${t}`),r=await req(e,{headers:o}),a=JSON.parse(r.content);let s=[];a.Playlist&&(s=a.Playlist.map(t=>`${t.EpisodeName}$${l(t.VId)}`));const i={vod_id:t,vod_name:a.Name,vod_pic:l(a.TnId),vod_remarks:`评分:${a.Rating}`,vod_year:a.ReleaseYear,vod_area:a.Region,vod_actor:Array.isArray(a.Actor)?a.Actor.join(","):a.Actor,vod_director:a.Director,vod_content:a.Description,vod_play_from:"独播库",vod_play_url:s.join("#"),type_name:`${a.Genre},${a.Scenario},${a.Language}`};return JSON.stringify({list:[i]})}async function d(t,e,r){const a=f(`${n}${e}`),s=await req(a,{headers:o}),i=l(JSON.parse(s.content).HId);return JSON.stringify({jx:0,parse:0,url:i,header:{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36","Accept-Encoding":"gzip, deflate",origin:"https://w.duboku.io",referer:"https://w.duboku.io/",priority:"u=1, i"}})}function l(e){if(!e||"string"!=typeof e)return"";let n=e.replace(/['"]/g,"");if(!n)return"";let o="";for(let t=0;t<n.length;t+=10){o+=n.substring(t,t+10).split("").reverse().join("")}o=o.replace(/\./g,"=");try{const e=t.enc.Base64.parse(o);return t.enc.Utf8.stringify(e)}catch(t){return""}}function f(e){const n=Math.floor(800000001*Math.random()),o=function(t,e){let n=[];const o=Math.min(t.length,e.length);for(let r=0;r<o;r++)n.push(t[r]),n.push(e[r]);return n.push(t.substring(o)),n.push(e.substring(o)),n.join("")}(`${n+1e8}${9e8-n}`,Math.floor(Date.now()/1e3).toString()),r=t.enc.Utf8.parse(o),a=t.enc.Base64.stringify(r).replace(/=/g,".");return`${e}?sign=${u(60)}&token=${u(38)}&ssid=${a}`}function u(t){const e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";let n="";for(let o=0;o<t;o++)n+=e.charAt(Math.floor(62*Math.random()));return n}export function __jsEvalReturn(){return{init:r,home:a,homeVod:s,category:i,search:c,detail:p,play:d}}
+3108
View File
File diff suppressed because it is too large. Load diff
+607
View File
@@ -0,0 +1,607 @@
/*!
* Jinja Templating for JavaScript v0.1.8
* https://github.com/sstur/jinja-js
*
* This is a slimmed-down Jinja2 implementation [http://jinja.pocoo.org/]
*
* In the interest of simplicity, it deviates from Jinja2 as follows:
* - Line statements, cycle, super, macro tags and block nesting are not implemented
* - auto escapes html by default (the filter is "html" not "e")
* - Only "html" and "safe" filters are built in
* - Filters are not valid in expressions; `foo|length > 1` is not valid
* - Expression Tests (`if num is odd`) not implemented (`is` translates to `==` and `isnot` to `!=`)
*
* Notes:
* - if property is not found, but method '_get' exists, it will be called with the property name (and cached)
* - `{% for n in obj %}` iterates the object's keys; get the value with `{% for n in obj %}{{ obj[n] }}{% endfor %}`
* - subscript notation `a[0]` takes literals or simple variables but not `a[item.key]`
* - `.2` is not a valid number literal; use `0.2`
*
*/
/*global require, exports, module, define */
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jinja = {}));
})(this, (function(jinja) {
"use strict";
var STRINGS = /'(\\.|[^'])*'|"(\\.|[^"'"])*"/g;
var IDENTS_AND_NUMS = /([$_a-z][$\w]*)|([+-]?\d+(\.\d+)?)/g;
var NUMBER = /^[+-]?\d+(\.\d+)?$/;
//non-primitive literals (array and object literals)
var NON_PRIMITIVES = /\[[@#~](,[@#~])*\]|\[\]|\{([@i]:[@#~])(,[@i]:[@#~])*\}|\{\}/g;
//bare identifiers such as variables and in object literals: {foo: 'value'}
var IDENTIFIERS = /[$_a-z][$\w]*/ig;
var VARIABLES = /i(\.i|\[[@#i]\])*/g;
var ACCESSOR = /(\.i|\[[@#i]\])/g;
var OPERATORS = /(===?|!==?|>=?|<=?|&&|\|\||[+\-\*\/%])/g;
//extended (english) operators
var EOPS = /(^|[^$\w])(and|or|not|is|isnot)([^$\w]|$)/g;
var LEADING_SPACE = /^\s+/;
var TRAILING_SPACE = /\s+$/;
var START_TOKEN = /\{\{\{|\{\{|\{%|\{#/;
var TAGS = {
'{{{': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}\}/,
'{{': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}/,
'{%': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?%\}/,
'{#': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?#\}/
};
var delimeters = {
'{%': 'directive',
'{{': 'output',
'{#': 'comment'
};
var operators = {
and: '&&',
or: '||',
not: '!',
is: '==',
isnot: '!='
};
var constants = {
'true': true,
'false': false,
'null': null
};
function Parser() {
this.nest = [];
this.compiled = [];
this.childBlocks = 0;
this.parentBlocks = 0;
this.isSilent = false;
}
Parser.prototype.push = function(line) {
if (!this.isSilent) {
this.compiled.push(line);
}
};
Parser.prototype.parse = function(src) {
this.tokenize(src);
return this.compiled;
};
Parser.prototype.tokenize = function(src) {
var lastEnd = 0,
parser = this,
trimLeading = false;
matchAll(src, START_TOKEN, function(open, index, src) {
//here we match the rest of the src against a regex for this tag
var match = src.slice(index + open.length).match(TAGS[open]);
match = (match ? match[0] : '');
//here we sub out strings so we don't get false matches
var simplified = match.replace(STRINGS, '@');
//if we don't have a close tag or there is a nested open tag
if (!match || ~simplified.indexOf(open)) {
return index + 1;
}
var inner = match.slice(0, 0 - open.length);
//check for white-space collapse syntax
if (inner.charAt(0) === '-') var wsCollapseLeft = true;
if (inner.slice(-1) === '-') var wsCollapseRight = true;
inner = inner.replace(/^-|-$/g, '').trim();
//if we're in raw mode and we are not looking at an "endraw" tag, move along
if (parser.rawMode && (open + inner) !== '{%endraw') {
return index + 1;
}
var text = src.slice(lastEnd, index);
lastEnd = index + open.length + match.length;
if (trimLeading) text = trimLeft(text);
if (wsCollapseLeft) text = trimRight(text);
if (wsCollapseRight) trimLeading = true;
if (open === '{{{') {
//liquid-style: make {{{x}}} => {{x|safe}}
open = '{{';
inner += '|safe';
}
parser.textHandler(text);
parser.tokenHandler(open, inner);
});
var text = src.slice(lastEnd);
if (trimLeading) text = trimLeft(text);
this.textHandler(text);
};
Parser.prototype.textHandler = function(text) {
this.push('write(' + JSON.stringify(text) + ');');
};
Parser.prototype.tokenHandler = function(open, inner) {
var type = delimeters[open];
if (type === 'directive') {
this.compileTag(inner);
} else if (type === 'output') {
var extracted = this.extractEnt(inner, STRINGS, '@');
//replace || operators with ~
extracted.src = extracted.src.replace(/\|\|/g, '~').split('|');
//put back || operators
extracted.src = extracted.src.map(function(part) {
return part.split('~').join('||');
});
var parts = this.injectEnt(extracted, '@');
if (parts.length > 1) {
var filters = parts.slice(1).map(this.parseFilter.bind(this));
this.push('filter(' + this.parseExpr(parts[0]) + ',' + filters.join(',') + ');');
} else {
this.push('filter(' + this.parseExpr(parts[0]) + ');');
}
}
};
Parser.prototype.compileTag = function(str) {
var directive = str.split(' ')[0];
var handler = tagHandlers[directive];
if (!handler) {
throw new Error('Invalid tag: ' + str);
}
handler.call(this, str.slice(directive.length).trim());
};
Parser.prototype.parseFilter = function(src) {
src = src.trim();
var match = src.match(/[:(]/);
var i = match ? match.index : -1;
if (i < 0) return JSON.stringify([src]);
var name = src.slice(0, i);
var args = src.charAt(i) === ':' ? src.slice(i + 1) : src.slice(i + 1, -1);
args = this.parseExpr(args, {
terms: true
});
return '[' + JSON.stringify(name) + ',' + args + ']';
};
Parser.prototype.extractEnt = function(src, regex, placeholder) {
var subs = [],
isFunc = typeof placeholder == 'function';
src = src.replace(regex, function(str) {
var replacement = isFunc ? placeholder(str) : placeholder;
if (replacement) {
subs.push(str);
return replacement;
}
return str;
});
return {
src: src,
subs: subs
};
};
Parser.prototype.injectEnt = function(extracted, placeholder) {
var src = extracted.src,
subs = extracted.subs,
isArr = Array.isArray(src);
var arr = (isArr) ? src : [src];
var re = new RegExp('[' + placeholder + ']', 'g'),
i = 0;
arr.forEach(function(src, index) {
arr[index] = src.replace(re, function() {
return subs[i++];
});
});
return isArr ? arr : arr[0];
};
//replace complex literals without mistaking subscript notation with array literals
Parser.prototype.replaceComplex = function(s) {
var parsed = this.extractEnt(s, /i(\.i|\[[@#i]\])+/g, 'v');
parsed.src = parsed.src.replace(NON_PRIMITIVES, '~');
return this.injectEnt(parsed, 'v');
};
//parse expression containing literals (including objects/arrays) and variables (including dot and subscript notation)
//valid expressions: `a + 1 > b.c or c == null`, `a and b[1] != c`, `(a < b) or (c < d and e)`, 'a || [1]`
Parser.prototype.parseExpr = function(src, opts) {
opts = opts || {};
//extract string literals -> @
var parsed1 = this.extractEnt(src, STRINGS, '@');
//note: this will catch {not: 1} and a.is; could we replace temporarily and then check adjacent chars?
parsed1.src = parsed1.src.replace(EOPS, function(s, before, op, after) {
return (op in operators) ? before + operators[op] + after : s;
});
//sub out non-string literals (numbers/true/false/null) -> #
// the distinction is necessary because @ can be object identifiers, # cannot
var parsed2 = this.extractEnt(parsed1.src, IDENTS_AND_NUMS, function(s) {
return (s in constants || NUMBER.test(s)) ? '#' : null;
});
//sub out object/variable identifiers -> i
var parsed3 = this.extractEnt(parsed2.src, IDENTIFIERS, 'i');
//remove white-space
parsed3.src = parsed3.src.replace(/\s+/g, '');
//the rest of this is simply to boil the expression down and check validity
var simplified = parsed3.src;
//sub out complex literals (objects/arrays) -> ~
// the distinction is necessary because @ and # can be subscripts but ~ cannot
while (simplified !== (simplified = this.replaceComplex(simplified)));
//now @ represents strings, # represents other primitives and ~ represents non-primitives
//replace complex variables (those with dot/subscript accessors) -> v
while (simplified !== (simplified = simplified.replace(/i(\.i|\[[@#i]\])+/, 'v')));
//empty subscript or complex variables in subscript, are not permitted
simplified = simplified.replace(/[iv]\[v?\]/g, 'x');
//sub in "i" for @ and # and ~ and v (now "i" represents all literals, variables and identifiers)
simplified = simplified.replace(/[@#~v]/g, 'i');
//sub out operators
simplified = simplified.replace(OPERATORS, '%');
//allow 'not' unary operator
simplified = simplified.replace(/!+[i]/g, 'i');
var terms = opts.terms ? simplified.split(',') : [simplified];
terms.forEach(function(term) {
//simplify logical grouping
while (term !== (term = term.replace(/\(i(%i)*\)/g, 'i')));
if (!term.match(/^i(%i)*/)) {
throw new Error('Invalid expression: ' + src + " " + term);
}
});
parsed3.src = parsed3.src.replace(VARIABLES, this.parseVar.bind(this));
parsed2.src = this.injectEnt(parsed3, 'i');
parsed1.src = this.injectEnt(parsed2, '#');
return this.injectEnt(parsed1, '@');
};
Parser.prototype.parseVar = function(src) {
var args = Array.prototype.slice.call(arguments);
var str = args.pop(),
index = args.pop();
//quote bare object identifiers (might be a reserved word like {while: 1})
if (src === 'i' && str.charAt(index + 1) === ':') {
return '"i"';
}
var parts = ['"i"'];
src.replace(ACCESSOR, function(part) {
if (part === '.i') {
parts.push('"i"');
} else if (part === '[i]') {
parts.push('get("i")');
} else {
parts.push(part.slice(1, -1));
}
});
return 'get(' + parts.join(',') + ')';
};
//escapes a name to be used as a javascript identifier
Parser.prototype.escName = function(str) {
return str.replace(/\W/g, function(s) {
return '$' + s.charCodeAt(0).toString(16);
});
};
Parser.prototype.parseQuoted = function(str) {
if (str.charAt(0) === "'") {
str = str.slice(1, -1).replace(/\\.|"/, function(s) {
if (s === "\\'") return "'";
return s.charAt(0) === '\\' ? s : ('\\' + s);
});
str = '"' + str + '"';
}
//todo: try/catch or deal with invalid characters (linebreaks, control characters)
return JSON.parse(str);
};
//the context 'this' inside tagHandlers is the parser instance
var tagHandlers = {
'if': function(expr) {
this.push('if (' + this.parseExpr(expr) + ') {');
this.nest.unshift('if');
},
'else': function() {
if (this.nest[0] === 'for') {
this.push('}, function() {');
} else {
this.push('} else {');
}
},
'elseif': function(expr) {
this.push('} else if (' + this.parseExpr(expr) + ') {');
},
'endif': function() {
this.nest.shift();
this.push('}');
},
'for': function(str) {
var i = str.indexOf(' in ');
var name = str.slice(0, i).trim();
var expr = str.slice(i + 4).trim();
this.push('each(' + this.parseExpr(expr) + ',' + JSON.stringify(name) + ',function() {');
this.nest.unshift('for');
},
'endfor': function() {
this.nest.shift();
this.push('});');
},
'raw': function() {
this.rawMode = true;
},
'endraw': function() {
this.rawMode = false;
},
'set': function(stmt) {
var i = stmt.indexOf('=');
var name = stmt.slice(0, i).trim();
var expr = stmt.slice(i + 1).trim();
this.push('set(' + JSON.stringify(name) + ',' + this.parseExpr(expr) + ');');
},
'block': function(name) {
if (this.isParent) {
++this.parentBlocks;
var blockName = 'block_' + (this.escName(name) || this.parentBlocks);
this.push('block(typeof ' + blockName + ' == "function" ? ' + blockName + ' : function() {');
} else if (this.hasParent) {
this.isSilent = false;
++this.childBlocks;
blockName = 'block_' + (this.escName(name) || this.childBlocks);
this.push('function ' + blockName + '() {');
}
this.nest.unshift('block');
},
'endblock': function() {
this.nest.shift();
if (this.isParent) {
this.push('});');
} else if (this.hasParent) {
this.push('}');
this.isSilent = true;
}
},
'extends': function(name) {
name = this.parseQuoted(name);
var parentSrc = this.readTemplateFile(name);
this.isParent = true;
this.tokenize(parentSrc);
this.isParent = false;
this.hasParent = true;
//silence output until we enter a child block
this.isSilent = true;
},
'include': function(name) {
name = this.parseQuoted(name);
var incSrc = this.readTemplateFile(name);
this.isInclude = true;
this.tokenize(incSrc);
this.isInclude = false;
}
};
//liquid style
tagHandlers.assign = tagHandlers.set;
//python/django style
tagHandlers.elif = tagHandlers.elseif;
var getRuntime = function runtime(data, opts) {
var defaults = {
autoEscape: 'toJson'
};
var _toString = Object.prototype.toString;
var _hasOwnProperty = Object.prototype.hasOwnProperty;
var getKeys = Object.keys || function(obj) {
var keys = [];
for (var n in obj)
if (_hasOwnProperty.call(obj, n)) keys.push(n);
return keys;
};
var isArray = Array.isArray || function(obj) {
return _toString.call(obj) === '[object Array]';
};
var create = Object.create || function(obj) {
function F() {}
F.prototype = obj;
return new F();
};
var toString = function(val) {
if (val == null) return '';
return (typeof val.toString == 'function') ? val.toString() : _toString.call(val);
};
var extend = function(dest, src) {
var keys = getKeys(src);
for (var i = 0, len = keys.length; i < len; i++) {
var key = keys[i];
dest[key] = src[key];
}
return dest;
};
//get a value, lexically, starting in current context; a.b -> get("a","b")
var get = function() {
var val, n = arguments[0],
c = stack.length;
while (c--) {
val = stack[c][n];
if (typeof val != 'undefined') break;
}
for (var i = 1, len = arguments.length; i < len; i++) {
if (val == null) continue;
n = arguments[i];
val = (_hasOwnProperty.call(val, n)) ? val[n] : (typeof val._get == 'function' ? (val[n] = val._get(n)) : null);
}
return (val == null) ? '' : val;
};
var set = function(n, val) {
stack[stack.length - 1][n] = val;
};
var push = function(ctx) {
stack.push(ctx || {});
};
var pop = function() {
stack.pop();
};
var write = function(str) {
output.push(str);
};
var filter = function(val) {
for (var i = 1, len = arguments.length; i < len; i++) {
var arr = arguments[i],
name = arr[0],
filter = filters[name];
if (filter) {
arr[0] = val;
//now arr looks like [val, arg1, arg2]
val = filter.apply(data, arr);
} else {
throw new Error('Invalid filter: ' + name);
}
}
if (opts.autoEscape && name !== opts.autoEscape && name !== 'safe') {
//auto escape if not explicitly safe or already escaped
val = filters[opts.autoEscape].call(data, val);
}
output.push(val);
};
var each = function(obj, loopvar, fn1, fn2) {
if (obj == null) return;
var arr = isArray(obj) ? obj : getKeys(obj),
len = arr.length;
var ctx = {
loop: {
length: len,
first: arr[0],
last: arr[len - 1]
}
};
push(ctx);
for (var i = 0; i < len; i++) {
extend(ctx.loop, {
index: i + 1,
index0: i
});
fn1(ctx[loopvar] = arr[i]);
}
if (len === 0 && fn2) fn2();
pop();
};
var block = function(fn) {
push();
fn();
pop();
};
var render = function() {
return output.join('');
};
data = data || {};
opts = extend(defaults, opts || {});
var filters = extend({
html: function(val) {
return toString(val)
.split('&').join('&amp;')
.split('<').join('&lt;')
.split('>').join('&gt;')
.split('"').join('&quot;');
},
safe: function(val) {
return val;
},
toJson: function(val) {
if (typeof val === 'object') {
return JSON.stringify(val);
}
return toString(val);
}
}, opts.filters || {});
var stack = [create(data || {})],
output = [];
return {
get: get,
set: set,
push: push,
pop: pop,
write: write,
filter: filter,
each: each,
block: block,
render: render
};
};
var runtime;
jinja.compile = function(markup, opts) {
opts = opts || {};
var parser = new Parser();
parser.readTemplateFile = this.readTemplateFile;
var code = [];
code.push('function render($) {');
code.push('var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;');
code.push.apply(code, parser.parse(markup));
code.push('return $.render();');
code.push('}');
code = code.join('\n');
if (opts.runtime === false) {
var fn = new Function('data', 'options', 'return (' + code + ')(runtime(data, options))');
} else {
runtime = runtime || (runtime = getRuntime.toString());
fn = new Function('data', 'options', 'return (' + code + ')((' + runtime + ')(data, options))');
}
return {
render: fn
};
};
jinja.render = function(markup, data, opts) {
var tmpl = jinja.compile(markup);
return tmpl.render(data, opts);
};
jinja.templateFiles = [];
jinja.readTemplateFile = function(name) {
var templateFiles = this.templateFiles || [];
var templateFile = templateFiles[name];
if (templateFile == null) {
throw new Error('Template file not found: ' + name);
}
return templateFile;
};
/*!
* Helpers
*/
function trimLeft(str) {
return str.replace(LEADING_SPACE, '');
}
function trimRight(str) {
return str.replace(TRAILING_SPACE, '');
}
function matchAll(str, reg, fn) {
//copy as global
reg = new RegExp(reg.source, 'g' + (reg.ignoreCase ? 'i' : '') + (reg.multiline ? 'm' : ''));
var match;
while ((match = reg.exec(str))) {
var result = fn(match[0], match.index, str);
if (typeof result == 'number') {
reg.lastIndex = result;
}
}
}
}));
+360
View File
@@ -0,0 +1,360 @@
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object') {
// CommonJS
module.exports = exports = factory();
} else if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else {
// Global (browser)
globalThis.JSEncrypt = factory();
}
})(this, () => {
return /******/ (() => { // webpackBootstrap
/******/
var __webpack_modules__ = ({
/***/
"./lib/JSEncrypt.js":
/*!**************************!*\
!*** ./lib/JSEncrypt.js ***!
\**************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"JSEncrypt\": () => (/* binding */ JSEncrypt)\n/* harmony export */ });\n/* harmony import */ var _lib_jsbn_base64__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./lib/jsbn/base64 */ \"./lib/lib/jsbn/base64.js\");\n/* harmony import */ var _JSEncryptRSAKey__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./JSEncryptRSAKey */ \"./lib/JSEncryptRSAKey.js\");\n/* provided dependency */ var process = __webpack_require__(/*! process/browser */ \"./node_modules/process/browser.js\");\nvar _a;\n\n\nvar version = typeof process !== 'undefined'\n ? (_a = process.env) === null || _a === void 0 ? void 0 : \"3.3.2\"\n : undefined;\n/**\n *\n * @param {Object} [options = {}] - An object to customize JSEncrypt behaviour\n * possible parameters are:\n * - default_key_size {number} default: 1024 the key size in bit\n * - default_public_exponent {string} default: '010001' the hexadecimal representation of the public exponent\n * - log {boolean} default: false whether log warn/error or not\n * @constructor\n */\nvar JSEncrypt = /** @class */ (function () {\n function JSEncrypt(options) {\n if (options === void 0) { options = {}; }\n options = options || {};\n this.default_key_size = options.default_key_size\n ? parseInt(options.default_key_size, 10)\n : 1024;\n this.default_public_exponent = options.default_public_exponent || \"010001\"; // 65537 default openssl public exponent for rsa key type\n this.log = options.log || false;\n // The private and public key.\n this.key = null;\n }\n /**\n * Method to set the rsa key parameter (one method is enough to set both the public\n * and the private key, since the private key contains the public key paramenters)\n * Log a warning if logs are enabled\n * @param {Object|string} key the pem encoded string or an object (with or without header/footer)\n * @public\n */\n JSEncrypt.prototype.setKey = function (key) {\n if (this.log && this.key) {\n console.warn(\"A key was already set, overriding existing.\");\n }\n this.key = new _JSEncryptRSAKey__WEBPACK_IMPORTED_MODULE_1__.JSEncryptRSAKey(key);\n };\n /**\n * Proxy method for setKey, for api compatibility\n * @see setKey\n * @public\n */\n JSEncrypt.prototype.setPrivateKey = function (privkey) {\n // Create the key.\n this.setKey(privkey);\n };\n /**\n * Proxy method for setKey, for api compatibility\n * @see setKey\n * @public\n */\n JSEncrypt.prototype.setPublicKey = function (pubkey) {\n // Sets the public key.\n this.setKey(pubkey);\n };\n /**\n * Proxy method for RSAKey object's decrypt, decrypt the string using the private\n * components of the rsa key object. Note that if the object was not set will be created\n * on the fly (by the getKey method) using the parameters passed in the JSEncrypt constructor\n * @param {string} str base64 encoded crypted string to decrypt\n * @return {string} the decrypted string\n * @public\n */\n JSEncrypt.prototype.decrypt = function (str) {\n // Return the decrypted string.\n try {\n return this.getKey().decrypt((0,_lib_jsbn_base64__WEBPACK_IMPORTED_MODULE_0__.b64tohex)(str));\n }\n catch (ex) {\n return false;\n }\n };\n /**\n * Proxy method for RSAKey object's encrypt, encrypt the string using the public\n * components of the rsa key object. Note that if the object was not set will be created\n * on the fly (by the getKey method) using the parameters passed in the JSEncrypt constructor\n * @param {string} str the string to encrypt\n * @return {string} the encrypted string encoded in base64\n * @public\n */\n JSEncrypt.prototype.encrypt = function (str) {\n // Return the encrypted string.\n try {\n return (0,_lib_jsbn_base64__WEBPACK_IMPORTED_MODULE_0__.hex2b64)(this.getKey().encrypt(str));\n }\n catch (ex) {\n return false;\n }\n };\n /**\n * Proxy method for RSAKey object's sign.\n * @param {string} str the string to sign\n * @param {function} digestMethod hash method\n * @param {string} digestName the name of the hash algorithm\n * @return {string} the signature encoded in base64\n * @public\n */\n JSEncrypt.prototype.sign = function (str, digestMethod, digestName) {\n // return the RSA signature of 'str' in 'hex' format.\n try {\n return (0,_lib_jsbn_base64__WEBPACK_IMPORTED_MODULE_0__.hex2b64)(this.getKey().sign(str, digestMethod, digestName));\n }\n catch (ex) {\n return false;\n }\n };\n Line truncated
/***/
}),
/***/
"./lib/JSEncryptRSAKey.js":
/*!********************************!*\
!*** ./lib/JSEncryptRSAKey.js ***!
\********************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"JSEncryptRSAKey\": () => (/* binding */ JSEncryptRSAKey)\n/* harmony export */ });\n/* harmony import */ var _lib_jsbn_base64__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./lib/jsbn/base64 */ \"./lib/lib/jsbn/base64.js\");\n/* harmony import */ var _lib_asn1js_hex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lib/asn1js/hex */ \"./lib/lib/asn1js/hex.js\");\n/* harmony import */ var _lib_asn1js_base64__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./lib/asn1js/base64 */ \"./lib/lib/asn1js/base64.js\");\n/* harmony import */ var _lib_asn1js_asn1__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./lib/asn1js/asn1 */ \"./lib/lib/asn1js/asn1.js\");\n/* harmony import */ var _lib_jsbn_rsa__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./lib/jsbn/rsa */ \"./lib/lib/jsbn/rsa.js\");\n/* harmony import */ var _lib_jsbn_jsbn__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./lib/jsbn/jsbn */ \"./lib/lib/jsbn/jsbn.js\");\n/* harmony import */ var _lib_jsrsasign_asn1_1_0__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./lib/jsrsasign/asn1-1.0 */ \"./lib/lib/jsrsasign/asn1-1.0.js\");\nvar __extends = (undefined && undefined.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n\n\n\n\n\n\n\n/**\n * Create a new JSEncryptRSAKey that extends Tom Wu's RSA key object.\n * This object is just a decorator for parsing the key parameter\n * @param {string|Object} key - The key in string format, or an object containing\n * the parameters needed to build a RSAKey object.\n * @constructor\n */\nvar JSEncryptRSAKey = /** @class */ (function (_super) {\n __extends(JSEncryptRSAKey, _super);\n function JSEncryptRSAKey(key) {\n var _this = _super.call(this) || this;\n // Call the super constructor.\n // RSAKey.call(this);\n // If a key key was provided.\n if (key) {\n // If this is a string...\n if (typeof key === \"string\") {\n _this.parseKey(key);\n }\n else if (JSEncryptRSAKey.hasPrivateKeyProperty(key) ||\n JSEncryptRSAKey.hasPublicKeyProperty(key)) {\n // Set the values for the key.\n _this.parsePropertiesFrom(key);\n }\n }\n return _this;\n }\n /**\n * Method to parse a pem encoded string containing both a public or private key.\n * The method will translate the pem encoded string in a der encoded string and\n * will parse private key and public key parameters. This method accepts public key\n * in the rsaencryption pkcs #1 format (oid: 1.2.840.113549.1.1.1).\n *\n * @todo Check how many rsa formats use the same format of pkcs #1.\n *\n * The format is defined as:\n * PublicKeyInfo ::= SEQUENCE {\n * algorithm AlgorithmIdentifier,\n * PublicKey BIT STRING\n * }\n * Where AlgorithmIdentifier is:\n * AlgorithmIdentifier ::= SEQUENCE {\n * algorithm OBJECT IDENTIFIER, the OID of the enc algorithm\n * parameters ANY DEFINED BY algorithm OPTIONAL (NULL for PKCS #1)\n * }\n * and PublicKey is a SEQUENCE encapsulated in a BIT STRING\n * RSAPublicKey ::= SEQUENCE {\n * modulus INTEGER, -- n\n * publicExponent INTEGER -- e\n * }\n * it's possible to examine the structure of the keys obtained from openssl using\n * an asn.1 dumper as the one used here to parse the components: http://lapo.it/asn1js/\n * @argument {string} pem the pem encoded string, can include the BEGIN/END header/footer\n * @private\n */\n JSEncryptRSAKey.prototype.parseKey = function (pem) {\n try {\n var modulus = 0;\n var public_exponent = 0;\n var reHex = /^\\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\\s*)+$/;\n var der = reHex.test(pem) ? _lib_asn1js_hex__WEBPACK_IMPORTED_MODULE_1__.Hex.decode(pem) : _lib_asn1js_base64__WEBPACK_IMPORTED_MODULE_2__.Base64.unarmor(pem);\n var asn1 = _lib_asn1js_asn1__WEBPACK_IMPORTED_MODULE_3__.ASN1.decode(der);\n // Fixes a bug with OpenSSL Line truncated
/***/
}),
/***/
"./lib/index.js":
/*!**********************!*\
!*** ./lib/index.js ***!
\**********************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"JSEncrypt\": () => (/* reexport safe */ _JSEncrypt__WEBPACK_IMPORTED_MODULE_0__.JSEncrypt),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _JSEncrypt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./JSEncrypt */ \"./lib/JSEncrypt.js\");\n\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_JSEncrypt__WEBPACK_IMPORTED_MODULE_0__.JSEncrypt);\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/index.js?");
/***/
}),
/***/
"./lib/lib/asn1js/asn1.js":
/*!********************************!*\
!*** ./lib/lib/asn1js/asn1.js ***!
\********************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"ASN1\": () => (/* binding */ ASN1),\n/* harmony export */ \"ASN1Tag\": () => (/* binding */ ASN1Tag),\n/* harmony export */ \"Stream\": () => (/* binding */ Stream)\n/* harmony export */ });\n/* harmony import */ var _int10__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./int10 */ \"./lib/lib/asn1js/int10.js\");\n// ASN.1 JavaScript decoder\n// Copyright (c) 2008-2014 Lapo Luchini <lapo@lapo.it>\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */\n/*global oids */\n\nvar ellipsis = \"\\u2026\";\nvar reTimeS = /^(\\d\\d)(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])([01]\\d|2[0-3])(?:([0-5]\\d)(?:([0-5]\\d)(?:[.,](\\d{1,3}))?)?)?(Z|[-+](?:[0]\\d|1[0-2])([0-5]\\d)?)?$/;\nvar reTimeL = /^(\\d\\d\\d\\d)(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])([01]\\d|2[0-3])(?:([0-5]\\d)(?:([0-5]\\d)(?:[.,](\\d{1,3}))?)?)?(Z|[-+](?:[0]\\d|1[0-2])([0-5]\\d)?)?$/;\nfunction stringCut(str, len) {\n if (str.length > len) {\n str = str.substring(0, len) + ellipsis;\n }\n return str;\n}\nvar Stream = /** @class */ (function () {\n function Stream(enc, pos) {\n this.hexDigits = \"0123456789ABCDEF\";\n if (enc instanceof Stream) {\n this.enc = enc.enc;\n this.pos = enc.pos;\n }\n else {\n // enc should be an array or a binary string\n this.enc = enc;\n this.pos = pos;\n }\n }\n Stream.prototype.get = function (pos) {\n if (pos === undefined) {\n pos = this.pos++;\n }\n if (pos >= this.enc.length) {\n throw new Error(\"Requesting byte offset \".concat(pos, \" on a stream of length \").concat(this.enc.length));\n }\n return (\"string\" === typeof this.enc) ? this.enc.charCodeAt(pos) : this.enc[pos];\n };\n Stream.prototype.hexByte = function (b) {\n return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF);\n };\n Stream.prototype.hexDump = function (start, end, raw) {\n var s = \"\";\n for (var i = start; i < end; ++i) {\n s += this.hexByte(this.get(i));\n if (raw !== true) {\n switch (i & 0xF) {\n case 0x7:\n s += \" \";\n break;\n case 0xF:\n s += \"\\n\";\n break;\n default:\n s += \" \";\n }\n }\n }\n return s;\n };\n Stream.prototype.isASCII = function (start, end) {\n for (var i = start; i < end; ++i) {\n var c = this.get(i);\n if (c < 32 || c > 176) {\n return false;\n }\n }\n return true;\n };\n Stream.prototype.parseStringISO = function (start, end) {\n var s = \"\";\n for (var i = start; i < end; ++i) {\n s += String.fromCharCode(this.get(i));\n }\n return s;\n };\n Stream.prototype.parseStringUTF = function (start, end) {\n var s = \"\";\n for (var i = start; i < end;) {\n var c = this.get(i++);\n if (c < 128) {\n s += String.fromCharCode(c);\n }\n else if ((c > 191) && (c < 224)) {\n s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F));\n }\n else {\n s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F));\n }\n }\n return s;\n };\n Stream.prototype.parseStringBMP = function (start, end) {\n var str = \"\";\n var hi;\n var lo;\n for (var i = start; i < end;) {\n hi = this.get(i++);\n lo = this.get(i++);\n str += String.fromCharCode((hi << 8) | lo);\n }\n return str;\n };\n Stream.prototype.parseTime = function (start, end, shortYear) {\n var s = this.parseStringISO(start, end);\n var m = (shoLine truncated
/***/
}),
/***/
"./lib/lib/asn1js/base64.js":
/*!**********************************!*\
!*** ./lib/lib/asn1js/base64.js ***!
\**********************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Base64\": () => (/* binding */ Base64)\n/* harmony export */ });\n// Base64 JavaScript decoder\n// Copyright (c) 2008-2013 Lapo Luchini <lapo@lapo.it>\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */\nvar decoder;\nvar Base64 = {\n decode: function (a) {\n var i;\n if (decoder === undefined) {\n var b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n var ignore = \"= \\f\\n\\r\\t\\u00A0\\u2028\\u2029\";\n decoder = Object.create(null);\n for (i = 0; i < 64; ++i) {\n decoder[b64.charAt(i)] = i;\n }\n decoder['-'] = 62; //+\n decoder['_'] = 63; //-\n for (i = 0; i < ignore.length; ++i) {\n decoder[ignore.charAt(i)] = -1;\n }\n }\n var out = [];\n var bits = 0;\n var char_count = 0;\n for (i = 0; i < a.length; ++i) {\n var c = a.charAt(i);\n if (c == \"=\") {\n break;\n }\n c = decoder[c];\n if (c == -1) {\n continue;\n }\n if (c === undefined) {\n throw new Error(\"Illegal character at offset \" + i);\n }\n bits |= c;\n if (++char_count >= 4) {\n out[out.length] = (bits >> 16);\n out[out.length] = (bits >> 8) & 0xFF;\n out[out.length] = bits & 0xFF;\n bits = 0;\n char_count = 0;\n }\n else {\n bits <<= 6;\n }\n }\n switch (char_count) {\n case 1:\n throw new Error(\"Base64 encoding incomplete: at least 2 bits missing\");\n case 2:\n out[out.length] = (bits >> 10);\n break;\n case 3:\n out[out.length] = (bits >> 16);\n out[out.length] = (bits >> 8) & 0xFF;\n break;\n }\n return out;\n },\n re: /-----BEGIN [^-]+-----([A-Za-z0-9+\\/=\\s]+)-----END [^-]+-----|begin-base64[^\\n]+\\n([A-Za-z0-9+\\/=\\s]+)====/,\n unarmor: function (a) {\n var m = Base64.re.exec(a);\n if (m) {\n if (m[1]) {\n a = m[1];\n }\n else if (m[2]) {\n a = m[2];\n }\n else {\n throw new Error(\"RegExp out of sync\");\n }\n }\n return Base64.decode(a);\n }\n};\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/asn1js/base64.js?");
/***/
}),
/***/
"./lib/lib/asn1js/hex.js":
/*!*******************************!*\
!*** ./lib/lib/asn1js/hex.js ***!
\*******************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Hex\": () => (/* binding */ Hex)\n/* harmony export */ });\n// Hex JavaScript decoder\n// Copyright (c) 2008-2013 Lapo Luchini <lapo@lapo.it>\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */\nvar decoder;\nvar Hex = {\n decode: function (a) {\n var i;\n if (decoder === undefined) {\n var hex = \"0123456789ABCDEF\";\n var ignore = \" \\f\\n\\r\\t\\u00A0\\u2028\\u2029\";\n decoder = {};\n for (i = 0; i < 16; ++i) {\n decoder[hex.charAt(i)] = i;\n }\n hex = hex.toLowerCase();\n for (i = 10; i < 16; ++i) {\n decoder[hex.charAt(i)] = i;\n }\n for (i = 0; i < ignore.length; ++i) {\n decoder[ignore.charAt(i)] = -1;\n }\n }\n var out = [];\n var bits = 0;\n var char_count = 0;\n for (i = 0; i < a.length; ++i) {\n var c = a.charAt(i);\n if (c == \"=\") {\n break;\n }\n c = decoder[c];\n if (c == -1) {\n continue;\n }\n if (c === undefined) {\n throw new Error(\"Illegal character at offset \" + i);\n }\n bits |= c;\n if (++char_count >= 2) {\n out[out.length] = bits;\n bits = 0;\n char_count = 0;\n }\n else {\n bits <<= 4;\n }\n }\n if (char_count) {\n throw new Error(\"Hex encoding incomplete: 4 bits missing\");\n }\n return out;\n }\n};\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/asn1js/hex.js?");
/***/
}),
/***/
"./lib/lib/asn1js/int10.js":
/*!*********************************!*\
!*** ./lib/lib/asn1js/int10.js ***!
\*********************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Int10\": () => (/* binding */ Int10)\n/* harmony export */ });\n// Big integer base-10 printing library\n// Copyright (c) 2014 Lapo Luchini <lapo@lapo.it>\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */\nvar max = 10000000000000; // biggest integer that can still fit 2^53 when multiplied by 256\nvar Int10 = /** @class */ (function () {\n function Int10(value) {\n this.buf = [+value || 0];\n }\n Int10.prototype.mulAdd = function (m, c) {\n // assert(m <= 256)\n var b = this.buf;\n var l = b.length;\n var i;\n var t;\n for (i = 0; i < l; ++i) {\n t = b[i] * m + c;\n if (t < max) {\n c = 0;\n }\n else {\n c = 0 | (t / max);\n t -= c * max;\n }\n b[i] = t;\n }\n if (c > 0) {\n b[i] = c;\n }\n };\n Int10.prototype.sub = function (c) {\n // assert(m <= 256)\n var b = this.buf;\n var l = b.length;\n var i;\n var t;\n for (i = 0; i < l; ++i) {\n t = b[i] - c;\n if (t < 0) {\n t += max;\n c = 1;\n }\n else {\n c = 0;\n }\n b[i] = t;\n }\n while (b[b.length - 1] === 0) {\n b.pop();\n }\n };\n Int10.prototype.toString = function (base) {\n if ((base || 10) != 10) {\n throw new Error(\"only base 10 is supported\");\n }\n var b = this.buf;\n var s = b[b.length - 1].toString();\n for (var i = b.length - 2; i >= 0; --i) {\n s += (max + b[i]).toString().substring(1);\n }\n return s;\n };\n Int10.prototype.valueOf = function () {\n var b = this.buf;\n var v = 0;\n for (var i = b.length - 1; i >= 0; --i) {\n v = v * max + b[i];\n }\n return v;\n };\n Int10.prototype.simplify = function () {\n var b = this.buf;\n return (b.length == 1) ? b[0] : this;\n };\n return Int10;\n}());\n\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/asn1js/int10.js?");
/***/
}),
/***/
"./lib/lib/jsbn/base64.js":
/*!********************************!*\
!*** ./lib/lib/jsbn/base64.js ***!
\********************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"b64toBA\": () => (/* binding */ b64toBA),\n/* harmony export */ \"b64tohex\": () => (/* binding */ b64tohex),\n/* harmony export */ \"hex2b64\": () => (/* binding */ hex2b64)\n/* harmony export */ });\n/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ \"./lib/lib/jsbn/util.js\");\n\nvar b64map = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nvar b64pad = \"=\";\nfunction hex2b64(h) {\n var i;\n var c;\n var ret = \"\";\n for (i = 0; i + 3 <= h.length; i += 3) {\n c = parseInt(h.substring(i, i + 3), 16);\n ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);\n }\n if (i + 1 == h.length) {\n c = parseInt(h.substring(i, i + 1), 16);\n ret += b64map.charAt(c << 2);\n }\n else if (i + 2 == h.length) {\n c = parseInt(h.substring(i, i + 2), 16);\n ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);\n }\n while ((ret.length & 3) > 0) {\n ret += b64pad;\n }\n return ret;\n}\n// convert a base64 string to hex\nfunction b64tohex(s) {\n var ret = \"\";\n var i;\n var k = 0; // b64 state, 0-3\n var slop = 0;\n for (i = 0; i < s.length; ++i) {\n if (s.charAt(i) == b64pad) {\n break;\n }\n var v = b64map.indexOf(s.charAt(i));\n if (v < 0) {\n continue;\n }\n if (k == 0) {\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(v >> 2);\n slop = v & 3;\n k = 1;\n }\n else if (k == 1) {\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)((slop << 2) | (v >> 4));\n slop = v & 0xf;\n k = 2;\n }\n else if (k == 2) {\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(slop);\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(v >> 2);\n slop = v & 3;\n k = 3;\n }\n else {\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)((slop << 2) | (v >> 4));\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(v & 0xf);\n k = 0;\n }\n }\n if (k == 1) {\n ret += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(slop << 2);\n }\n return ret;\n}\n// convert a base64 string to a byte/number array\nfunction b64toBA(s) {\n // piggyback on b64tohex for now, optimize later\n var h = b64tohex(s);\n var i;\n var a = [];\n for (i = 0; 2 * i < h.length; ++i) {\n a[i] = parseInt(h.substring(2 * i, 2 * i + 2), 16);\n }\n return a;\n}\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/jsbn/base64.js?");
/***/
}),
/***/
"./lib/lib/jsbn/jsbn.js":
/*!******************************!*\
!*** ./lib/lib/jsbn/jsbn.js ***!
\******************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"BigInteger\": () => (/* binding */ BigInteger),\n/* harmony export */ \"intAt\": () => (/* binding */ intAt),\n/* harmony export */ \"nbi\": () => (/* binding */ nbi),\n/* harmony export */ \"nbits\": () => (/* binding */ nbits),\n/* harmony export */ \"nbv\": () => (/* binding */ nbv),\n/* harmony export */ \"parseBigInt\": () => (/* binding */ parseBigInt)\n/* harmony export */ });\n/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ \"./lib/lib/jsbn/util.js\");\n// Copyright (c) 2005 Tom Wu\n// All Rights Reserved.\n// See \"LICENSE\" for details.\n// Basic JavaScript BN library - subset useful for RSA encryption.\n\n// Bits per digit\nvar dbits;\n// JavaScript engine analysis\nvar canary = 0xdeadbeefcafe;\nvar j_lm = ((canary & 0xffffff) == 0xefcafe);\n//#region\nvar lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];\nvar lplim = (1 << 26) / lowprimes[lowprimes.length - 1];\n//#endregion\n// (public) Constructor\nvar BigInteger = /** @class */ (function () {\n function BigInteger(a, b, c) {\n if (a != null) {\n if (\"number\" == typeof a) {\n this.fromNumber(a, b, c);\n }\n else if (b == null && \"string\" != typeof a) {\n this.fromString(a, 256);\n }\n else {\n this.fromString(a, b);\n }\n }\n }\n //#region PUBLIC\n // BigInteger.prototype.toString = bnToString;\n // (public) return string representation in given radix\n BigInteger.prototype.toString = function (b) {\n if (this.s < 0) {\n return \"-\" + this.negate().toString(b);\n }\n var k;\n if (b == 16) {\n k = 4;\n }\n else if (b == 8) {\n k = 3;\n }\n else if (b == 2) {\n k = 1;\n }\n else if (b == 32) {\n k = 5;\n }\n else if (b == 4) {\n k = 2;\n }\n else {\n return this.toRadix(b);\n }\n var km = (1 << k) - 1;\n var d;\n var m = false;\n var r = \"\";\n var i = this.t;\n var p = this.DB - (i * this.DB) % k;\n if (i-- > 0) {\n if (p < this.DB && (d = this[i] >> p) > 0) {\n m = true;\n r = (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(d);\n }\n while (i >= 0) {\n if (p < k) {\n d = (this[i] & ((1 << p) - 1)) << (k - p);\n d |= this[--i] >> (p += this.DB - k);\n }\n else {\n d = (this[i] >> (p -= k)) & km;\n if (p <= 0) {\n p += this.DB;\n --i;\n }\n }\n if (d > 0) {\n m = true;\n }\n if (m) {\n r += (0,_util__WEBPACK_IMPORTED_MODULE_0__.int2char)(d);\n }\n }\n }\n return m ? r : \"0\";\n };\n // BigInteger.prototype.negate = bnNegate;\n // (public) -this\n BigInteger.prototype.negate = function () {\n var r = nbi();\n BigInteger.ZERO.subTo(this, r);\n return r;\n };\n // BigInteger.prototype.abs = bnAbs;\n // (public) |this|\n BigInteger.prototype.abs = function () {\n return (this.s < 0) ? this.negate() : this;\n };\n // BigInteger.prototype.compareTo = bnCompareTo;\n // (public) return + if this > a, - if this < a, 0 if equal\n BigInteger.prototype.compareTo = function (a) {\n var r = this.s - a.s;\n if (r != 0) {\n return r;\n }\n var i = this.t;\n r = i - a.t;\n if (r != 0) {\n return (this.s < 0) ? -r : r;\n }\n while (--i >= 0) {\n if ((r = this[i] - a[i]) != 0) {\n return r;\n }\n }\n return 0;\n };\n // BigInteger.protot Line truncated
/***/
}),
/***/
"./lib/lib/jsbn/prng4.js":
/*!*******************************!*\
!*** ./lib/lib/jsbn/prng4.js ***!
\*******************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Arcfour\": () => (/* binding */ Arcfour),\n/* harmony export */ \"prng_newstate\": () => (/* binding */ prng_newstate),\n/* harmony export */ \"rng_psize\": () => (/* binding */ rng_psize)\n/* harmony export */ });\n// prng4.js - uses Arcfour as a PRNG\nvar Arcfour = /** @class */ (function () {\n function Arcfour() {\n this.i = 0;\n this.j = 0;\n this.S = [];\n }\n // Arcfour.prototype.init = ARC4init;\n // Initialize arcfour context from key, an array of ints, each from [0..255]\n Arcfour.prototype.init = function (key) {\n var i;\n var j;\n var t;\n for (i = 0; i < 256; ++i) {\n this.S[i] = i;\n }\n j = 0;\n for (i = 0; i < 256; ++i) {\n j = (j + this.S[i] + key[i % key.length]) & 255;\n t = this.S[i];\n this.S[i] = this.S[j];\n this.S[j] = t;\n }\n this.i = 0;\n this.j = 0;\n };\n // Arcfour.prototype.next = ARC4next;\n Arcfour.prototype.next = function () {\n var t;\n this.i = (this.i + 1) & 255;\n this.j = (this.j + this.S[this.i]) & 255;\n t = this.S[this.i];\n this.S[this.i] = this.S[this.j];\n this.S[this.j] = t;\n return this.S[(t + this.S[this.i]) & 255];\n };\n return Arcfour;\n}());\n\n// Plug in your RNG constructor here\nfunction prng_newstate() {\n return new Arcfour();\n}\n// Pool size must be a multiple of 4 and greater than 32.\n// An array of bytes the size of the pool will be passed to init()\nvar rng_psize = 256;\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/jsbn/prng4.js?");
/***/
}),
/***/
"./lib/lib/jsbn/rng.js":
/*!*****************************!*\
!*** ./lib/lib/jsbn/rng.js ***!
\*****************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"SecureRandom\": () => (/* binding */ SecureRandom)\n/* harmony export */ });\n/* harmony import */ var _prng4__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./prng4 */ \"./lib/lib/jsbn/prng4.js\");\n// Random number generator - requires a PRNG backend, e.g. prng4.js\n\nvar rng_state;\nvar rng_pool = null;\nvar rng_pptr;\n// Initialize the pool with junk if needed.\nif (rng_pool == null) {\n rng_pool = [];\n rng_pptr = 0;\n var t = void 0;\n if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {\n // Extract entropy (2048 bits) from RNG if available\n var z = new Uint32Array(256);\n window.crypto.getRandomValues(z);\n for (t = 0; t < z.length; ++t) {\n rng_pool[rng_pptr++] = z[t] & 255;\n }\n }\n // Use mouse events for entropy, if we do not have enough entropy by the time\n // we need it, entropy will be generated by Math.random.\n var count = 0;\n var onMouseMoveListener_1 = function (ev) {\n count = count || 0;\n if (count >= 256 || rng_pptr >= _prng4__WEBPACK_IMPORTED_MODULE_0__.rng_psize) {\n if (window.removeEventListener) {\n window.removeEventListener(\"mousemove\", onMouseMoveListener_1, false);\n }\n else if (window.detachEvent) {\n window.detachEvent(\"onmousemove\", onMouseMoveListener_1);\n }\n return;\n }\n try {\n var mouseCoordinates = ev.x + ev.y;\n rng_pool[rng_pptr++] = mouseCoordinates & 255;\n count += 1;\n }\n catch (e) {\n // Sometimes Firefox will deny permission to access event properties for some reason. Ignore.\n }\n };\n if (typeof window !== 'undefined') {\n if (window.addEventListener) {\n window.addEventListener(\"mousemove\", onMouseMoveListener_1, false);\n }\n else if (window.attachEvent) {\n window.attachEvent(\"onmousemove\", onMouseMoveListener_1);\n }\n }\n}\nfunction rng_get_byte() {\n if (rng_state == null) {\n rng_state = (0,_prng4__WEBPACK_IMPORTED_MODULE_0__.prng_newstate)();\n // At this point, we may not have collected enough entropy. If not, fall back to Math.random\n while (rng_pptr < _prng4__WEBPACK_IMPORTED_MODULE_0__.rng_psize) {\n var random = Math.floor(65536 * Math.random());\n rng_pool[rng_pptr++] = random & 255;\n }\n rng_state.init(rng_pool);\n for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) {\n rng_pool[rng_pptr] = 0;\n }\n rng_pptr = 0;\n }\n // TODO: allow reseeding after first request\n return rng_state.next();\n}\nvar SecureRandom = /** @class */ (function () {\n function SecureRandom() {\n }\n SecureRandom.prototype.nextBytes = function (ba) {\n for (var i = 0; i < ba.length; ++i) {\n ba[i] = rng_get_byte();\n }\n };\n return SecureRandom;\n}());\n\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/jsbn/rng.js?");
/***/
}),
/***/
"./lib/lib/jsbn/rsa.js":
/*!*****************************!*\
!*** ./lib/lib/jsbn/rsa.js ***!
\*****************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"RSAKey\": () => (/* binding */ RSAKey)\n/* harmony export */ });\n/* harmony import */ var _jsbn__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./jsbn */ \"./lib/lib/jsbn/jsbn.js\");\n/* harmony import */ var _rng__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rng */ \"./lib/lib/jsbn/rng.js\");\n// Depends on jsbn.js and rng.js\n// Version 1.1: support utf-8 encoding in pkcs1pad2\n// convert a (hex) string to a bignum object\n\n\n// function linebrk(s,n) {\n// var ret = \"\";\n// var i = 0;\n// while(i + n < s.length) {\n// ret += s.substring(i,i+n) + \"\\n\";\n// i += n;\n// }\n// return ret + s.substring(i,s.length);\n// }\n// function byte2Hex(b) {\n// if(b < 0x10)\n// return \"0\" + b.toString(16);\n// else\n// return b.toString(16);\n// }\nfunction pkcs1pad1(s, n) {\n if (n < s.length + 22) {\n console.error(\"Message too long for RSA\");\n return null;\n }\n var len = n - s.length - 6;\n var filler = \"\";\n for (var f = 0; f < len; f += 2) {\n filler += \"ff\";\n }\n var m = \"0001\" + filler + \"00\" + s;\n return (0,_jsbn__WEBPACK_IMPORTED_MODULE_0__.parseBigInt)(m, 16);\n}\n// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint\nfunction pkcs1pad2(s, n) {\n if (n < s.length + 11) { // TODO: fix for utf-8\n console.error(\"Message too long for RSA\");\n return null;\n }\n var ba = [];\n var i = s.length - 1;\n while (i >= 0 && n > 0) {\n var c = s.charCodeAt(i--);\n if (c < 128) { // encode using utf-8\n ba[--n] = c;\n }\n else if ((c > 127) && (c < 2048)) {\n ba[--n] = (c & 63) | 128;\n ba[--n] = (c >> 6) | 192;\n }\n else {\n ba[--n] = (c & 63) | 128;\n ba[--n] = ((c >> 6) & 63) | 128;\n ba[--n] = (c >> 12) | 224;\n }\n }\n ba[--n] = 0;\n var rng = new _rng__WEBPACK_IMPORTED_MODULE_1__.SecureRandom();\n var x = [];\n while (n > 2) { // random non-zero pad\n x[0] = 0;\n while (x[0] == 0) {\n rng.nextBytes(x);\n }\n ba[--n] = x[0];\n }\n ba[--n] = 2;\n ba[--n] = 0;\n return new _jsbn__WEBPACK_IMPORTED_MODULE_0__.BigInteger(ba);\n}\n// \"empty\" RSA key constructor\nvar RSAKey = /** @class */ (function () {\n function RSAKey() {\n this.n = null;\n this.e = 0;\n this.d = null;\n this.p = null;\n this.q = null;\n this.dmp1 = null;\n this.dmq1 = null;\n this.coeff = null;\n }\n //#region PROTECTED\n // protected\n // RSAKey.prototype.doPublic = RSADoPublic;\n // Perform raw public operation on \"x\": return x^e (mod n)\n RSAKey.prototype.doPublic = function (x) {\n return x.modPowInt(this.e, this.n);\n };\n // RSAKey.prototype.doPrivate = RSADoPrivate;\n // Perform raw private operation on \"x\": return x^d (mod n)\n RSAKey.prototype.doPrivate = function (x) {\n if (this.p == null || this.q == null) {\n return x.modPow(this.d, this.n);\n }\n // TODO: re-calculate any missing CRT params\n var xp = x.mod(this.p).modPow(this.dmp1, this.p);\n var xq = x.mod(this.q).modPow(this.dmq1, this.q);\n while (xp.compareTo(xq) < 0) {\n xp = xp.add(this.p);\n }\n return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);\n };\n //#endregion PROTECTED\n //#region PUBLIC\n // RSAKey.prototype.setPublic = RSASetPublic;\n // Set the public key fields N and e from hex strings\n RSAKey.prototype.setPublic = function (N, E) {\n if (N != null && E != null && N.length > 0 && E.length > 0) {\n this.n = (0,_jsbn__WEBPACK_IMPORTED_MODULE_0__.parseBigInt)(N, 16);\n this.e = parseInt(E, 16);\n }\n else {\n console.error(\"Invalid RSA public key\");\n }\n };\n // RSAKey.prototype.encrypt = RSAEncrypt;\n // Return the PKCS#1 RSA encryption of \"text\" as an even-length hex string\n RSAKey.prototype.encrypt = function (text) {\n var maxLength = (this.n.bitLength() + 7) >> 3;\n var m = pkcs1pad2(text, maxLength);\n if (m == null) {\n return null;\n }\n var c = this.doPublic(m);\n if (c == null) {\n return null;\n }\n var h = c.toString(16);\n var length = h.length;\n // fix zero before result\n for (var i = 0; i < maxLength * 2 - length; i++) {\n h = \"0\" + h;\n }\n return h;\n };\n // RSAKey.prototype.setPrivate = RSASetPrivate;\n // Set the private key fields N, e, and d from hex strings\n RSAKey.prototype.setPrivate = function (N, E, Line truncated
/***/
}),
/***/
"./lib/lib/jsbn/util.js":
/*!******************************!*\
!*** ./lib/lib/jsbn/util.js ***!
\******************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"cbit\": () => (/* binding */ cbit),\n/* harmony export */ \"int2char\": () => (/* binding */ int2char),\n/* harmony export */ \"lbit\": () => (/* binding */ lbit),\n/* harmony export */ \"op_and\": () => (/* binding */ op_and),\n/* harmony export */ \"op_andnot\": () => (/* binding */ op_andnot),\n/* harmony export */ \"op_or\": () => (/* binding */ op_or),\n/* harmony export */ \"op_xor\": () => (/* binding */ op_xor)\n/* harmony export */ });\nvar BI_RM = \"0123456789abcdefghijklmnopqrstuvwxyz\";\nfunction int2char(n) {\n return BI_RM.charAt(n);\n}\n//#region BIT_OPERATIONS\n// (public) this & a\nfunction op_and(x, y) {\n return x & y;\n}\n// (public) this | a\nfunction op_or(x, y) {\n return x | y;\n}\n// (public) this ^ a\nfunction op_xor(x, y) {\n return x ^ y;\n}\n// (public) this & ~a\nfunction op_andnot(x, y) {\n return x & ~y;\n}\n// return index of lowest 1-bit in x, x < 2^31\nfunction lbit(x) {\n if (x == 0) {\n return -1;\n }\n var r = 0;\n if ((x & 0xffff) == 0) {\n x >>= 16;\n r += 16;\n }\n if ((x & 0xff) == 0) {\n x >>= 8;\n r += 8;\n }\n if ((x & 0xf) == 0) {\n x >>= 4;\n r += 4;\n }\n if ((x & 3) == 0) {\n x >>= 2;\n r += 2;\n }\n if ((x & 1) == 0) {\n ++r;\n }\n return r;\n}\n// return number of 1 bits in x\nfunction cbit(x) {\n var r = 0;\n while (x != 0) {\n x &= x - 1;\n ++r;\n }\n return r;\n}\n//#endregion BIT_OPERATIONS\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/jsbn/util.js?");
/***/
}),
/***/
"./lib/lib/jsrsasign/asn1-1.0.js":
/*!***************************************!*\
!*** ./lib/lib/jsrsasign/asn1-1.0.js ***!
\***************************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"KJUR\": () => (/* binding */ KJUR)\n/* harmony export */ });\n/* harmony import */ var _jsbn_jsbn__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../jsbn/jsbn */ \"./lib/lib/jsbn/jsbn.js\");\n/* harmony import */ var _yahoo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./yahoo */ \"./lib/lib/jsrsasign/yahoo.js\");\n/* asn1-1.0.13.js (c) 2013-2017 Kenji Urushima | kjur.github.com/jsrsasign/license\n */\n/*\n * asn1.js - ASN.1 DER encoder classes\n *\n * Copyright (c) 2013-2017 Kenji Urushima (kenji.urushima@gmail.com)\n *\n * This software is licensed under the terms of the MIT License.\n * https://kjur.github.io/jsrsasign/license\n *\n * The above copyright and license notice shall be\n * included in all copies or substantial portions of the Software.\n */\n\n\n/**\n * @fileOverview\n * @name asn1-1.0.js\n * @author Kenji Urushima kenji.urushima@gmail.com\n * @version asn1 1.0.13 (2017-Jun-02)\n * @since jsrsasign 2.1\n * @license <a href=\"https://kjur.github.io/jsrsasign/license/\">MIT License</a>\n */\n/**\n * kjur's class library name space\n * <p>\n * This name space provides following name spaces:\n * <ul>\n * <li>{@link KJUR.asn1} - ASN.1 primitive hexadecimal encoder</li>\n * <li>{@link KJUR.asn1.x509} - ASN.1 structure for X.509 certificate and CRL</li>\n * <li>{@link KJUR.crypto} - Java Cryptographic Extension(JCE) style MessageDigest/Signature\n * class and utilities</li>\n * </ul>\n * </p>\n * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.\n * @name KJUR\n * @namespace kjur's class library name space\n */\nvar KJUR = {};\n/**\n * kjur's ASN.1 class library name space\n * <p>\n * This is ITU-T X.690 ASN.1 DER encoder class library and\n * class structure and methods is very similar to\n * org.bouncycastle.asn1 package of\n * well known BouncyCaslte Cryptography Library.\n * <h4>PROVIDING ASN.1 PRIMITIVES</h4>\n * Here are ASN.1 DER primitive classes.\n * <ul>\n * <li>0x01 {@link KJUR.asn1.DERBoolean}</li>\n * <li>0x02 {@link KJUR.asn1.DERInteger}</li>\n * <li>0x03 {@link KJUR.asn1.DERBitString}</li>\n * <li>0x04 {@link KJUR.asn1.DEROctetString}</li>\n * <li>0x05 {@link KJUR.asn1.DERNull}</li>\n * <li>0x06 {@link KJUR.asn1.DERObjectIdentifier}</li>\n * <li>0x0a {@link KJUR.asn1.DEREnumerated}</li>\n * <li>0x0c {@link KJUR.asn1.DERUTF8String}</li>\n * <li>0x12 {@link KJUR.asn1.DERNumericString}</li>\n * <li>0x13 {@link KJUR.asn1.DERPrintableString}</li>\n * <li>0x14 {@link KJUR.asn1.DERTeletexString}</li>\n * <li>0x16 {@link KJUR.asn1.DERIA5String}</li>\n * <li>0x17 {@link KJUR.asn1.DERUTCTime}</li>\n * <li>0x18 {@link KJUR.asn1.DERGeneralizedTime}</li>\n * <li>0x30 {@link KJUR.asn1.DERSequence}</li>\n * <li>0x31 {@link KJUR.asn1.DERSet}</li>\n * </ul>\n * <h4>OTHER ASN.1 CLASSES</h4>\n * <ul>\n * <li>{@link KJUR.asn1.ASN1Object}</li>\n * <li>{@link KJUR.asn1.DERAbstractString}</li>\n * <li>{@link KJUR.asn1.DERAbstractTime}</li>\n * <li>{@link KJUR.asn1.DERAbstractStructured}</li>\n * <li>{@link KJUR.asn1.DERTaggedObject}</li>\n * </ul>\n * <h4>SUB NAME SPACES</h4>\n * <ul>\n * <li>{@link KJUR.asn1.cades} - CAdES long term signature format</li>\n * <li>{@link KJUR.asn1.cms} - Cryptographic Message Syntax</li>\n * <li>{@link KJUR.asn1.csr} - Certificate Signing Request (CSR/PKCS#10)</li>\n * <li>{@link KJUR.asn1.tsp} - RFC 3161 Timestamping Protocol Format</li>\n * <li>{@link KJUR.asn1.x509} - RFC 5280 X.509 certificate and CRL</li>\n * </ul>\n * </p>\n * NOTE: Please ignore method summary and document of this namespace.\n * This caused by a bug of jsdoc2.\n * @name KJUR.asn1\n * @namespace\n */\nif (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1)\n KJUR.asn1 = {};\n/**\n * ASN1 utilities class\n * @name KJUR.asn1.ASN1Util\n * @class ASN1 utilities class\n * @since asn1 1.0.2\n */\nKJUR.asn1.ASN1Util = new function () {\n this.integerToByteHex = function (i) {\n var h = i.toString(16);\n if ((h.length % 2) == 1)\n h = '0' + h;\n return h;\n };\n this.bigIntToMinTwosComplementsHex = function (bigIntegerValue) {\n var h = bigIntegerValue.toString(16);\n if (h.substr(0, 1) != '-') {\n if (h.length % 2 == 1) {\n h = '0' + h;\n }\n else {\n if (!h.match(/^[0-7]/)) {\n h = '00' + h;\n }\n }\n }\n else {\n var hPos = h.substr(1);\n var xorLen = hPos.length;\n if (xorLen % 2 == 1) {\n xorLen += 1;\n }\n else {\n if (!h.match(/^[0-7]/)) {\n xorLen += 2;\n }\n }\n var hMask = '';\n for (var i = 0; i < xorLen; i++) {\n hMask += 'f';\n Line truncated
/***/
}),
/***/
"./lib/lib/jsrsasign/yahoo.js":
/*!************************************!*\
!*** ./lib/lib/jsrsasign/yahoo.js ***!
\************************************/
/***/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"YAHOO\": () => (/* binding */ YAHOO)\n/* harmony export */ });\n/*!\nCopyright (c) 2011, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 2.9.0\n*/\nvar YAHOO = {};\nYAHOO.lang = {\n /**\n * Utility to set up the prototype, constructor and superclass properties to\n * support an inheritance strategy that can chain constructors and methods.\n * Static members will not be inherited.\n *\n * @method extend\n * @static\n * @param {Function} subc the object to modify\n * @param {Function} superc the object to inherit\n * @param {Object} overrides additional properties/methods to add to the\n * subclass prototype. These will override the\n * matching items obtained from the superclass\n * if present.\n */\n extend: function (subc, superc, overrides) {\n if (!superc || !subc) {\n throw new Error(\"YAHOO.lang.extend failed, please check that \" +\n \"all dependencies are included.\");\n }\n var F = function () { };\n F.prototype = superc.prototype;\n subc.prototype = new F();\n subc.prototype.constructor = subc;\n subc.superclass = superc.prototype;\n if (superc.prototype.constructor == Object.prototype.constructor) {\n superc.prototype.constructor = superc;\n }\n if (overrides) {\n var i;\n for (i in overrides) {\n subc.prototype[i] = overrides[i];\n }\n /*\n * IE will not enumerate native functions in a derived object even if the\n * function was overridden. This is a workaround for specific functions\n * we care about on the Object prototype.\n * @property _IEEnumFix\n * @param {Function} r the object to receive the augmentation\n * @param {Function} s the object that supplies the properties to augment\n * @static\n * @private\n */\n var _IEEnumFix = function () { }, ADD = [\"toString\", \"valueOf\"];\n try {\n if (/MSIE/.test(navigator.userAgent)) {\n _IEEnumFix = function (r, s) {\n for (i = 0; i < ADD.length; i = i + 1) {\n var fname = ADD[i], f = s[fname];\n if (typeof f === 'function' && f != Object.prototype[fname]) {\n r[fname] = f;\n }\n }\n };\n }\n }\n catch (ex) { }\n ;\n _IEEnumFix(subc.prototype, overrides);\n }\n }\n};\n\n\n//# sourceURL=webpack://JSEncrypt/./lib/lib/jsrsasign/yahoo.js?");
/***/
}),
/***/
"./node_modules/process/browser.js":
/*!*****************************************!*\
!*** ./node_modules/process/browser.js ***!
\*****************************************/
/***/
((module) => {
eval("// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess Line truncated
/***/
})
/******/
});
/************************************************************************/
/******/ // The module cache
/******/
var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/
function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/
var cachedModule = __webpack_module_cache__[moduleId];
/******/
if (cachedModule !== undefined) {
/******/
return cachedModule.exports;
/******/
}
/******/ // Create a new module (and put it into the cache)
/******/
var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/
exports: {}
/******/
};
/******/
/******/ // Execute the module function
/******/
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/
return module.exports;
/******/
}
/******/
/************************************************************************/
/******/
/* webpack/runtime/define property getters */
/******/
(() => {
/******/ // define getter functions for harmony exports
/******/
__webpack_require__.d = (exports, definition) => {
/******/
for (var key in definition) {
/******/
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key]
});
/******/
}
/******/
}
/******/
};
/******/
})();
/******/
/******/
/* webpack/runtime/hasOwnProperty shorthand */
/******/
(() => {
/******/
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/
})();
/******/
/******/
/* webpack/runtime/make namespace object */
/******/
(() => {
/******/ // define __esModule on exports
/******/
__webpack_require__.r = (exports) => {
/******/
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/
Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module'
});
/******/
}
/******/
Object.defineProperty(exports, '__esModule', {
value: true
});
/******/
};
/******/
})();
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval devtool is used.
/******/
var __webpack_exports__ = __webpack_require__("./lib/index.js");
/******/
__webpack_exports__ = __webpack_exports__["default"];
/******/
/******/
return __webpack_exports__;
/******/
})();
});
+1809
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1439
View File
File diff suppressed because it is too large. Load diff
+552
View File
@@ -0,0 +1,552 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 精彩一瞬间 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import urlparse
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import hashlib
import base64
import json
import time
import hmac
import sys
import re
import os
sys.path.append('..')
xurl = "https://www.wasu.cn"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
headerz = {
'accept': '*/*',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'no-cache',
'origin': xurl,
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': xurl,
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
}
class Spider(Spider):
def getName(self):
return "精彩"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeVideoContent(self):
pass
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "961", "type_name": "电影"},
{"type_id": "962", "type_name": "剧集"},
{"type_id": "963", "type_name": "少儿"},
{"type_id": "965", "type_name": "栏目"},
{"type_id": "966", "type_name": "新闻"}],
"list": [],
"filters": {"961": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "港台", "v": "港台"},
{"n": "欧美", "v": "欧美"},
{"n": "日韩", "v": "日韩"},
{"n": "泰国", "v": "泰国"},
{"n": "其他", "v": "其他"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "动作", "v": "动作"},
{"n": "科幻", "v": "科幻"},
{"n": "惊悚", "v": "惊悚"},
{"n": "冒险", "v": "冒险"},
{"n": "剧情", "v": "剧情"},
{"n": "励志", "v": "励志"},
{"n": "爱情", "v": "爱情"},
{"n": "喜剧", "v": "喜剧"},
{"n": "家庭", "v": "家庭"},
{"n": "历史", "v": "历史"},
{"n": "魔幻", "v": "魔幻"},
{"n": "恐怖", "v": "恐怖"},
{"n": "战争", "v": "战争"},
{"n": "武侠", "v": "武侠"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2026", "v": "2026"},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"962": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "港台", "v": "港台"},
{"n": "日韩", "v": "日韩"},
{"n": "欧美", "v": "欧美"},
{"n": "泰国", "v": "泰国"},
{"n": "其他", "v": "其他"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "都市", "v": "都市"},
{"n": "爱情", "v": "爱情"},
{"n": "短剧", "v": "短剧"}, {"n": "战争", "v": "战争"},
{"n": "家庭", "v": "家庭"},
{"n": "悬疑", "v": "悬疑"},
{"n": "古装", "v": "古装"}, {"n": "谍战", "v": "谍战"},
{"n": "喜剧", "v": "喜剧"},
{"n": "农村", "v": "农村"},
{"n": "刑侦", "v": "刑侦"},
{"n": "武侠", "v": "武侠"},
{"n": "历史", "v": "历史"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2026", "v": "2026"},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"963": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "日韩", "v": "日韩"},
{"n": "欧美", "v": "欧美"},
{"n": "港台", "v": "港台"},
{"n": "其他", "v": "其他"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "动作", "v": "动作"},
{"n": "冒险", "v": "冒险"},
{"n": "益智", "v": "益智"},
{"n": "亲子", "v": "亲子"},
{"n": "热血", "v": "热血"},
{"n": "剧情", "v": "剧情"},
{"n": "魔幻", "v": "魔幻"},
{"n": "励志", "v": "励志"},
{"n": "机战", "v": "机战"},
{"n": "搞笑", "v": "搞笑"},
{"n": "科幻", "v": "科幻"},
{"n": "治愈", "v": "治愈"},
{"n": "儿歌", "v": "儿歌"},
{"n": "教育", "v": "教育"},
{"n": "校园", "v": "校园"},
{"n": "童话", "v": "童话"},
{"n": "推理", "v": "推理"},
{"n": "怀旧", "v": "怀旧"},
{"n": "宠物", "v": "宠物"},
{"n": "舞蹈", "v": "舞蹈"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2026", "v": "2026"},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"965": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "欧美", "v": "欧美"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "文化", "v": "文化"},
{"n": "纪实", "v": "纪实"},
{"n": "访谈", "v": "访谈"},
{"n": "历史", "v": "历史"},
{"n": "美食", "v": "美食"},
{"n": "旅游", "v": "旅游"},
{"n": "时尚", "v": "时尚"},
{"n": "情感", "v": "情感"},
{"n": "生活", "v": "生活"},
{"n": "真人秀", "v": "真人秀"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2026", "v": "2026"},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"966": [{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "国内视野", "v": "国内视野"},
{"n": "国际纵览", "v": "国际纵览"},
{"n": "军事话题", "v": "军事话题"},
{"n": "社会百态", "v": "社会百态"},
{"n": "央视频", "v": "央视频"}]}]}}
return result
def generate_x_sign(self, secret_b64):
secret_key = self.decode_secret_key(secret_b64)
data = self.get_data_string()
signature = self.compute_signature(secret_key, data)
x_sign = self.encode_signature(signature)
return x_sign
def decode_secret_key(self, secret_b64):
return base64.b64decode(secret_b64)
def get_data_string(self):
return "{}"
def compute_signature(self, secret_key, data):
return hmac.new(secret_key, data.encode('utf-8'), hashlib.sha256).digest()
def encode_signature(self, signature):
return base64.b64encode(signature).decode('utf-8')
def get_current_app_key(self, xurl, headerx):
resp = self.fetch_index_page(xurl, headerx)
js_path = self.extract_js_path(resp.text)
js_url = f"{xurl}{js_path}"
js_content = self.fetch_js_content(js_url, headerx)
target_key_b64 = self.extract_target_key(js_content)
return target_key_b64
def fetch_index_page(self, xurl, headerx):
resp = requests.get(xurl, headers=headerx)
resp.raise_for_status()
return resp
def extract_js_path(self, html_content):
js_path_pattern = r'src="(/[\d\.]+/assets/js/index-[\w\.-]+\.js)"'
match = re.search(js_path_pattern, html_content)
return match.group(1)
def fetch_js_content(self, js_url, headerx):
js_resp = requests.get(js_url, headers=headerx)
js_resp.raise_for_status()
return js_resp.text
def extract_target_key(self, js_content):
key_pattern = r'const \w+="([^"]+)",\w+="([^"]+)",\w+="([^"]+)";'
key_match = re.search(key_pattern, js_content)
return key_match.group(2)
def get_headers(self, sign1, xurl):
return {
'accept': 'application/json, text/plain, */*',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'no-cache',
'launchchannel': 'web_channel',
'origin': xurl,
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': xurl,
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'siteid': '1000101',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
'x-sign': sign1,
}
def categoryContent(self, cid, pg, filter, ext):
secret_b64 = self.get_current_app_key(xurl, headerx)
sign1 = self.generate_x_sign(secret_b64)
page = self.get_page_number(pg)
NdType, DqType, LxType = self.extract_filter_types(ext)
headers = self.get_headers(sign1, xurl)
params = self.build_category_params(cid, page, NdType, DqType, LxType)
data = self.fetch_category_data(params, headers)
videos = self.parse_category_videos(data)
return self.build_category_result(videos, pg)
def get_page_number(self, pg):
return int(pg) if pg else 1
def extract_filter_types(self, ext):
NdType = ext.get('年代', '全部')
DqType = ext.get('地区', '全部')
LxType = ext.get('类型', '全部')
return NdType, DqType, LxType
def build_category_params(self, cid, page, NdType, DqType, LxType):
return {'functionName': 'getNewsSearchedByCondition','nodeId': cid,'nodeTag': LxType,'yearTag': NdType,'countryTag': DqType,'orderType': '0','pageSize': '40','page': page,'keyword': '','siteId': '1000101',}
def fetch_category_data(self, params, headers):
detail = requests.get('https://ups.5g.wasu.tv/rmp-user-suggest/1000101/hzhs/searchServlet', params=params,headers=headers)
detail.encoding = "utf-8"
return detail.json()
def parse_category_videos(self, data):
videos = []
for vod in data['data']:
videos.append(self.parse_category_video(vod))
return videos
def parse_category_video(self, vod):
return {"vod_id": f"{vod['nodeId']}@{vod['newsId']}","vod_name": vod['title'],"vod_pic": vod['hPic'],"vod_year": vod.get('pubTime', '暂无日期'),"vod_remarks": vod.get('episodeDesc', '暂无备注')}
def build_category_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
secret_b64 = self.get_current_app_key(xurl, headerx)
sign1 = self.generate_x_sign(secret_b64)
fenge = did.split("@")
headers = self.get_headers(sign1, xurl)
params = self.build_detail_params(fenge)
data = self.fetch_detail_data(params, headers)
content = self.build_content(data)
director = self.extract_detail_field(data, 'director')
actor = self.extract_detail_field(data, 'actor')
remarks = self.extract_detail_field(data, 'episodeDesc')
year = self.extract_detail_field(data, 'pubTime')
area = self.extract_detail_field(data, 'countryTag')
bofang = self.build_play_url(data)
videos = [self.build_video_data(did, director, actor, remarks, year, area, content, bofang)]
return self.build_result(videos)
def build_detail_params(self, fenge):
return {'siteId': '1000101', 'functionName': 'getCurrentNews', 'nodeId': fenge[0], 'newsId': fenge[1],'platform': 'web',}
def fetch_detail_data(self, params, headers):
detail = requests.get('https://mcspapp.5g.wasu.tv/bvradio_app/hzhs/newsServlet', params=params, headers=headers)
detail.encoding = "utf-8"
return detail.json()
def build_content(self, data):
return '介绍剧情👉' + data.get('data', {}).get('newsAbstract', '')
def extract_detail_field(self, data, field_name):
return data.get('data', {}).get(field_name, '')
def build_play_url(self, data):
bofang = ''
for vod in data['data']['vodList']:
name = vod['title']
if len(vod['fileList']) > 1:
id = vod['fileList'][1]['playUrl']
else:
id = vod['fileList'][0]['playUrl']
bofang += name + '$' + id + '#'
return bofang[:-1]
def build_video_data(self, did, director, actor, remarks, year, area, content, bofang):
return {"vod_id": did, "vod_director": director, "vod_actor": actor, "vod_remarks": remarks, "vod_year": year, "vod_area": area, "vod_content": content, "vod_play_from": "华数", "vod_play_url": bofang}
def build_result(self, videos):
result = {}
result['list'] = videos
return result
def get_x_sign_for_post(self, secret_b64, playUrl):
secret_key = self.decode_secret_key(secret_b64)
payload_dict = self.build_payload_dict(playUrl)
data_string = self.serialize_payload(payload_dict)
signature = self.compute_hmac_signature(secret_key, data_string)
x_sign = self.encode_signature(signature)
return x_sign
def decode_secret_key(self, secret_b64):
return base64.b64decode(secret_b64)
def build_payload_dict(self, playUrl):
return {"playUrl": playUrl, "platform": "web"}
def serialize_payload(self, payload_dict):
return json.dumps(payload_dict, separators=(',', ':'), ensure_ascii=False)
def compute_hmac_signature(self, secret_key, data_string):
return hmac.new(secret_key, data_string.encode('utf-8'), hashlib.sha256).digest()
def encode_signature(self, signature):
return base64.b64encode(signature).decode('utf-8')
def playerContent(self, flag, id, vipFlags):
secret_b64 = self.get_current_app_key(xurl, headerx)
id = self.normalize_play_url(id)
sign2 = self.get_x_sign_for_post(secret_b64, id)
headers = self.get_headers(sign2, xurl)
json_data = self.build_request_json(id)
result = self.post_play_url_request(headers, json_data)
play_url = result['data']['playUrl']
return self.build_player_result(play_url)
def normalize_play_url(self, id):
return id.replace('.mp4', '/playlist.m3u8')
def build_request_json(self, playUrl):
return {'playUrl': playUrl, 'platform': 'web', }
def post_play_url_request(self, headers, json_data):
response = requests.post('https://mcspapp.5g.wasu.tv/thirdApiFile/file/getPlayUrl', headers=headers, json=json_data)
return response.json()
def build_player_result(self, play_url):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = play_url
result["header"] = headerz
return result
def searchContentPage(self, key, quick, pg):
page = self.get_page_number(pg)
secret_b64 = self.get_current_app_key(xurl, headerx)
sign1 = self.generate_x_sign(secret_b64)
headers = self.get_headers(sign1, xurl)
params = self.build_search_params(key, page)
data = self.fetch_search_data(params, headers)
videos = self.parse_search_videos(data)
return self.build_search_result(videos, pg)
def get_page_number(self, pg):
return int(pg) if pg else 1
def build_search_params(self, key, page):
return {'functionName': 'getNewsSearched', 'searchNewsType': '3,4,5', 'keyword': key, 'pageSize': 10, 'page': page, 'siteId': 1000101}
def fetch_search_data(self, params, headers):
detail = requests.get("https://ups.5g.wasu.tv/rmp-user-suggest/1000101/hzhs/searchServlet", params=params, headers=headers)
detail.encoding = "utf-8"
return detail.json()
def parse_search_videos(self, data):
videos = []
for vod in data['data']:
videos.append(self.parse_search_video(vod))
return videos
def parse_search_video(self, vod):
return {"vod_id": f"{vod['nodeId']}@{vod['newsId']}", "vod_name": vod['title'], "vod_pic": vod['hPic'], "vod_year": vod.get('pubTime', '暂无日期'), "vod_remarks": vod.get('episodeDesc', '暂无备注')}
def build_search_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+314
View File
@@ -0,0 +1,314 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://search.bilibili.com"
xurl1 = "https://api.live.bilibili.com"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0'
}
class Spider(Spider):
global xurl
global xurl1
global headerx
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "舞", "type_name": "舞蹈"},
{"type_id": "音乐", "type_name": "音乐"},
{"type_id": "手游", "type_name": "手游"},
{"type_id": "网游", "type_name": "网游"},
{"type_id": "单机游戏", "type_name": "单机游戏"},
{"type_id": "虚拟主播", "type_name": "虚拟主播"},
{"type_id": "电台", "type_name": "电台"},
{"type_id": "体育", "type_name": "体育"},
{"type_id": "聊天", "type_name": "聊天"},
{"type_id": "娱乐", "type_name": "娱乐"},
{"type_id": "电影", "type_name": "影视"},
{"type_id": "新闻", "type_name": "新闻"}]
}
return result
def homeVideoContent(self):
pass
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
if pg:
page = int(pg)
else:
page = 1
url = f'{xurl}/live?keyword={cid}&page={str(page)}'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
doc = BeautifulSoup(res, "lxml")
soups = doc.find_all('div', class_="video-list-item")
for vod in soups:
names = vod.find('h3', class_="bili-live-card__info--tit")
name = names.text.strip().replace('直播中', '')
id = names.find('a')['href']
id = self.extract_middle_text(id, 'bilibili.com/', '?', 0)
pic = vod.find('img')['src']
if 'http' not in pic:
pic = "https:" + pic
remarks = vod.find('a', class_="bili-live-card__info--uname")
remark = remarks.text.strip()
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
result = {}
videos = []
xianlu = ''
bofang = ''
url = f'{xurl1}/xlive/web-room/v2/index/getRoomPlayInfo?room_id={did}&platform=web&protocol=0,1&format=0,1,2&codec=0,1'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
data = detail.json()
content = '欢迎观看哔哩直播'
setup = data['data']['playurl_info']['playurl']['stream']
nam = 0
for vod in setup:
try:
host = vod['format'][nam]['codec'][0]['url_info'][1]['host']
except (KeyError, IndexError):
continue
base = vod['format'][nam]['codec'][0]['base_url']
extra = vod['format'][nam]['codec'][0]['url_info'][1]['extra']
id = host + base + extra
nam = nam + 1
namc = f"{nam}号线路"
bofang = bofang + namc + '$' + id + '#'
bofang = bofang[:-1]
xianlu = '哔哩专线'
videos.append({
"vod_id": did,
"vod_content": content,
"vod_play_from": xianlu,
"vod_play_url": bofang
})
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headerx
return result
def searchContentPage(self, key, quick, pg):
result = {}
videos = []
if pg:
page = int(pg)
else:
page = 1
url = f'{xurl}/live?keyword={key}&page={str(page)}'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
doc = BeautifulSoup(res, "lxml")
soups = doc.find_all('div', class_="video-list-item")
for vod in soups:
names = vod.find('h3', class_="bili-live-card__info--tit")
name = names.text.strip().replace('直播中', '')
id = names.find('a')['href']
id = self.extract_middle_text(id, 'bilibili.com/', '?', 0)
pic = vod.find('img')['src']
if 'http' not in pic:
pic = "https:" + pic
remarks = vod.find('a', class_="bili-live-card__info--uname")
remark = remarks.text.strip()
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result['list'] = videos
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+476
View File
@@ -0,0 +1,476 @@
"""
@header({
searchable: 1,
filterable: 1,
quickSearch: 1,
title: '中央电视台',
lang: 'hipy'
})
"""
#coding=utf-8
#!/usr/bin/python
import sys
sys.path.append('..')
from base.spider import Spider
import json
import time
import base64
import re
from urllib import request, parse
import urllib
import urllib.request
import time
class Spider(Spider): # 元类 默认的元类 type
def getName(self):
return "中央电视台"#可搜索
def init(self,extend=""):
print("============{0}============".format(extend))
pass
def destroy(self):
pass
def isVideoFormat(self,url):
pass
def manualVideoCheck(self):
pass
def homeContent(self,filter):
result = {}
cateManual = {
"央视大全":"节目大全",
"电视剧": "电视剧",
"动画片": "动画片",
"纪录片": "纪录片",
"特别节目": "特别节目"
}
classes = []
for k in cateManual:
classes.append({
'type_name':k,
'type_id':cateManual[k]
})
result['class'] = classes
if(filter):
result['filters'] = self.config['filter']
return result
def homeVideoContent(self):
result = {
'list':[]
}
return result
def categoryContent(self,tid,pg,filter,extend):
result = {}
month = ""#月
year = ""#年
area=''#地区
channel=''#频道
datafl=''#类型
letter=''#字母
pagecount=24
if tid=='动画片':
id=urllib.parse.quote(tid)
if 'datadq-area' in extend.keys():
area=urllib.parse.quote(extend['datadq-area'])
if 'dataszm-letter' in extend.keys():
letter=extend['dataszm-letter']
if 'datafl-sc' in extend.keys():
datafl=urllib.parse.quote(extend['datafl-sc'])
url='https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955899450127&area={0}&sc={4}&fc={1}&letter={2}&p={3}&n=24&serviceId=tvcctv&topv=1&t=json'.format(area,id,letter,pg,datafl)
elif tid=='纪录片':
id=urllib.parse.quote(tid)
if 'datapd-channel' in extend.keys():
channel=urllib.parse.quote(extend['datapd-channel'])
if 'datafl-sc' in extend.keys():
datafl=urllib.parse.quote(extend['datafl-sc'])
if 'datanf-year' in extend.keys():
year=extend['datanf-year']
if 'dataszm-letter' in extend.keys():
letter=extend['dataszm-letter']
url='https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955924871139&fc={0}&channel={1}&sc={2}&year={3}&letter={4}&p={5}&n=24&serviceId=tvcctv&topv=1&t=json'.format(id,channel,datafl,year,letter,pg)
elif tid=='电视剧':
id=urllib.parse.quote(tid)
if 'datafl-sc' in extend.keys():
datafl=urllib.parse.quote(extend['datafl-sc'])
if 'datanf-year' in extend.keys():
year=extend['datanf-year']
if 'dataszm-letter' in extend.keys():
letter=extend['dataszm-letter']
url='https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955853485115&area={0}&sc={1}&fc={2}&year={3}&letter={4}&p={5}&n=24&serviceId=tvcctv&topv=1&t=json'.format(area,datafl,id,year,letter,pg)
elif tid=='特别节目':
id=urllib.parse.quote(tid)
if 'datapd-channel' in extend.keys():
channel=urllib.parse.quote(extend['datapd-channel'])
if 'datafl-sc' in extend.keys():
datafl=urllib.parse.quote(extend['datafl-sc'])
if 'dataszm-letter' in extend.keys():
letter=extend['dataszm-letter']
url='https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955953877151&channel={0}&sc={1}&fc={2}&bigday=&letter={3}&p={4}&n=24&serviceId=tvcctv&topv=1&t=json'.format(channel,datafl,id,letter,pg)
elif tid=='节目大全':
cid=''#频道
if 'cid' in extend.keys():
cid=extend['cid']
fc=''#分类
if 'fc' in extend.keys():
fc=extend['fc']
fl=''#字母
if 'fl' in extend.keys():
fl=extend['fl']
url = 'https://api.cntv.cn/lanmu/columnSearch?&fl={0}&fc={1}&cid={2}&p={3}&n=20&serviceId=tvcctv&t=json&cb=ko'.format(fl,fc,cid,pg)
pagecount=20
else:
url = 'https://tv.cctv.com/epg/index.shtml'
videos=[]
htmlText =self.webReadFile(urlStr=url,header=self.header)
if tid=='节目大全':
index=htmlText.rfind(');')
if index>-1:
htmlText=htmlText[3:index]
videos =self.get_list1(html=htmlText,tid=tid)
else:
videos =self.get_list(html=htmlText,tid=tid)
#print(videos)
result['list'] = videos
result['page'] = pg
result['pagecount'] = 9999 if len(videos)>=pagecount else pg
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self,array):
result={}
aid = array[0].split('###')
tid = aid[0]
logo = aid[3]
lastVideo = aid[2]
title = aid[1]
id= aid[4]
vod_year= aid[5]
actors= aid[6]
brief= aid[7]
fromId='CCTV'
if tid=="节目大全":
lastUrl = 'https://api.cntv.cn/video/videoinfoByGuid?guid={0}&serviceId=tvcctv'.format(id)
htmlTxt = self.webReadFile(urlStr=lastUrl,header=self.header)
topicId=json.loads(htmlTxt)['ctid']
Url = "https://api.cntv.cn/NewVideo/getVideoListByColumn?id={0}&d=&p=1&n=100&sort=desc&mode=0&serviceId=tvcctv&t=json".format(topicId)
htmlTxt = self.webReadFile(urlStr=Url,header=self.header)
else:
Url='https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id={0}&serviceId=tvcctv&p=1&n=100&mode=0&pub=1'.format(id)
jRoot = ''
videoList = []
try:
if tid=="搜索":
fromId='中央台'
videoList=[title+"$"+lastVideo]
else:
htmlTxt=self.webReadFile(urlStr=Url,header=self.header)
jRoot = json.loads(htmlTxt)
data=jRoot['data']
jsonList=data['list']
videoList=self.get_EpisodesList(jsonList=jsonList)
if len(videoList)<1:
htmlTxt=self.webReadFile(urlStr=lastVideo,header=self.header)
if tid=="电视剧" or tid=="纪录片":
patternTxt=r"'title':\s*'(?P<title>.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'"
elif tid=="特别节目":
patternTxt=r'class="tp1"><a\s*href="(?P<url>https://.+?)"\s*target="_blank"\s*title="(?P<title>.+?)"></a></div>'
elif tid=="动画片":
patternTxt=r"'title':\s*'(?P<title>.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'"
elif tid=="节目大全":
patternTxt=r'href="(?P<url>.+?)" target="_blank" alt="(?P<title>.+?)" title=".+?">'
videoList=self.get_EpisodesList_re(htmlTxt=htmlTxt,patternTxt=patternTxt)
fromId='央视'
except:
pass
if len(videoList) == 0:
return {}
vod = {
"vod_id":array[0],
"vod_name":title,
"vod_pic":logo,
"type_name":tid,
"vod_year":vod_year,
"vod_area":"",
"vod_remarks":'',
"vod_actor":actors,
"vod_director":'',
"vod_content":brief
}
vod['vod_play_from'] = fromId
vod['vod_play_url'] = "#".join(videoList)
result = {
'list':[
vod
]
}
return result
def get_lineList(self,Txt,mark,after):
circuit=[]
origin=Txt.find(mark)
while origin>8:
end=Txt.find(after,origin)
circuit.append(Txt[origin:end])
origin=Txt.find(mark,end)
return circuit
def get_RegexGetTextLine(self,Text,RegexText,Index):
returnTxt=[]
pattern = re.compile(RegexText, re.M|re.S)
ListRe=pattern.findall(Text)
if len(ListRe)<1:
return returnTxt
for value in ListRe:
returnTxt.append(value)
return returnTxt
def searchContent(self,key,quick):
return self.searchContentPage(key, quick, '1')
def searchContentPage(self, key, quick, page):
key=urllib.parse.quote(key)
Url='https://search.cctv.com/ifsearch.php?page=1&qtext={0}&sort=relevance&pageSize=20&type=video&vtime=-1&datepid=1&channel=&pageflag=0&qtext_str={0}'.format(key)
htmlTxt=self.webReadFile(urlStr=Url,header=self.header)
videos=self.get_list_search(html=htmlTxt,tid='搜索')
result = {
'list':videos
}
return result
def playerContent(self,flag,id,vipFlags):
result = {}
url=''
parse=0
headers = {
'User-Agent':'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
}
if flag=='CCTV':
url=self.get_m3u8(urlTxt=id)
else:
try:
html=self.webReadFile(urlStr=id,header=self.header)
guid=self.get_RegexGetText(Text=html,RegexText=r'var\sguid\s*=\s*"(.+?)";',Index=1)
url=self.get_m3u8(urlTxt=guid)
except :
url=id
parse=1
if url.find('https:')<0:
url=id
parse=1
result["parse"] = parse#1=嗅探,0=播放
result["playUrl"] = ''
result["url"] = url
result["header"] =headers
return result
config = {
"player": {},
"filter": {
"电视剧":[
{"key":"datafl-sc","name":"类型","value":[{"n":"全部","v":""},{"n":"谍战","v":"谍战"},{"n":"悬疑","v":"悬疑"},{"n":"刑侦","v":"刑侦"},{"n":"历史","v":"历史"},{"n":"古装","v":"古装"},{"n":"武侠","v":"武侠"},{"n":"军旅","v":"军旅"},{"n":"战争","v":"战争"},{"n":"喜剧","v":"喜剧"},{"n":"青春","v":"青春"},{"n":"言情","v":"言情"},{"n":"偶像","v":"偶像"},{"n":"家庭","v":"家庭"},{"n":"年代","v":"年代"},{"n":"革命","v":"革命"},{"n":"农村","v":"农村"},{"n":"都市","v":"都市"},{"n":"其他","v":"其他"}]},
{"key":"datadq-area","name":"地区","value":[{"n":"全部","v":""},{"n":"中国大陆","v":"中国大陆"},{"n":"中国香港","v":"香港"},{"n":"美国","v":"美国"},{"n":"欧洲","v":"欧洲"},{"n":"泰国","v":"泰国"}]},
{"key":"datanf-year","name":"年份","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"},{"n":"1999","v":"1999"},{"n":"1998","v":"1998"},{"n":"1997","v":"1997"}]},
{"key":"dataszm-letter","name":"字母","value":[{"n":"全部","v":""},{"n":"A","v":"A"},{"n":"C","v":"C"},{"n":"E","v":"E"},{"n":"F","v":"F"},{"n":"G","v":"G"},{"n":"H","v":"H"},{"n":"I","v":"I"},{"n":"J","v":"J"},{"n":"K","v":"K"},{"n":"L","v":"L"},{"n":"M","v":"M"},{"n":"N","v":"N"},{"n":"O","v":"O"},{"n":"P","v":"P"},{"n":"Q","v":"Q"},{"n":"R","v":"R"},{"n":"S","v":"S"},{"n":"T","v":"T"},{"n":"U","v":"U"},{"n":"V","v":"V"},{"n":"W","v":"W"},{"n":"X","v":"X"},{"n":"Y","v":"Y"},{"n":"Z","v":"Z"},{"n":"0-9","v":"0-9"}]}
],
"动画片":[
{"key":"datafl-sc","name":"类型","value":[{"n":"全部","v":""},{"n":"亲子","v":"亲子"},{"n":"搞笑","v":"搞笑"},{"n":"冒险","v":"冒险"},{"n":"动作","v":"动作"},{"n":"宠物","v":"宠物"},{"n":"体育","v":"体育"},{"n":"益智","v":"益智"},{"n":"历史","v":"历史"},{"n":"教育","v":"教育"},{"n":"校园","v":"校园"},{"n":"言情","v":"言情"},{"n":"武侠","v":"武侠"},{"n":"经典","v":"经典"},{"n":"未来","v":"未来"},{"n":"古代","v":"古代"},{"n":"神话","v":"神话"},{"n":"真人","v":"真人"},{"n":"励志","v":"励志"},{"n":"热血","v":"热血"},{"n":"奇幻","v":"奇幻"},{"n":"童话","v":"童话"},{"n":"剧情","v":"剧情"},{"n":"夺宝","v":"夺宝"},{"n":"其他","v":"其他"}]},
{"key":"datadq-area","name":"地区","value":[{"n":"全部","v":""},{"n":"中国大陆","v":"中国大陆"},{"n":"美国","v":"美国"},{"n":"欧洲","v":"欧洲"}]},
{"key":"dataszm-letter","name":"字母","value":[{"n":"全部","v":""},{"n":"A","v":"A"},{"n":"C","v":"C"},{"n":"E","v":"E"},{"n":"F","v":"F"},{"n":"G","v":"G"},{"n":"H","v":"H"},{"n":"I","v":"I"},{"n":"J","v":"J"},{"n":"K","v":"K"},{"n":"L","v":"L"},{"n":"M","v":"M"},{"n":"N","v":"N"},{"n":"O","v":"O"},{"n":"P","v":"P"},{"n":"Q","v":"Q"},{"n":"R","v":"R"},{"n":"S","v":"S"},{"n":"T","v":"T"},{"n":"U","v":"U"},{"n":"V","v":"V"},{"n":"W","v":"W"},{"n":"X","v":"X"},{"n":"Y","v":"Y"},{"n":"Z","v":"Z"},{"n":"0-9","v":"0-9"}]}
],
"纪录片":[
{"key":"datapd-channel","name":"频道","value":[{"n":"全部","v":""},{"n":"CCTV{1 综合","v":"CCTV{1 综合"},{"n":"CCTV{2 财经","v":"CCTV{2 财经"},{"n":"CCTV{3 综艺","v":"CCTV{3 综艺"},{"n":"CCTV{4 中文国际","v":"CCTV{4 中文国际"},{"n":"CCTV{5 体育","v":"CCTV{5 体育"},{"n":"CCTV{6 电影","v":"CCTV{6 电影"},{"n":"CCTV{7 国防军事","v":"CCTV{7 国防军事"},{"n":"CCTV{8 电视剧","v":"CCTV{8 电视剧"},{"n":"CCTV{9 纪录","v":"CCTV{9 纪录"},{"n":"CCTV{10 科教","v":"CCTV{10 科教"},{"n":"CCTV{11 戏曲","v":"CCTV{11 戏曲"},{"n":"CCTV{12 社会与法","v":"CCTV{12 社会与法"},{"n":"CCTV{13 新闻","v":"CCTV{13 新闻"},{"n":"CCTV{14 少儿","v":"CCTV{14 少儿"},{"n":"CCTV{15 音乐","v":"CCTV{15 音乐"},{"n":"CCTV{17 农业农村","v":"CCTV{17 农业农村"}]},
{"key":"datafl-sc","name":"类型","value":[{"n":"全部","v":""},{"n":"人文历史","v":"人文历史"},{"n":"人物","v":"人物"},{"n":"军事","v":"军事"},{"n":"探索","v":"探索"},{"n":"社会","v":"社会"},{"n":"时政","v":"时政"},{"n":"经济","v":"经济"},{"n":"科技","v":"科技"}]},
{"key":"datanf-year","name":"年份","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"}]},
{"key":"dataszm-letter","name":"字母","value":[{"n":"全部","v":""},{"n":"A","v":"A"},{"n":"C","v":"C"},{"n":"E","v":"E"},{"n":"F","v":"F"},{"n":"G","v":"G"},{"n":"H","v":"H"},{"n":"I","v":"I"},{"n":"J","v":"J"},{"n":"K","v":"K"},{"n":"L","v":"L"},{"n":"M","v":"M"},{"n":"N","v":"N"},{"n":"O","v":"O"},{"n":"P","v":"P"},{"n":"Q","v":"Q"},{"n":"R","v":"R"},{"n":"S","v":"S"},{"n":"T","v":"T"},{"n":"U","v":"U"},{"n":"V","v":"V"},{"n":"W","v":"W"},{"n":"X","v":"X"},{"n":"Y","v":"Y"},{"n":"Z","v":"Z"},{"n":"0-9","v":"0-9"}]}
],
"特别节目":[
{"key":"datapd-channel","name":"频道","value":[{"n":"全部","v":""},{"n":"CCTV{1 综合","v":"CCTV{1 综合"},{"n":"CCTV{2 财经","v":"CCTV{2 财经"},{"n":"CCTV{3 综艺","v":"CCTV{3 综艺"},{"n":"CCTV{4 中文国际","v":"CCTV{4 中文国际"},{"n":"CCTV{5 体育","v":"CCTV{5 体育"},{"n":"CCTV{6 电影","v":"CCTV{6 电影"},{"n":"CCTV{7 国防军事","v":"CCTV{7 国防军事"},{"n":"CCTV{8 电视剧","v":"CCTV{8 电视剧"},{"n":"CCTV{9 纪录","v":"CCTV{9 纪录"},{"n":"CCTV{10 科教","v":"CCTV{10 科教"},{"n":"CCTV{11 戏曲","v":"CCTV{11 戏曲"},{"n":"CCTV{12 社会与法","v":"CCTV{12 社会与法"},{"n":"CCTV{13 新闻","v":"CCTV{13 新闻"},{"n":"CCTV{14 少儿","v":"CCTV{14 少儿"},{"n":"CCTV{15 音乐","v":"CCTV{15 音乐"},{"n":"CCTV{17 农业农村","v":"CCTV{17 农业农村"}]},
{"key":"datafl-sc","name":"类型","value":[{"n":"全部","v":""},{"n":"全部","v":"全部"},{"n":"新闻","v":"新闻"},{"n":"经济","v":"经济"},{"n":"综艺","v":"综艺"},{"n":"体育","v":"体育"},{"n":"军事","v":"军事"},{"n":"影视","v":"影视"},{"n":"科教","v":"科教"},{"n":"戏曲","v":"戏曲"},{"n":"青少","v":"青少"},{"n":"音乐","v":"音乐"},{"n":"社会","v":"社会"},{"n":"公益","v":"公益"},{"n":"其他","v":"其他"}]},
{"key":"dataszm-letter","name":"字母","value":[{"n":"全部","v":""},{"n":"A","v":"A"},{"n":"C","v":"C"},{"n":"E","v":"E"},{"n":"F","v":"F"},{"n":"G","v":"G"},{"n":"H","v":"H"},{"n":"I","v":"I"},{"n":"J","v":"J"},{"n":"K","v":"K"},{"n":"L","v":"L"},{"n":"M","v":"M"},{"n":"N","v":"N"},{"n":"O","v":"O"},{"n":"P","v":"P"},{"n":"Q","v":"Q"},{"n":"R","v":"R"},{"n":"S","v":"S"},{"n":"T","v":"T"},{"n":"U","v":"U"},{"n":"V","v":"V"},{"n":"W","v":"W"},{"n":"X","v":"X"},{"n":"Y","v":"Y"},{"n":"Z","v":"Z"},{"n":"0-9","v":"0-9"}]}
],
"节目大全":[{"key":"cid","name":"频道","value":[{"n":"全部","v":""},{"n":"CCTV-1综合","v":"EPGC1386744804340101"},{"n":"CCTV-2财经","v":"EPGC1386744804340102"},{"n":"CCTV-3综艺","v":"EPGC1386744804340103"},{"n":"CCTV-4中文国际","v":"EPGC1386744804340104"},{"n":"CCTV-5体育","v":"EPGC1386744804340107"},{"n":"CCTV-6电影","v":"EPGC1386744804340108"},{"n":"CCTV-7国防军事","v":"EPGC1386744804340109"},{"n":"CCTV-8电视剧","v":"EPGC1386744804340110"},{"n":"CCTV-9纪录","v":"EPGC1386744804340112"},{"n":"CCTV-10科教","v":"EPGC1386744804340113"},{"n":"CCTV-11戏曲","v":"EPGC1386744804340114"},{"n":"CCTV-12社会与法","v":"EPGC1386744804340115"},{"n":"CCTV-13新闻","v":"EPGC1386744804340116"},{"n":"CCTV-14少儿","v":"EPGC1386744804340117"},{"n":"CCTV-15音乐","v":"EPGC1386744804340118"},{"n":"CCTV-16奥林匹克","v":"EPGC1634630207058998"},{"n":"CCTV-17农业农村","v":"EPGC1563932742616872"},{"n":"CCTV-5+体育赛事","v":"EPGC1468294755566101"}]},{"key":"fc","name":"分类","value":[{"n":"全部","v":""},{"n":"新闻","v":"新闻"},{"n":"体育","v":"体育"},{"n":"综艺","v":"综艺"},{"n":"健康","v":"健康"},{"n":"生活","v":"生活"},{"n":"科教","v":"科教"},{"n":"经济","v":"经济"},{"n":"农业","v":"农业"},{"n":"法治","v":"法治"},{"n":"军事","v":"军事"},{"n":"少儿","v":"少儿"},{"n":"动画","v":"动画"},{"n":"纪实","v":"纪实"},{"n":"戏曲","v":"戏曲"},{"n":"音乐","v":"音乐"},{"n":"影视","v":"影视"}]},{"key":"fl","name":"字母","value":[{"n":"全部","v":""},{"n":"A","v":"A"},{"n":"B","v":"B"},{"n":"C","v":"C"},{"n":"D","v":"D"},{"n":"E","v":"E"},{"n":"F","v":"F"},{"n":"G","v":"G"},{"n":"H","v":"H"},{"n":"I","v":"I"},{"n":"J","v":"J"},{"n":"K","v":"K"},{"n":"L","v":"L"},{"n":"M","v":"M"},{"n":"N","v":"N"},{"n":"O","v":"O"},{"n":"P","v":"P"},{"n":"Q","v":"Q"},{"n":"R","v":"R"},{"n":"S","v":"S"},{"n":"T","v":"T"},{"n":"U","v":"U"},{"n":"V","v":"V"},{"n":"W","v":"W"},{"n":"X","v":"X"},{"n":"Y","v":"Y"},{"n":"Z","v":"Z"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"month","name":"月份","value":[{"n":"全部","v":""},{"n":"12","v":"12"},{"n":"11","v":"11"},{"n":"10","v":"10"},{"n":"09","v":"09"},{"n":"08","v":"08"},{"n":"07","v":"07"},{"n":"06","v":"06"},{"n":"05","v":"05"},{"n":"04","v":"04"},{"n":"03","v":"03"},{"n":"02","v":"02"},{"n":"01","v":"01"}]}]
}
}
header = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
"Host": "tv.cctv.com",
"Referer": "https://tv.cctv.com/"
}
def localProxy(self,param):
return [200, "video/MP2T", ""]
#-----------------------------------------------自定义函数-----------------------------------------------
#访问网页
def webReadFile(self,urlStr,header):
html=''
req=urllib.request.Request(url=urlStr)#,headers=header
with urllib.request.urlopen(req) as response:
html = response.read().decode('utf-8')
return html
#判断网络地址是否存在
def TestWebPage(self,urlStr,header):
html=''
req=urllib.request.Request(url=urlStr,method='HEAD')#,headers=header
with urllib.request.urlopen(req) as response:
html = response.getcode ()
return html
#正则取文本
def get_RegexGetText(self,Text,RegexText,Index):
returnTxt=""
Regex=re.search(RegexText, Text, re.M|re.S)
if Regex is None:
returnTxt=""
else:
returnTxt=Regex.group(Index)
return returnTxt
#取集数
def get_EpisodesList(self,jsonList):
videos=[]
for vod in jsonList:
url = vod['guid']
title =vod['title']
if len(url) == 0:
continue
videos.append(title+"$"+url)
return videos
#取集数
def get_EpisodesList_re(self,htmlTxt,patternTxt):
ListRe=re.finditer(patternTxt, htmlTxt, re.M|re.S)
videos=[]
for vod in ListRe:
url = vod.group('url')
title =vod.group('title')
if len(url) == 0:
continue
videos.append(title+"$"+url)
return videos
#取剧集区
def get_lineList(self,Txt,mark,after):
circuit=[]
origin=Txt.find(mark)
while origin>8:
end=Txt.find(after,origin)
circuit.append(Txt[origin:end])
origin=Txt.find(mark,end)
return circuit
#正则取文本,返回数组
def get_RegexGetTextLine(self,Text,RegexText,Index):
returnTxt=[]
pattern = re.compile(RegexText, re.M|re.S)
ListRe=pattern.findall(Text)
if len(ListRe)<1:
return returnTxt
for value in ListRe:
returnTxt.append(value)
return returnTxt
#删除html标签
def removeHtml(self,txt):
soup = re.compile(r'<[^>]+>',re.S)
txt =soup.sub('', txt)
return txt.replace("&nbsp;"," ")
#取m3u8
def get_m3u8(self,urlTxt):
url = "https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid={0}".format(urlTxt)
html=self.webReadFile(urlStr=url,header=self.header)
jo =json.loads(html)
link = jo['hls_url'].strip()
html = self.webReadFile(urlStr=link,header=self.header)
content = html.strip()
arr = content.split('\n')
urlPrefix = self.get_RegexGetText(Text=link,RegexText='(http[s]?://[a-zA-z0-9.]+)/',Index=1)
subUrl = arr[-1].split('/')
subUrl[3] = '1200'
subUrl[-1] = '1200.m3u8'
hdUrl = urlPrefix + '/'.join(subUrl)
url = urlPrefix + arr[-1]
hdRsp = self.TestWebPage(urlStr=hdUrl,header=self.header)
if hdRsp == 200:
url = hdUrl
else:
url=''
return url
#搜索
def get_list_search(self,html,tid):
jRoot = json.loads(html)
jsonList=jRoot['list']
videos=[]
for vod in jsonList:
url = vod['urllink']
title =self.removeHtml(txt=vod['title'])
img=vod['imglink']
id=vod['id']
brief=vod['channel']
year=vod['uploadtime']
if len(url) == 0:
continue
guid="{0}###{1}###{2}###{3}###{4}###{5}###{6}###{7}".format(tid,title,url,img,id,year,'',brief)
videos.append({
"vod_id":guid,
"vod_name":title,
"vod_pic":img,
"vod_remarks":year
})
return videos
return videos
def get_list1(self,html,tid):
jRoot = json.loads(html)
videos = []
data=jRoot['response']
if data is None:
return []
jsonList=data['docs']
for vod in jsonList:
id = vod['lastVIDE']['videoSharedCode']
title =vod['column_name']
url=vod['column_website']
img=vod['column_logo']
year=vod['column_playdate']
brief=vod['column_brief']
actors=''
if len(url) == 0:
continue
guid="{0}###{1}###{2}###{3}###{4}###{5}###{6}###{7}".format(tid,title,url,img,id,year,actors,brief)
#print(vod_id)
videos.append({
"vod_id":guid,
"vod_name":title,
"vod_pic":img,
"vod_remarks":''
})
#print(videos)
return videos
#分类取结果
def get_list(self,html,tid):
jRoot = json.loads(html)
videos = []
data=jRoot['data']
if data is None:
return []
jsonList=data['list']
for vod in jsonList:
url = vod['url']
title =vod['title']
img=vod['image']
id=vod['id']
try:
brief=vod['brief']
except:
brief=''
try:
year=vod['year']
except:
year=''
try:
actors=vod['actors']
except:
actors=''
if len(url) == 0:
continue
guid="{0}###{1}###{2}###{3}###{4}###{5}###{6}###{7}".format(tid,title,url,img,id,year,actors,brief)
#print(vod_id)
videos.append({
"vod_id":guid,
"vod_name":title,
"vod_pic":img,
"vod_remarks":''
})
return videos
+225
View File
@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
# by @嗷呜
import json
import sys
import threading
import uuid
import requests
sys.path.append('..')
from base.spider import Spider
import time
from Crypto.Hash import MD5, SHA1
class Spider(Spider):
'''
配置示例:
{
"key": "xxxx",
"name": "xxxx",
"type": 3,
"api": ".所在路径/金牌.py",
"searchable": 1,
"quickSearch": 1,
"filterable": 1,
"changeable": 1,
"ext": {
"site": "https://www.jiabaide.cn,域名2,域名3"
}
},
'''
def init(self, extend=""):
if extend:
hosts=json.loads(extend)['site']
self.host = self.host_late(hosts)
pass
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
def homeContent(self, filter):
cdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/get/filer/type", headers=self.getheaders()).json()
fdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/get/filer/list", headers=self.getheaders()).json()
result = {}
classes = []
filters={}
for k in cdata['data']:
classes.append({
'type_name': k['typeName'],
'type_id': str(k['typeId']),
})
sort_values = [{"n": "最近更新", "v": "2"},{"n": "人气高低", "v": "3"}, {"n": "评分高低", "v": "4"}]
for tid, d in fdata['data'].items():
current_sort_values = sort_values.copy()
if tid == '1':
del current_sort_values[0]
filters[tid] = [
{"key": "type", "name": "类型",
"value": [{"n": i["itemText"], "v": i["itemValue"]} for i in d["typeList"]]},
*([] if not d["plotList"] else [{"key": "v_class", "name": "剧情",
"value": [{"n": i["itemText"], "v": i["itemText"]}
for i in d["plotList"]]}]),
{"key": "area", "name": "地区",
"value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["districtList"]]},
{"key": "year", "name": "年份",
"value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["yearList"]]},
{"key": "lang", "name": "语言",
"value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["languageList"]]},
{"key": "sort", "name": "排序", "value": current_sort_values}
]
result['class'] = classes
result['filters'] = filters
return result
def homeVideoContent(self):
data1 = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/home/all/list", headers=self.getheaders()).json()
data2=self.fetch(f"{self.host}/api/mw-movie/anonymous/home/hotSearch",headers=self.getheaders()).json()
data=[]
for i in data1['data'].values():
data.extend(i['list'])
data.extend(data2['data'])
vods=self.getvod(data)
return {'list':vods}
def categoryContent(self, tid, pg, filter, extend):
params = {
"area": extend.get('area', ''),
"filterStatus": "1",
"lang": extend.get('lang', ''),
"pageNum": pg,
"pageSize": "30",
"sort": extend.get('sort', '1'),
"sortBy": "1",
"type": extend.get('type', ''),
"type1": tid,
"v_class": extend.get('v_class', ''),
"year": extend.get('year', '')
}
data = self.fetch(f"{self.host}/api/mw-movie/anonymous/video/list?{self.js(params)}", headers=self.getheaders(params)).json()
result = {}
result['list'] = self.getvod(data['data']['list'])
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/detail?id={ids[0]}",headers=self.getheaders({'id':ids[0]})).json()
vod=self.getvod([data['data']])[0]
vod['vod_play_from']='装歌有文才'
vod['vod_play_url'] = '#'.join(
f"{i['name'] if len(vod['episodelist']) > 1 else vod['vod_name']}${ids[0]}@@{i['nid']}" for i in
vod['episodelist'])
vod.pop('episodelist', None)
return {'list':[vod]}
def searchContent(self, key, quick, pg="1"):
params = {
"keyword": key,
"pageNum": pg,
"pageSize": "8",
"sourceCode": "1"
}
data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/searchByWord?{self.js(params)}",headers=self.getheaders(params)).json()
vods=self.getvod(data['data']['result']['list'])
return {'list':vods,'page':pg}
def playerContent(self, flag, id, vipFlags):
self.header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36',
'sec-ch-ua-platform': '"Windows"',
'DNT': '1',
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
'sec-ch-ua-mobile': '?0',
'Origin': self.host,
'Referer': f'{self.host}/'
}
ids=id.split('@@')
pdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v2/video/episode/url?clientType=1&id={ids[0]}&nid={ids[1]}",headers=self.getheaders({'clientType':'1','id': ids[0], 'nid': ids[1]})).json()
vlist=[]
for i in pdata['data']['list']:vlist.extend([i['resolutionName'],i['url']])
return {'parse':0,'url':vlist,'header':self.header}
def localProxy(self, param):
pass
def host_late(self, url_list):
if isinstance(url_list, str):
urls = [u.strip() for u in url_list.split(',')]
else:
urls = url_list
if len(urls) <= 1:
return urls[0] if urls else ''
results = {}
threads = []
def test_host(url):
try:
start_time = time.time()
response = requests.head(url, timeout=1.0, allow_redirects=False)
delay = (time.time() - start_time) * 1000
results[url] = delay
except Exception as e:
results[url] = float('inf')
for url in urls:
t = threading.Thread(target=test_host, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
return min(results.items(), key=lambda x: x[1])[0]
def md5(self, sign_key):
md5_hash = MD5.new()
md5_hash.update(sign_key.encode('utf-8'))
md5_result = md5_hash.hexdigest()
return md5_result
def js(self, param):
return '&'.join(f"{k}={v}" for k, v in param.items())
def getheaders(self, param=None):
if param is None:param = {}
t=str(int(time.time()*1000))
param['key']='cb808529bae6b6be45ecfab29a4889bc'
param['t']=t
sha1_hash = SHA1.new()
sha1_hash.update(self.md5(self.js(param)).encode('utf-8'))
sign = sha1_hash.hexdigest()
deviceid = str(uuid.uuid4())
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'sign': sign,
't': t,
'deviceid':deviceid
}
return headers
def convert_field_name(self, field):
field = field.lower()
if field.startswith('vod') and len(field) > 3:
field = field.replace('vod', 'vod_')
if field.startswith('type') and len(field) > 4:
field = field.replace('type', 'type_')
return field
def getvod(self, array):
return [{self.convert_field_name(k): v for k, v in item.items()} for item in array]
+318
View File
@@ -0,0 +1,318 @@
/*
title: '星辰影院', author: '小可乐/v6.1.1'
说明:可以不写ext,也可以写ext,ext支持的参数和格式参数如下
"ext": {
"host": "xxxx", //站点网址
"timeout": 6000, //请求超时,单位毫秒
"catesSet": "电视剧&电影&综艺", //指定分类和顺序
"tabsSet": "土星&下载线1" //指定线路和顺序
}
*/
const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36';
const DefHeader = {'User-Agent': MOBILE_UA};
var HOST;
var KParams = {
headers: {'User-Agent': MOBILE_UA},
timeout: 5000
};
async function init(cfg) {
try {
HOST = (cfg.ext?.host?.trim() || 'https://www.xcyycn.com').replace(/\/$/, '');
KParams.headers['Referer'] = HOST;
let parseTimeout = parseInt(cfg.ext?.timeout?.trim(), 10);
if (parseTimeout > 0) {KParams.timeout = parseTimeout;}
KParams.catesSet = cfg.ext?.catesSet?.trim() || '';
KParams.tabsSet = cfg.ext?.tabsSet?.trim() || '';
KParams.resHtml = await request(HOST);
} catch (e) {
console.error('初始化参数失败:', e.message);
}
}
async function home(filter) {
try {
let resHtml = KParams.resHtml;
if (!resHtml) {throw new Error('源码为空');}
let typeArr = cutStr(resHtml, 'hl-nav-item£>', '</li>', '', false, 0, true).filter(flt => flt.includes('/v/'));
let classes = typeArr.map((it,idx) => {
let cName = cutStr(it, '>', '<', `分类${idx+1}`);
let cId = cutStr(it, '/v/', '.', `值${idx+1}`);
return {type_name: cName, type_id: cId};
});
if (KParams.catesSet) {classes = ctSet(classes, KParams.catesSet);}
let filters = {};
try {
const nameObj = {class: 'class,剧情', area: 'area,地区', lang: 'lang,语言', year: 'year,年份', letter: 'letter,字母', by: 'by,排序'};
const regObj = {class: /\d+---([^-]+)-/, area: /\/\d+-([^-]+)-/, lang: /\d+----([^-]+)-/, year: /-([^-]+)\./, letter: /\d+-----([^-]+)-/, by: /\d+--([^-]+)-/};
let resHtmlList = await Promise.all(
classes.map(async (it) => {
try {return await request(`${HOST}/vs/${it.type_id}-----------.html`);} catch (sErr) {return '';}
})
);
classes.forEach((it,idx) => {
let resfHtml = resHtmlList[idx];
if (resfHtml) {
let flValArr = cutStr(resfHtml, 'hl-filter-list', '</ul>', '', false, 0, true).slice(1);
filters[it.type_id] = Object.entries(nameObj).map(([nObjk, nObjv]) => {
let [kkey, kname] = nObjv.split(',');
let tgVal = flValArr.find(fv => regObj[kkey].test(fv)) ?? '';
if (kkey === 'by') {tgVal = cutStr(resfHtml, 'hl-rb-title', '</div>', '', false);}
let tgValArr = cutStr(tgVal, '<a', '/a>', '', false, 0, true);
let tValArr = kkey !== 'by' ? tgValArr.slice(1) : tgValArr;
let kvalue = tValArr.map(el => {
let n = cutStr(el, '>', '<', '空白');
let v = n;
if (kkey === 'by') {v = el.match(regObj[kkey])?.[1] ?? '';}
return {n: n, v: v};
});
if (kkey !== 'by') {kvalue.unshift({n: '全部', v: ''});}
return {key: kkey, name: kname, value: kvalue};
}).filter(flt => flt.key && flt.value.length > 1);
}
});
} catch (e) {
filters = {}
}
return JSON.stringify({class: classes, filters: filters});
} catch (e) {
console.error('获取分类失败:', e.message);
return JSON.stringify({class: [], filters: {}});
}
}
async function homeVod() {
try {
let resHtml = KParams.resHtml;
let VODS = getVodList(resHtml);
return JSON.stringify({list: VODS});
} catch (e) {
console.error('推荐页获取失败:', e.message);
return JSON.stringify({list: []});
}
}
async function category(tid, pg, filter, extend) {
try {
pg = parseInt(pg, 10), pg = pg > 0 ? pg : 1;
let fl = extend || {};
let cateUrl = `${HOST}/vs/${fl.cateId || tid}-${fl.area ?? ''}-${fl.by ?? ''}-${fl.class ?? ''}-${fl.lang ?? ''}-${fl.letter ?? ''}---${pg}---${fl.year ?? ''}.html`;
let resHtml = await request(cateUrl);
let VODS = getVodList(resHtml);
let limit = VODS.length;
let pagecount = cutStr(resHtml, 'hl-page-total£/', '页', '1');
pagecount = Number(pagecount);
return JSON.stringify({list: VODS, page: pg, pagecount: pagecount, limit: limit, total: limit*pagecount});
} catch (e) {
console.error('类别页获取失败:', e.message);
return JSON.stringify({list: [], page: 1, pagecount: 0, limit: 30, total: 0});
}
}
async function search(wd, quick, pg) {
try {
pg = parseInt(pg, 10), pg = pg > 0 ? pg : 1;
let searchUrl = `${HOST}/s${wd}/page/${pg}.html`;
let resHtml = await request(searchUrl);
let VODS = getVodList(resHtml);
return JSON.stringify({list: VODS, page: pg, pagecount: 10, limit: 30, total: 300});
} catch (e) {
console.error('搜索页获取失败:', e.message);
return JSON.stringify({list: [], page: 1, pagecount: 0, limit: 30, total: 0});
}
}
function getVodList(khtml) {
try {
if (!khtml) {throw new Error('源码为空');}
let kvods = [];
let listArr = cutStr(khtml, 'hl-lazy', '</a>', '', false, 0, true).filter(flt => flt.includes('remarks'));
for (let it of listArr) {
let kname = cutStr(it, 'title="', '"', '名称');
let kpic = cutStr(it, 'data-original="', '"', '图片');
let kremarks = cutStr(it, 'remarks">', '</', '状态');
let kid = cutStr(it, 'href="', '"');
if (kid) {
kvods.push({
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
vod_id: `${kid}@${kname}@${kpic}@${kremarks}`
});
}
}
return kvods;
} catch (e) {
console.error(`生成视频列表失败:`, e.message);
return [];
}
}
async function detail(ids) {
try {
let [id, kname, kpic, kremarks] = ids.split('@');
let detailUrl = !/^http/.test(id) ? `${HOST}${id}` : id;
let resHtml = await request(detailUrl);
if (!resHtml) {throw new Error('源码为空');}
let intros = cutStr(resHtml, '"clearfix">', '</ul>', '', false);
let [ktabs, kurls] = [[], []];
let zx_tabs = cutStr(resHtml, '<span class="hl-from', '/span>', '', false, 0, true).map((it,idx) => cutStr(it, '>', '<', `在线${idx+1}`) );
ktabs.push(...zx_tabs);
let zx_urls = cutStr(resHtml, 'hl-plays-list">', '</ul>', '', false, 0, true).map((item,idx) => cutStr(item, '<li', '</li>', '', false, 0, true).map(it => { return cutStr(it, '>', '</a>', 'noEpi') + '$' + HOST + cutStr(it, 'href="', '"', 'noUrl'); }).join('#') );
kurls.push(...zx_urls);
let xzArr = cutStr(resHtml, 'hl-downs-list hl', '</ul>', '', false, 0, true);
if (xzArr[0]) {
xzArr.forEach((item,idx) => {
let siglUrl = cutStr(item, 'down_url', '>', '', false, 0, true).map(it => `${cutStr(it, 'file_name="', '"', 'noEpi')}$${cutStr(it, 'value="', '"', 'noUrl')}` ).join('#');
ktabs.push(`下载线${idx+1}`);
kurls.push(siglUrl);
});
}
if (KParams.tabsSet) {
let ktus = ktabs.map((it, idx) => { return {type_name: it, type_value: kurls[idx]} });
ktus = ctSet(ktus, KParams.tabsSet);
ktabs = ktus.map(it => it.type_name);
kurls = ktus.map(it => it.type_value);
}
let VOD = {
vod_id: detailUrl,
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
type_name: cutStr(intros, '类型:', '</li>', '类型'),
vod_year: cutStr(intros, '年份:', '</li>', '1000'),
vod_area: cutStr(intros, '地区:', '</li>', '地区'),
vod_lang: cutStr(intros, '语言:', '</li>', '语言'),
vod_director: cutStr(intros, '导演:', '</li>', '导演'),
vod_actor: cutStr(intros, '主演:', '</li>', '主演'),
vod_content: cutStr(intros, '简介:', '</li>', kname),
vod_play_from: ktabs.join('$$$'),
vod_play_url: kurls.join('$$$')
};
return JSON.stringify({list: [VOD]});
} catch (e) {
console.error('详情页获取失败:', e.message);
return JSON.stringify({list: []});
}
}
async function play(flag, ids, flags) {
try {
let kp = 0, kurl = '';
if (/下载/.test(flag)) {
kurl = ids;
} else {
let resHtml = await request(ids);
let codeObj = safeParseJSON(cutStr(resHtml, 'var player_£=', '<', '', false));
kurl = codeObj?.url ?? '';
if (!/^http/.test(kurl)) {
kurl = ids;
kp = 1;
}
}
return JSON.stringify({jx: 0, parse: kp, url: kurl, header: DefHeader});
} catch (e) {
console.error('播放失败:', e.message);
return JSON.stringify({jx: 0, parse: 0, url: '', header: {}});
}
}
function ctSet(kArr, setStr) {
try {
if (!Array.isArray(kArr) || kArr.length === 0 || typeof setStr !== 'string' || !setStr) { throw new Error('第一参数需为非空数组,第二参数需为非空字符串'); }
const set_arr = [...kArr];
const arrNames = setStr.split('&');
const filtered_arr = arrNames.map(item => set_arr.find(it => it.type_name === item)).filter(Boolean);
return filtered_arr.length? filtered_arr : [set_arr[0]];
} catch (e) {
console.error('ctSet 执行异常:', e.message);
return kArr;
}
}
function safeParseJSON(jStr){
try {return JSON.parse(jStr);} catch(e) {return null;}
}
function cutStr(str, prefix = '', suffix = '', defVal = '', clean = true, i = 0, all = false) {
try {
if (typeof str !== 'string') {throw new Error('被截取对象必须为字符串');}
const cleanStr = cs => String(cs).replace(/<[^>]*?>/g, ' ').replace(/(&nbsp;|[\u0020\u00A0\u3000\s])+/g, ' ').trim().replace(/\s+/g, ' ');
const esc = s => String(s).replace(/[.*+?${}()|[\]\\/^]/g, '\\$&');
let pre = esc(prefix).replace(/£/g, '[^]*?'), end = esc(suffix);
const regex = new RegExp(`${pre || '^'}([^]*?)${end || '$'}`, 'g');
const matchIter = str.matchAll(regex);
if (all) {
let matchArr = [...matchIter];
if (!matchArr.length) {return [defVal];}
return matchArr.map(ela => ela[1] !== undefined ? (clean ? cleanStr(ela[1]) : ela[1]) : defVal);
}
const idx = parseInt(i, 10);
if (isNaN(idx)) {throw new Error('序号必须为整数');}
let tgResult, matchIdx = 0;
if (idx >= 0) {
for (let elt of matchIter) {
if (matchIdx++ === idx) {
tgResult = elt[1];
break;
}
}
} else {
let absI = Math.abs(idx), ringBuf = new Array(absI), ringPtr = 0, ringCnt = 0;
for (let elt of matchIter) {
ringBuf[ringPtr] = elt[1];
ringPtr = (ringPtr + 1) % absI;
ringCnt = Math.min(ringCnt + 1, absI);
matchIdx++;
}
tgResult = (matchIdx >= absI && ringCnt > 0) ? ringBuf[ringPtr % ringCnt] : undefined;
}
return tgResult !== undefined ? (clean ? (cleanStr(tgResult) || defVal) : tgResult) : defVal;
} catch (e) {
console.error(`字符串截取错误:`, e.message);
return all ? ['cutErr'] : 'cutErr';
}
}
async function request(reqUrl, options = {}) {
try {
if (typeof reqUrl !== 'string' || !reqUrl.trim()) { throw new Error('reqUrl需为字符串且非空'); }
if (typeof options !== 'object' || Array.isArray(options) || options === null) { throw new Error('options类型需为非null对象'); }
options.method = options.method?.toUpperCase() || 'GET';
if (['GET', 'HEAD'].includes(options.method)) {
delete options.body;
delete options.data;
delete options.postType;
}
let {headers, timeout, ...restOpts} = options;
const optObj = {
headers: (typeof headers === 'object' && !Array.isArray(headers) && headers) ? headers : KParams.headers,
timeout: parseInt(timeout, 10) > 0 ? parseInt(timeout, 10) : KParams.timeout,
...restOpts
};
const res = await req(reqUrl, optObj);
if (options.withHeaders) {
const resHeaders = typeof res.headers === 'object' && !Array.isArray(res.headers) && res.headers ? res.headers : {};
const resWithHeaders = { ...resHeaders, body: res?.content ?? '' };
return JSON.stringify(resWithHeaders);
}
return res?.content ?? '';
} catch (e) {
console.error(`${reqUrl}→请求失败:`, e.message);
return options?.withHeaders ? JSON.stringify({ body: '' }) : '';
}
}
export function __jsEvalReturn() {
return {
init,
home,
homeVod,
category,
search,
detail,
play,
proxy: null
};
}
+429
View File
@@ -0,0 +1,429 @@
if (typeof Object.assign !== 'function') {
Object.assign = function() {
let target = arguments[0];
for (let i = 1; i < arguments.length; i++) {
let source = arguments[i];
for (let key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
}
// 通用免嗅探播放
let common_lazy = `js:
let html = request(input);
let hconf = html.match(/r player_.*?=(.*?)</)[1];
let json = JSON5.parse(hconf);
let url = json.url;
if (json.encrypt == '1') {
url = unescape(url);
} else if (json.encrypt == '2') {
url = unescape(base64Decode(url));
}
if (/\\.(m3u8|mp4|m4a|mp3)/.test(url)) {
input = {
parse: 0,
jx: 0,
url: url,
};
} else {
input = url && url.startsWith('http') && tellIsJx(url) ? {parse:0,jx:1,url:url}:input;
}`;
// 默认嗅探播放
let def_lazy = `js:
input = { parse: 1, url: input, js: '' };`;
// 采集站播放
let cj_lazy = `js:
if (/\\.(m3u8|mp4)/.test(input)) {
input = { parse: 0, url: input };
} else {
if (rule.parse_url.startsWith('json:')) {
let purl = rule.parse_url.replace('json:', '') + input;
let html = request(purl);
let json = JSON.parse(html);
if (json.url) {
input = { parse: 0, url: json.url };
}
} else {
input = rule.parse_url + input;
}
}`;
function getMubans() {
const mubanDict = { // 模板字典
mx: {
title: '',
host: '',
url: '/vodshow/fyclass--------fypage---/',
searchUrl: '/vodsearch/**----------fypage---/',
class_parse: '.top_nav li;a&&Text;a&&href;.*/(.*?)/',
searchable: 2,
quickSearch: 0,
filterable: 0,
headers: {
'User-Agent': 'MOBILE_UA',
},
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true,
推荐: '.cbox_list;*;*;*;*;*',
一级: 'ul.vodlist li;a&&title;a&&data-original;.pic_text&&Text;a&&href',
二级: {
title: 'h2&&Text;.content_detail:eq(1)&&li&&a:eq(2)&&Text',
img: '.vodlist_thumb&&data-original',
desc: '.content_detail:eq(1)&&li:eq(1)&&Text;.content_detail:eq(1)&&li&&a&&Text;.content_detail:eq(1)&&li&&a:eq(1)&&Text;.content_detail:eq(1)&&li:eq(2)&&Text;.content_detail:eq(1)&&li:eq(3)&&Text',
content: '.content_desc&&span&&Text',
tabs: '.play_source_tab&&a',
lists: '.content_playlist:eq(#id) li',
},
搜索: '*',
},
mxpro: {
title: '',
host: '', // homeUrl:'/',
url: '/vodshow/fyclass--------fypage---.html',
searchUrl: '/vodsearch/**----------fypage---.html',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
headers: { //网站的请求头,完整支持所有的,常带ua和cookies
'User-Agent': 'MOBILE_UA', // "Cookie": "searchneed=ok"
},
class_parse: '.navbar-items li:gt(0):lt(10);a&&Text;a&&href;/(\\d+)',
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true, // 推荐内容是否双层定位
推荐: '.tab-list.active;a.module-poster-item.module-item;.module-poster-item-title&&Text;.lazyload&&data-original;.module-item-note&&Text;a&&href',
一级: 'body a.module-poster-item.module-item;a&&title;.lazyload&&data-original;.module-item-note&&Text;a&&href',
二级: {
title: 'h1&&Text;.module-info-tag-link:eq(-1)&&Text',
img: '.lazyload&&data-original||data-src||src',
desc: '.module-info-item:eq(-2)&&Text;.module-info-tag-link&&Text;.module-info-tag-link:eq(1)&&Text;.module-info-item:eq(2)&&Text;.module-info-item:eq(1)&&Text',
content: '.module-info-introduction&&Text',
tabs: '.module-tab-item',
lists: '.module-play-list:eq(#id) a',
tab_text: 'div--small&&Text',
},
搜索: 'body .module-item;.module-card-item-title&&Text;.lazyload&&data-original;.module-item-note&&Text;a&&href;.module-info-item-content&&Text',
},
mxone5: {
title: '',
host: '',
url: '/show/fyclass--------fypage---.html',
searchUrl: '/search/**----------fypage---.html',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
class_parse: '.nav-menu-items&&li;a&&Text;a&&href;.*/(.*?)\.html',
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true, // 推荐内容是否双层定位
推荐: '.module-list;.module-items&&.module-item;a&&title;img&&data-src;.module-item-text&&Text;a&&href',
一级: '.module-items .module-item;a&&title;img&&data-src;.module-item-text&&Text;a&&href',
二级: {
title: 'h1&&Text;.tag-link&&Text',
img: '.module-item-pic&&img&&data-src',
desc: '.video-info-items:eq(3)&&Text;.tag-link:eq(2)&&Text;.tag-link:eq(1)&&Text;.video-info-items:eq(1)&&Text;.video-info-items:eq(0)&&Text',
content: '.vod_content&&Text',
tabs: '.module-tab-item',
lists: '.module-player-list:eq(#id)&&.scroll-content&&a',
tab_text: 'div--small&&Text',
},
搜索: '.module-items .module-search-item;a&&title;img&&data-src;.video-serial&&Text;a&&href',
},
首图: {
title: '',
host: '',
url: '/vodshow/fyclass--------fypage---/',
searchUrl: '/vodsearch/**----------fypage---.html',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
headers: { //网站的请求头,完整支持所有的,常带ua和cookies
'User-Agent': 'MOBILE_UA', // "Cookie": "searchneed=ok"
},
class_parse: '.myui-header__menu li.hidden-sm:gt(0):lt(7);a&&Text;a&&href;/(\\d+).html',
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true, // 推荐内容是否双层定位
推荐: 'ul.myui-vodlist.clearfix;li;a&&title;a&&data-original;.pic-text&&Text;a&&href',
一级: '.myui-vodlist li;a&&title;a&&data-original;.pic-text&&Text;a&&href',
二级: {
title: '.myui-content__detail .title--span&&Text;.myui-content__detail p.data:eq(3)&&Text',
img: '.myui-content__thumb .lazyload&&data-original',
desc: '.myui-content__detail p.otherbox&&Text;.year&&Text;.myui-content__detail p.data:eq(4)&&Text;.myui-content__detail p.data:eq(2)&&Text;.myui-content__detail p.data:eq(0)&&Text',
content: '.content&&Text',
tabs: '.myui-panel__head&&li',
// tabs: '.nav-tabs&&li',
lists: '.myui-content__list:eq(#id) li',
},
搜索: '#searchList li;a&&title;.lazyload&&data-original;.pic-text&&Text;a&&href;.detail&&Text',
},
首图2: {
title: '',
host: '',
url: '/list/fyclass-fypage.html',
searchUrl: '/vodsearch/**----------fypage---.html',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
headers: {
'User-Agent': 'UC_UA', // "Cookie": ""
},
class_parse: '.stui-header__menu li:gt(0):lt(7);a&&Text;a&&href;.*/(.*?).html',
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true, // 推荐内容是否双层定位
推荐: 'ul.stui-vodlist.clearfix;li;a&&title;.lazyload&&data-original;.pic-text&&Text;a&&href',
一级: '.stui-vodlist li;a&&title;a&&data-original;.pic-text&&Text;a&&href',
二级: {
title: '.stui-content__detail .title&&Text;.stui-content__detail&&p:eq(-2)&&a&&Text',
title1: '.stui-content__detail .title&&Text;.stui-content__detail&&p&&Text',
img: '.stui-content__thumb .lazyload&&data-original',
desc: '.stui-content__detail p&&Text;.stui-content__detail&&p:eq(-2)&&a:eq(2)&&Text;.stui-content__detail&&p:eq(-2)&&a:eq(1)&&Text;.stui-content__detail p:eq(2)&&Text;.stui-content__detail p:eq(1)&&Text',
desc1: '.stui-content__detail p:eq(4)&&Text;;;.stui-content__detail p:eq(1)&&Text',
content: '.detail&&Text',
tabs: '.stui-pannel__head h3',
tabs1: '.stui-vodlist__head h3',
lists: '.stui-content__playlist:eq(#id) li',
},
搜索: 'ul.stui-vodlist__media,ul.stui-vodlist,#searchList li;a&&title;.lazyload&&data-original;.pic-text&&Text;a&&href;.detail&&Text',
},
默认: {
title: '',
host: '',
url: '',
searchUrl: '',
searchable: 2,
quickSearch: 0,
filterable: 0,
filter: '',
filter_url: '',
filter_def: {},
headers: {
'User-Agent': 'MOBILE_UA',
},
timeout: 5000,
class_parse: '#side-menu li;a&&Text;a&&href;/(.*?)\.html',
cate_exclude: '',
play_parse: true,
lazy: def_lazy,
double: true,
推荐: '列表1;列表2;标题;图片;描述;链接;详情',
一级: '列表;标题;图片;描述;链接;详情',
二级: {
title: 'vod_name;vod_type',
img: '图片链接',
desc: '主要信息;年代;地区;演员;导演',
content: '简介',
tabs: '',
lists: 'xx:eq(#id)&&a',
tab_text: 'body&&Text',
list_text: 'body&&Text',
list_url: 'a&&href',
},
搜索: '列表;标题;图片;描述;链接;详情',
},
vfed: {
title: '',
host: '',
url: '/index.php/vod/show/id/fyclass/page/fypage.html',
searchUrl: '/index.php/vod/search/page/fypage/wd/**.html',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
headers: {
'User-Agent': 'UC_UA',
},
class_parse: '.fed-pops-navbar&&ul.fed-part-rows&&a;a&&Text;a&&href;.*/(.*?).html',
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true, // 推荐内容是否双层定位
推荐: 'ul.fed-list-info.fed-part-rows;li;a.fed-list-title&&Text;a&&data-original;.fed-list-remarks&&Text;a&&href',
一级: '.fed-list-info&&li;a.fed-list-title&&Text;a&&data-original;.fed-list-remarks&&Text;a&&href',
二级: {
title: 'h1.fed-part-eone&&Text;.fed-deta-content&&.fed-part-rows&&li&&Text',
img: '.fed-list-info&&a&&data-original',
desc: '.fed-deta-content&&.fed-part-rows&&li:eq(1)&&Text;.fed-deta-content&&.fed-part-rows&&li:eq(2)&&Text;.fed-deta-content&&.fed-part-rows&&li:eq(3)&&Text',
content: '.fed-part-esan&&Text',
tabs: '.fed-drop-boxs&&.fed-part-rows&&li',
lists: '.fed-play-item:eq(#id)&&ul:eq(1)&&li',
},
搜索: '.fed-deta-info;h1&&Text;.lazyload&&data-original;.fed-list-remarks&&Text;a&&href;.fed-deta-content&&Text',
},
海螺3: {
title: '',
host: '',
searchUrl: '/v_search/**----------fypage---.html',
url: '/vod_____show/fyclass--------fypage---.html',
headers: {
'User-Agent': 'MOBILE_UA',
},
timeout: 5000,
class_parse: 'body&&.hl-nav li:gt(0);a&&Text;a&&href;.*/(.*?).html',
cate_exclude: '明星|专题|最新|排行',
limit: 40,
play_parse: true,
lazy: common_lazy,
double: true,
推荐: '.hl-vod-list;li;a&&title;a&&data-original;.remarks&&Text;a&&href',
一级: '.hl-vod-list&&.hl-list-item;a&&title;a&&data-original;.remarks&&Text;a&&href',
二级: {
title: '.hl-dc-title&&Text;.hl-dc-content&&li:eq(6)&&Text',
img: '.hl-lazy&&data-original',
desc: '.hl-dc-content&&li:eq(10)&&Text;.hl-dc-content&&li:eq(4)&&Text;.hl-dc-content&&li:eq(5)&&Text;.hl-dc-content&&li:eq(2)&&Text;.hl-dc-content&&li:eq(3)&&Text',
content: '.hl-content-text&&Text',
tabs: '.hl-tabs&&a',
tab_text: 'a--span&&Text',
lists: '.hl-plays-list:eq(#id)&&li',
},
搜索: '.hl-list-item;a&&title;a&&data-original;.remarks&&Text;a&&href',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
},
海螺2: {
title: '',
host: '',
searchUrl: '/index.php/vod/search/page/fypage/wd/**/',
url: '/index.php/vod/show/id/fyclass/page/fypage/',
headers: {
'User-Agent': 'MOBILE_UA',
},
timeout: 5000,
class_parse: '#nav-bar li;a&&Text;a&&href;id/(.*?)/',
limit: 40,
play_parse: true,
lazy: common_lazy,
double: true,
推荐: '.list-a.size;li;a&&title;.lazy&&data-original;.bt&&Text;a&&href',
一级: '.list-a&&li;a&&title;.lazy&&data-original;.list-remarks&&Text;a&&href',
二级: {
title: 'h2&&Text;.deployment&&Text',
img: '.lazy&&data-original',
desc: '.deployment&&Text',
content: '.ec-show&&Text',
tabs: '#tag&&a',
lists: '.play_list_box:eq(#id)&&li',
},
搜索: '.search-list;a&&title;.lazy&&data-original;.deployment&&Text;a&&href',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
},
短视: {
title: '',
host: '', // homeUrl:'/',
url: '/channel/fyclass-fypage.html',
searchUrl: '/search.html?wd=**',
searchable: 2, //是否启用全局搜索,
quickSearch: 0, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
headers: { //网站的请求头,完整支持所有的,常带ua和cookies
'User-Agent': 'MOBILE_UA', // "Cookie": "searchneed=ok"
},
class_parse: '.menu_bottom ul li;a&&Text;a&&href;.*/(.*?).html',
cate_exclude: '解析|动态',
play_parse: true,
lazy: common_lazy,
limit: 6,
double: true, // 推荐内容是否双层定位
推荐: '.indexShowBox;ul&&li;a&&title;img&&data-src;.s1&&Text;a&&href',
一级: '.pic-list&&li;a&&title;img&&data-src;.s1&&Text;a&&href',
二级: {
title: 'h1&&Text;.content-rt&&p:eq(0)&&Text',
img: '.img&&img&&data-src',
desc: '.content-rt&&p:eq(1)&&Text;.content-rt&&p:eq(2)&&Text;.content-rt&&p:eq(3)&&Text;.content-rt&&p:eq(4)&&Text;.content-rt&&p:eq(5)&&Text',
content: '.zkjj_a&&Text',
tabs: '.py-tabs&&option',
lists: '.player:eq(#id) li',
},
搜索: '.sr_lists&&ul&&li;h3&&Text;img&&data-src;.int&&p:eq(0)&&Text;a&&href',
},
短视2: {
title: '',
host: '',
class_name: '电影&电视剧&综艺&动漫',
class_url: '1&2&3&4',
searchUrl: '/index.php/ajax/suggest?mid=1&wd=**&limit=50',
searchable: 2,
quickSearch: 0,
headers: {
'User-Agent': 'MOBILE_UA'
},
url: '/index.php/api/vod#type=fyclass&page=fypage',
filterable: 0, //是否启用分类筛选,
filter_url: '',
filter: {},
filter_def: {},
detailUrl: '/index.php/vod/detail/id/fyid.html',
play_parse: true,
lazy: common_lazy,
limit: 6,
推荐: '.list-vod.flex .public-list-box;a&&title;.lazy&&data-original;.public-list-prb&&Text;a&&href',
一级: 'js:let body=input.split("#")[1];let t=Math.round(new Date/1e3).toString();let key=md5("DS"+t+"DCC147D11943AF75");let url=input.split("#")[0];body=body+"&time="+t+"&key="+key;print(body);fetch_params.body=body;let html=post(url,fetch_params);let data=JSON.parse(html);VODS=data.list.map(function(it){it.vod_pic=urljoin2(input.split("/i")[0],it.vod_pic);return it});',
二级: {
title: '.slide-info-title&&Text;.slide-info:eq(2)--strong&&Text',
img: '.detail-pic&&data-original',
desc: '.slide-info-remarks&&Text;.slide-info-remarks:eq(1)&&Text;.slide-info-remarks:eq(2)&&Text;.slide-info:eq(1)--strong&&Text;.info-parameter&&ul&&li:eq(3)&&Text',
content: '#height_limit&&Text',
tabs: '.anthology.wow.fadeInUp.animated&&.swiper-wrapper&&a',
tab_text: 'a--span&&Text',
lists: '.anthology-list-box:eq(#id) li',
},
搜索: 'json:list;name;pic;;id',
},
采集1: {
title: '',
host: '',
homeTid: '13',
homeUrl: '/api.php/provide/vod/?ac=detail&t={{rule.homeTid}}',
detailUrl: '/api.php/provide/vod/?ac=detail&ids=fyid',
searchUrl: '/api.php/provide/vod/?wd=**&pg=fypage',
url: '/api.php/provide/vod/?ac=detail&pg=fypage&t=fyclass',
headers: {
'User-Agent': 'MOBILE_UA'
},
timeout: 5000, // class_name: '电影&电视剧&综艺&动漫',
// class_url: '1&2&3&4',
// class_parse:'js:let html=request(input);input=JSON.parse(html).class;',
class_parse: 'json:class;',
limit: 20,
multi: 1,
searchable: 2, //是否启用全局搜索,
quickSearch: 1, //是否启用快速搜索,
filterable: 0, //是否启用分类筛选,
play_parse: true,
parse_url: '',
lazy: cj_lazy,
推荐: '*',
一级: 'json:list;vod_name;vod_pic;vod_remarks;vod_id;vod_play_from',
二级: `js:
let html=request(input);
html=JSON.parse(html);
let data=html.list;
VOD=data[0];`,
搜索: '*',
},
};
return JSON.parse(JSON.stringify(mubanDict));
}
var mubanDict = getMubans();
var muban = getMubans();
export default {
muban,
getMubans
};
+138
View File
@@ -0,0 +1,138 @@
/**
* 毒舌影视 - 2026/01/22 源码定点适配版 (移除console.log)
* 适配路径: /dsshiyidt/
* 适配图片: data-original
*/
const host = 'https://www.xnhrsb.com';
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': host + '/'
};
async function init(cfg) {}
const m = (s, r, i = 1) => (s.match(r) || [])[i] || "";
const fixPic = p => p && p.startsWith('/') ? host + p : p;
/**
* 核心解析:针对 <li> 结构进行精准提取
*/
function getList(html) {
let list = [];
// 源码特征:<li class="..."><a href="/dsshiyidt/ID.html">...<img data-original="PIC" alt="NAME">...</h3>
// 使用 pdfa 提取所有 li 元素
const items = pdfa(html, ".bt_img ul li");
items.forEach(it => {
// 提取 ID (匹配 dsshiyidt 或 dsshiyixq 或 dsshiyipy 以防万一)
let id = m(it, /href="\/dsshiyi(?:dt|xq|py)\/(.*?)\.html"/);
if (!id) return;
// 提取图片:源码明确使用 data-original
let pic = m(it, /data-original="([^"]+)"/) || m(it, /src="([^"]+)"/);
// 提取标题:源码在 alt 属性里,也在 dytit 类的 a 标签里
let name = m(it, /alt="([^"]+)"/) || m(it, /class="dytit"[\s\S]*?>(.*?)<\/a>/);
name = name ? name.replace(/<.*?>/g, "").trim() : "";
// 提取备注:源码在 hdinfo 类里
let remarks = m(it, /class="hdinfo">[\s\S]*?<span>(.*?)<\/span>/);
if (id && name) {
list.push({
vod_id: id,
vod_name: name,
vod_pic: fixPic(pic),
vod_remarks: remarks.trim()
});
}
});
return list;
}
async function home(filter) {
return JSON.stringify({
class: [
{ type_id: "1", type_name: "电影" },
{ type_id: "2", type_name: "电视剧" },
{ type_id: "3", type_name: "综艺" },
{ type_id: "4", type_name: "动漫" },
{ type_id: "5", type_name: "短剧" }
]
});
}
async function homeVod() {
const r = await req(host, { headers });
return JSON.stringify({ list: getList(r.content) });
}
async function category(tid, pg, filter, extend = {}) {
let p = pg || 1;
// 适配源码中的 11 横杠分页逻辑
// 第一页: /dsshiyisw/1-----------.html
// 第二页: /dsshiyisw/1--------2---.html
let url = `${host}/dsshiyisw/${tid}-----------.html`;
if (p > 1) {
url = `${host}/dsshiyisw/${tid}--------${p}---.html`;
}
const r = await req(url, { headers });
const list = getList(r.content);
// 已移除 console.log
return JSON.stringify({ page: p, list: list });
}
async function detail(id) {
// 详情页路径统一尝试 dt (源码中新发现的路径)
const r = await req(`${host}/dsshiyidt/${id}.html`, { headers });
const h = r.content;
// 详情页解析:标题在 h1,图片在 dyimg 类的 img 里
const name = m(h, /<h1[^>]*>(.*?)<\/h1>/);
const pic = fixPic(m(h, /class="dyimg"[\s\S]*?src="(.*?)"/));
const content = m(h, /id="p_content"[\s\S]*?>(.*?)<\/div>/s).replace(/<.*?>/g, "").trim();
// 播放源解析
const playFrom = pdfa(h, ".mi_paly_box .ypxingq_t").map(it => it.replace(/<.*?>/g, "").trim()).join("$$$") || "播放源";
const playUrl = pdfa(h, ".paly_list_btn").map(l =>
pdfa(l, "a").map(it => {
let aName = it.replace(/<.*?>/g, "").trim();
let aId = m(it, /href="\/dsshiyipy\/(.*?)\.html"/);
if (!aId || aName.includes('立即播放')) return null;
return `${aName}$${aId}`;
}).filter(Boolean).join("#")
).join("$$$");
return JSON.stringify({
list: [{
vod_id: id,
vod_name: name,
vod_pic: pic,
vod_content: content || "暂无简介",
vod_play_from: playFrom,
vod_play_url: playUrl
}]
});
}
async function search(wd, quick, pg = 1) {
let url = `${host}/dsshiyisc/${encodeURIComponent(wd)}----------${pg}---.html`;
const r = await req(url, { headers });
return JSON.stringify({ list: getList(r.content) });
}
async function play(flag, id, flags) {
const r = await req(`${host}/dsshiyipy/${id}.html`, { headers });
const html = r.content;
// 提取播放器配置中的 m3u8 地址
let u = m(html, /"url"\s*:\s*"([^"]+\.m3u8[^"]*)"/);
if (u) return JSON.stringify({ parse: 0, url: u.replace(/\\/g, ""), header: headers });
// 如果没有直链,调用 webview 解析
return JSON.stringify({ parse: 1, url: `${host}/dsshiyipy/${id}.html` });
}
export default { init, home, homeVod, category, detail, search, play };
+107
View File
@@ -0,0 +1,107 @@
let host = 'https://www.ylys.tv';
let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": host + "/"
};
async function init(cfg) {}
/**
* 通用解析:不做过滤,不做干扰
*/
function getList(html) {
let videos = [];
let items = pdfa(html, ".module-item,.module-card-item");
items.forEach(it => {
let idMatch = it.match(/detail\/(\d+)/);
let nameMatch = it.match(/title="(.*?)"/) || it.match(/<strong>(.*?)<\/strong>/);
let picMatch = it.match(/data-original="(.*?)"/) || it.match(/src="(.*?)"/);
if (idMatch && nameMatch) {
let pic = picMatch ? (picMatch[1] || picMatch[2]) : "";
videos.push({
"vod_id": idMatch[1],
"vod_name": nameMatch[1].replace(/<.*?>/g, ""),
"vod_pic": pic.startsWith('/') ? host + pic : pic,
"vod_remarks": (it.match(/module-item-note">(.*?)<\/div>/) || ["",""])[1].replace(/<.*?>/g, "")
});
}
});
return videos;
}
async function home(filter) {
return JSON.stringify({
"class": [
{"type_id":"1","type_name":"电影"},
{"type_id":"2","type_name":"剧集"},
{"type_id":"3","type_name":"综艺"},
{"type_id":"4","type_name":"动漫"}
],
"filters": {
"1":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"动作片","v":"6"},{"n":"喜剧片","v":"7"},{"n":"爱情片","v":"8"},{"n":"科幻片","v":"9"},{"n":"恐怖片","v":"11"}]}],
"2":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"国产剧","v":"13"},{"n":"港台剧","v":"14"},{"n":"日剧","v":"15"},{"n":"韩剧","v":"33"},{"n":"欧美剧","v":"16"}]}],
"3":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"内地综艺","v":"27"},{"n":"港台综艺","v":"28"},{"n":"日本综艺","v":"29"},{"n":"韩国综艺","v":"36"}]}],
"4":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"国产动漫","v":"31"},{"n":"日本动漫","v":"32"},{"n":"欧美动漫","v":"42"},{"n":"其他动漫","v":"43"}]}]
}
});
}
async function homeVod() {
let resp = await req(host, { headers: headers });
return JSON.stringify({ list: getList(resp.content) });
}
async function category(tid, pg, filter, extend) {
let p = pg || 1;
let targetId = (extend && extend.class) ? extend.class : tid;
let url = host + "/vodtype/" + targetId + "/" + (parseInt(p) > 1 ? "page/" + p + "/" : "");
let resp = await req(url, { headers: headers });
return JSON.stringify({
"list": getList(resp.content),
"page": parseInt(p)
});
}
async function detail(id) {
let url = host + '/voddetail/' + id + '/';
let resp = await req(url, { headers: headers });
let html = resp.content;
let playFrom = pdfa(html, ".module-tab-item").map(it => (it.match(/<span>(.*?)<\/span>/) || ["","线路"])[1]).join('$$$');
let playUrl = pdfa(html, ".module-play-list-content").map(list =>
pdfa(list, "a").map(a => {
let n = (a.match(/<span>(.*?)<\/span>/) || ["","播放"])[1];
let v = a.match(/href="\/play\/(.*?)\/"/);
return n + '$' + (v ? v[1] : "");
}).join('#')
).join('$$$');
return JSON.stringify({
list: [{
'vod_id': id,
'vod_name': (html.match(/<h1>(.*?)<\/h1>/) || ["", ""])[1],
'vod_pic': (html.match(/data-original="(.*?)"/) || ["", ""])[1],
'vod_content': (html.match(/introduction-content">.*?<p>(.*?)<\/p>/s) || ["", ""])[1].replace(/<.*?>/g, ""),
'vod_play_from': playFrom,
'vod_play_url': playUrl
}]
});
}
async function search(wd, quick, pg) {
let p = pg || 1;
let url = host + "/vodsearch/" + encodeURIComponent(wd) + "-------------/" + (parseInt(p) > 1 ? "page/" + p + "/" : "");
let resp = await req(url, { headers: headers });
return JSON.stringify({ list: getList(resp.content) });
}
async function play(flag, id, flags) {
let url = host + "/play/" + id + "/";
let resp = await req(url, { headers: headers });
let m3u8 = resp.content.match(/"url":"([^"]+\.m3u8)"/);
if (m3u8) return JSON.stringify({ parse: 0, url: m3u8[1].replace(/\\/g, ""), header: headers });
return JSON.stringify({ parse: 1, url: url, header: headers });
}
export default { init, home, homeVod, category, detail, search, play };
+240
View File
@@ -0,0 +1,240 @@
import re
import sys
from base64 import b64encode, b64decode
from urllib.parse import quote, unquote
from pyquery import PyQuery as pq
from requests import Session, adapters
from urllib3.util.retry import Retry
from concurrent.futures import ThreadPoolExecutor, as_completed
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
self.host = "https://www.22a5.com"
self.session = Session()
adapter = adapters.HTTPAdapter(max_retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[429, 500, 502, 503, 504]), pool_connections=20, pool_maxsize=50)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
self.headers = {"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"}
self.session.headers.update(self.headers)
def getName(self): return "爱听音乐"
def isVideoFormat(self, url): return bool(re.search(r'\.(m3u8|mp4|mp3|m4a|flv)(\?|$)', url or "", re.I))
def manualVideoCheck(self): return False
def destroy(self): self.session.close()
def homeContent(self, filter):
classes = [{"type_name": n, "type_id": i} for n, i in [("歌手","/singerlist/index/index/index/index.html"), ("TOP榜单","/list/top.html"), ("新歌榜","/list/new.html"), ("电台","/radiolist/index.html"), ("高清MV","/mvlist/oumei.html"), ("专辑","/albumlist/index.html"), ("歌单","/playtype/index.html")]]
filters = {p: d for p in [c["type_id"] for c in classes if "singer" not in c["type_id"]] if (d := self._fetch_filters(p))}
if "/radiolist/index.html" not in filters:
filters["/radiolist/index.html"] = [{"key": "id", "name": "分类", "value": [{"n": n, "v": v} for n,v in zip(["最新","最热","有声小说","相声","音乐","情感","国漫","影视","脱口秀","历史","儿童","教育","八卦","推理","头条"], ["index","hot","novel","xiangyi","music","emotion","game","yingshi","talkshow","history","children","education","gossip","tuili","headline"])]}]
filters["/singerlist/index/index/index/index.html"] = [
{"key": "area", "name": "地区", "value": [{"n": n, "v": v} for n,v in [("全部","index"),("华语","huayu"),("欧美","oumei"),("韩国","hanguo"),("日本","ribrn")]]},
{"key": "sex", "name": "性别", "value": [{"n": n, "v": v} for n,v in [("全部","index"),("男","male"),("女","girl"),("组合","band")]]},
{"key": "genre", "name": "流派", "value": [{"n": n, "v": v} for n,v in [("全部","index"),("流行","liuxing"),("电子","dianzi"),("摇滚","yaogun"),("嘻哈","xiha"),("R&B","rb"),("民谣","minyao"),("爵士","jueshi"),("古典","gudian")]]},
{"key": "char", "name": "字母", "value": [{"n": n, "v": v} for n,v in [("全部","index")] + [{"n": chr(i), "v": chr(i).lower()} for i in range(65, 91)]]}
]
return {"class": classes, "filters": filters, "list": []}
def homeVideoContent(self): return {"list": []}
def categoryContent(self, tid, pg, filter, extend):
pg = int(pg or 1)
url = tid
if "/singerlist/" in tid:
p = tid.split('/')
if len(p) >= 6:
url = "/".join(p[:2] + [extend.get(k, p[i]) for i, k in enumerate(["area", "sex", "genre"], 2)] + [f"{extend.get('char', 'index')}.html"])
elif "id" in extend and extend["id"] not in ["index", "top"]:
url = tid.replace("index.html", f"{extend['id']}.html").replace("top.html", f"{extend['id']}.html")
if url == tid: url = f"{tid.rsplit('/', 1)[0]}/{extend['id']}.html"
if pg > 1:
sep = "/" if any(x in url for x in ["/singerlist/", "/radiolist/", "/mvlist/", "/playtype/", "/list/"]) else "_"
url = re.sub(r'(_\d+|/\d+)?\.html$', f'{sep}{pg}.html', url)
doc = self.getpq(url)
return {"list": self._parse_list(doc(".play_list li, .video_list li, .pic_list li, .singer_list li, .ali li, .layui-row li, .base_l li"), tid), "page": pg, "pagecount": 9999, "limit": 90, "total": 999999}
def searchContent(self, key, quick, pg="1"):
return {"list": self._parse_list(self.getpq(f"/so/{quote(key)}/{pg}.html")(".base_l li, .play_list li"), "search"), "page": int(pg)}
def detailContent(self, ids):
url = self._abs(ids[0])
doc = self.getpq(url)
vod = {"vod_id": url, "vod_name": self._clean(doc("h1").text() or doc("title").text()), "vod_pic": self._abs(doc(".djpg img, .pic img, .djpic img").attr("src")), "vod_play_from": "爱听音乐", "vod_content": ""}
if any(x in url for x in ["/playlist/", "/album/", "/list/", "/singer/", "/special/", "/radio/", "/radiolist/"]):
eps = self._get_eps(doc)
page_urls = {self._abs(a.attr("href")) for a in doc(".page a, .dede_pages a, .pagelist a").items() if a.attr("href") and "javascript" not in a.attr("href")} - {url}
if page_urls:
with ThreadPoolExecutor(max_workers=5) as ex:
for r in as_completed([ex.submit(lambda u: self._get_eps(self.getpq(u)), u) for u in sorted(page_urls, key=lambda x: int(re.search(r'[_\/](\d+)\.html', x).group(1)) if re.search(r'[_\/](\d+)\.html', x) else 0)]):
eps.extend(r.result() or [])
if eps:
vod.update({"vod_play_from": "播放列表", "vod_play_url": "#".join(eps)})
return {"list": [vod]}
play_list = []
if mid := re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', url):
lrc_url = f"{self.host}/plug/down.php?ac=music&lk=lrc&id={mid.group(2)}"
play_list = [f"播放${self.e64('0@@@@' + url + '|||' + lrc_url)}"]
elif vid := re.search(r'/(video|mp4)/([^/]+)\.html', url):
with ThreadPoolExecutor(max_workers=3) as ex:
fs = {ex.submit(self._api, "/plug/down.php", {"ac": "vplay", "id": vid.group(2), "q": q}): n for n, q in [("蓝光", 1080), ("超清", 720), ("高清", 480)]}
play_list = [f"{fs[f]}${self.e64('0@@@@'+u)}" for f in as_completed(fs) if (u := f.result())]
play_list.sort(key=lambda x: {"蓝":0, "超":1, "高":2}.get(x[0], 3))
vod["vod_play_url"] = "#".join(play_list) if play_list else f"解析失败${self.e64('1@@@@'+url)}"
return {"list": [vod]}
def playerContent(self, flag, id, vipFlags):
raw = self.d64(id).split("@@@@")[-1]
url, subt = raw.split("|||") if "|||" in raw else (raw, "")
url = url.replace(r"\/", "/")
if ".html" in url and not self.isVideoFormat(url):
if mid := re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', url):
if r_url := self._api("/js/play.php", method="POST", data={"id": mid.group(2), "type": "music"}, headers={"Referer": url.replace("http://","https://"), "X-Requested-With": "XMLHttpRequest"}):
url = r_url if ".php" not in r_url else url
elif vid := re.search(r'/(video|mp4)/([^/]+)\.html', url):
with ThreadPoolExecutor(max_workers=3) as ex:
for f in as_completed([ex.submit(self._api, "/plug/down.php", {"ac": "vplay", "id": vid.group(2), "q": q}) for q in [1080, 720, 480]]):
if v_url := f.result():
url = v_url; break
result = {"parse": 0, "url": url, "header": {"User-Agent": self.headers["User-Agent"]}}
if "22a5.com" in url: result["header"]["Referer"] = self.host + "/"
# OK影视3.6.5+支持LRC格式滚动歌词
if subt:
try:
r = self.session.get(subt, headers={"Referer": self.host + "/"}, timeout=5)
lrc_content = r.text
if lrc_content:
# 过滤广告内容
lrc_content = self._filter_lrc_ads(lrc_content)
result["lrc"] = lrc_content
except:
pass
return result
def _filter_lrc_ads(self, lrc_text):
"""过滤LRC歌词中的广告内容"""
lines = lrc_text.splitlines()
filtered_lines = []
# 广告关键词模式
ad_patterns = [
r'欢迎来访.*',
r'本站.*',
r'.*广告.*',
r'QQ群.*',
r'.*www\..*',
r'.*http.*',
r'.*\.com.*',
r'.*\.cn.*',
r'.*\.net.*',
r'.*音乐网.*',
r'.*提供.*',
r'.*下载.*',
]
for line in lines:
# 保留时间标签行,但过滤掉广告文本
if re.match(r'\[\d{2}:\d{2}', line):
# 检查是否包含广告
is_ad = False
for pattern in ad_patterns:
if re.search(pattern, line, re.IGNORECASE):
is_ad = True
break
if not is_ad:
filtered_lines.append(line)
else:
# 非时间标签行(可能是元数据),保留
filtered_lines.append(line)
return '\n'.join(filtered_lines)
def localProxy(self, param):
url = unquote(param.get("url", ""))
type_ = param.get("type")
if type_ == "img":
return [200, "image/jpeg", self.session.get(url, headers={"Referer": self.host + "/"}, timeout=5).content, {}]
elif type_ == "lrc":
try:
r = self.session.get(url, headers={"Referer": self.host + "/"}, timeout=5)
# 同时过滤代理中的广告
lrc_content = r.text
lrc_content = self._filter_lrc_ads(lrc_content)
return [200, "application/octet-stream", lrc_content.encode('utf-8'), {}]
except:
return [404, "text/plain", "Error", {}]
return None
def _parse_list(self, items, tid=""):
res = []
for li in items.items():
a = li("a").eq(0)
if not (href := a.attr("href")) or href == "/" or any(x in href for x in ["/user/", "/login/", "javascript"]): continue
if not (name := self._clean(li(".name").text() or a.attr("title") or a.text())): continue
pic = self._abs((li("img").attr("src") or "").replace('120', '500'))
res.append({"vod_id": self._abs(href), "vod_name": name, "vod_pic": f"{self.getProxyUrl()}&url={pic}&type=img" if pic else "", "style": {"type": "oval" if "/singer/" in href else ("list" if any(x in tid for x in ["/list/", "/playtype/", "/albumlist/"]) else "rect"), "ratio": 1 if "/singer/" in href else 1.33}})
return res
def _get_eps(self, doc):
eps = []
for li in doc(".play_list li, .song_list li, .music_list li").items():
if not (a := li("a").eq(0)).attr("href") or not re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', a.attr("href")): continue
full_url = self._abs(a.attr("href"))
lrc_part = ""
mid = re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', full_url)
if mid:
lrc_url = f"{self.host}/plug/down.php?ac=music&lk=lrc&id={mid.group(2)}"
lrc_part = f"|||{lrc_url}"
eps.append(f"{self._clean(a.text() or li('.name').text())}${self.e64('0@@@@' + full_url + lrc_part)}")
return eps
def _clean(self, text): return re.sub(r'(爱玩音乐网|视频下载说明|视频下载地址|www\.2t58\.com|MP3免费下载|LRC歌词下载|全部歌曲|\[第\d+页\]|刷新|每日推荐|最新|热门|推荐|MV|高清|无损)', '', text or "", flags=re.I).strip()
def _fetch_filters(self, url):
doc, filters = self.getpq(url), []
for i, group in enumerate([doc(s) for s in [".ilingku_fl", ".class_list", ".screen_list", ".box_list", ".nav_list"] if doc(s)]):
opts, seen = [{"n": "全部", "v": "top" if "top" in url else "index"}], set()
for a in group("a").items():
if (v := (a.attr("href") or "").split("?")[0].rstrip('/').split('/')[-1].replace('.html','')) and v not in seen:
opts.append({"n": a.text().strip(), "v": v}); seen.add(v)
if len(opts) > 1: filters.append({"key": f"id{i}" if i else "id", "name": "分类", "value": opts})
return filters
def _api(self, path, params=None, method="GET", headers=None, data=None):
try:
h = self.headers.copy()
if headers: h.update(headers)
r = (self.session.post if method == "POST" else self.session.get)(f"{self.host}{path}", params=params, data=data, headers=h, timeout=10, allow_redirects=False)
if loc := r.headers.get("Location"): return self._abs(loc.strip())
return self._abs(r.json().get("url", "").replace(r"\/", "/")) or (r.text.strip() if r.text.strip().startswith("http") else "")
except: return ""
def getpq(self, url):
import time
for _ in range(2):
try: return pq(self.session.get(self._abs(url), timeout=5).text)
except: time.sleep(0.1)
return pq("<html></html>")
def _abs(self, url): return url if url.startswith("http") else (f"{self.host}{'/' if not url.startswith('/') else ''}{url}" if url else "")
def e64(self, text): return b64encode(text.encode("utf-8")).decode("utf-8")
def d64(self, text): return b64decode(text.encode("utf-8")).decode("utf-8")
+279
View File
@@ -0,0 +1,279 @@
# coding=utf-8
# !/usr/bin/python
# by嗷呜(finally)
import sys
import os
sys.path.append("..")
import re
import hashlib
import hmac
import random
import string
from Crypto.Util.Padding import unpad
from concurrent.futures import ThreadPoolExecutor
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5, AES
from base64 import b64encode, b64decode
import json
import time
from base.spider import Spider
class Spider(Spider):
def getName(self):
return "电影猎手"
def init(self, extend=""):
self.device = self.device_id()
self.host = self.gethost()
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def action(self, action):
pass
def destroy(self):
pass
t = str(int(time.time()))
def homeContent(self, filter):
result = {}
filters = {}
classes = []
bba = self.url()
data = self.fetch(f"{self.host}/api/v1/app/config?pack={bba[0]}&signature={bba[1]}", headers=self.header()).text
data1 = self.aes(data)
dy = {"class":"类型","area":"地区","lang":"语言","year":"年份","letter":"字母","by":"排序","sort":"排序"}
data1['data']['movie_screen']['sort'].pop(0)
for item in data1['data']['movie_screen']['sort']:
item['n'] = item.pop('name')
item['v'] = item.pop('value')
for item in data1['data']['movie_screen']['filter']:
has_non_empty_field = False
classes.append({"type_name": item["name"], "type_id": str(item["id"])})
for key in dy:
if key in item and item[key]:
has_non_empty_field = True
break
if has_non_empty_field:
filters[str(item["id"])] = []
filters[str(item["id"])].append(
{"key": 'sort', "name": '排序', "value": data1['data']['movie_screen']['sort']})
for dkey in item:
if dkey in dy and item[dkey]:
item[dkey].pop(0)
value_array = [
{"n": value.strip(), "v": value.strip()}
for value in item[dkey]
if value.strip() != ""
]
filters[str(item["id"])].append(
{"key": dkey, "name": dy[dkey], "value": value_array}
)
result["class"] = classes
result["filters"] = filters
return result
def homeVideoContent(self):
bba = self.url()
url = f'{self.host}/api/v1/movie/index_recommend?pack={bba[0]}&signature={bba[1]}'
data = self.fetch(url, headers=self.header()).json()
videos = []
for item in data['data']:
if len(item['list']) > 0:
for it in item['list']:
try:
videos.append(self.voides(it))
except Exception as e:
continue
result = {"list": videos}
return result
def categoryContent(self, tid, pg, filter, extend):
body = {"type_id": tid, "sort": extend.get("sort", "by_default"), "class": extend.get("class", "类型"),
"area": extend.get("area", "地区"), "year": extend.get("year", "年份"), "page": str(pg),
"pageSize": "21"}
result = {}
list = []
bba = self.url(body)
url = f"{self.host}/api/v1/movie/screen/list?pack={bba[0]}&signature={bba[1]}"
data = self.fetch(url, headers=self.header()).json()['data']['list']
for item in data:
list.append(self.voides(item))
result["list"] = list
result["page"] = pg
result["pagecount"] = 9999
result["limit"] = 90
result["total"] = 999999
return result
def detailContent(self, ids):
body = {"id": ids[0]}
bba = self.url(body)
url = f'{self.host}/api/v1/movie/detail?pack={bba[0]}&signature={bba[1]}'
data = self.fetch(url, headers=self.header()).json()['data']
video = {'vod_name': data.get('name'),'type_name': data.get('type_name'),'vod_year': data.get('year'),'vod_area': data.get('area'),'vod_remarks': data.get('dynami'),'vod_content': data.get('content')}
play = []
names = []
tasks = []
for itt in data["play_from"]:
name = itt["name"]
a = []
if len(itt["list"]) > 0:
names.append(name)
play.append(self.playeach(itt['list']))
else:
tasks.append({"movie_id": ids[0], "from_code": itt["code"]})
names.append(name)
if tasks:
with ThreadPoolExecutor(max_workers=len(tasks)) as executor:
results = executor.map(self.playlist, tasks)
for result in results:
if result:
play.append(result)
else:
play.append("")
video["vod_play_from"] = "$$$".join(names)
video["vod_play_url"] = "$$$".join(play)
result = {"list": [video]}
return result
def searchContent(self, key, quick, pg=1):
body = {"keyword": key, "sort": "", "type_id": "0", "page": str(pg), "pageSize": "10",
"res_type": "by_movie_name"}
bba = self.url(body)
url = f"{self.host}/api/v1/movie/search?pack={bba[0]}&signature={bba[1]}"
data = self.fetch(url, headers=self.header()).json()['data'].get('list')
videos = []
for it in data:
try:
videos.append(self.voides(it))
except Exception as e:
continue
result = {"list": videos, "page": pg}
return result
def playerContent(self, flag, id, vipFlags):
url = id
if "m3u8" not in url and "mp4" not in url:
try:
add = id.split('|||')
data = {"from_code": add[0], "play_url": add[1], "episode_id": add[2], "type": "play"}
bba = self.url(data)
data2 = self.fetch(f"{self.host}/api/v1/movie_addr/parse_url?pack={bba[0]}&signature={bba[1]}",
headers=self.header()).json()['data']
url = data2.get('play_url') or data2.get('download_url')
try:
url1 = self.fetch(url, headers=self.header(), allow_redirects=False).headers['Location']
if url1 and "http" in url1:
url = url1
except:
pass
except Exception as e:
pass
if '.jpg' in url or '.jpeg' in url or '.png' in url:
url = self.getProxyUrl() + "&url=" + b64encode(url.encode('utf-8')).decode('utf-8') + "&type=m3u8"
result = {}
result["parse"] = 0
result["url"] = url
result["header"] = {'user-agent': 'okhttp/4.9.2'}
return result
def localProxy(self, param):
url = b64decode(param["url"]).decode('utf-8')
durl = url[:url.rfind('/')]
data = self.fetch(url, headers=self.header()).content.decode("utf-8")
lines = data.strip().split('\n')
for index, string in enumerate(lines):
# if 'URI="' in string and 'http' not in string:
# lines[index] = index
# 暂时预留,貌似用不到
if '#EXT' not in string and 'http' not in string:
lines[index] = durl + ('' if string.startswith('/') else '/') + string
data = '\n'.join(lines)
return [200, "application/vnd.apple.mpegur", data]
def device_id(self):
characters = string.ascii_lowercase + string.digits
random_string = ''.join(random.choices(characters, k=32))
return random_string
def gethost(self):
headers = {
'User-Agent': 'okhttp/4.9.2',
'Connection': 'Keep-Alive',
}
response = self.fetch('https://app-site.ecoliving168.com/domain_v5.json', headers=headers).json()
url = response['api_service'].replace('/api/', '')
return url
def header(self):
headers = {
'User-Agent': 'Android',
'Accept': 'application/prs.55App.v2+json',
'timestamp': self.t,
'x-client-setting': '{"pure-mode":1}',
'x-client-uuid': '{"device_id":' + self.device + '}, "type":1,"brand":"Redmi", "model":"M2012K10C", "system_version":30, "sdk_version":"3.1.0.7"}',
'x-client-version': '3096 '
}
return headers
def url(self, id=None):
if not id:
id = {}
id["timestamp"] = self.t
public_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA02F/kPg5A2NX4qZ5JSns+bjhVMCC6JbTiTKpbgNgiXU+Kkorg6Dj76gS68gB8llhbUKCXjIdygnHPrxVHWfzmzisq9P9awmXBkCk74Skglx2LKHa/mNz9ivg6YzQ5pQFUEWS0DfomGBXVtqvBlOXMCRxp69oWaMsnfjnBV+0J7vHbXzUIkqBLdXSNfM9Ag5qdRDrJC3CqB65EJ3ARWVzZTTcXSdMW9i3qzEZPawPNPe5yPYbMZIoXLcrqvEZnRK1oak67/ihf7iwPJqdc+68ZYEmmdqwunOvRdjq89fQMVelmqcRD9RYe08v+xDxG9Co9z7hcXGTsUquMxkh29uNawIDAQAB'
encrypted_text = json.dumps(id)
public_key = RSA.import_key(b64decode(public_key))
cipher = PKCS1_v1_5.new(public_key)
encrypted_message = cipher.encrypt(encrypted_text.encode('utf-8'))
encrypted_message_base64 = b64encode(encrypted_message).decode('utf-8')
result = encrypted_message_base64.replace('+', '-').replace('/', '_').replace('=', '')
key = '635a580fcb5dc6e60caa39c31a7bde48'
sign = hmac.new(key.encode(), result.encode(), hashlib.md5).hexdigest()
return result, sign
def playlist(self, body):
try:
bba = self.url(body)
url = f'{self.host}/api/v1/movie_addr/list?pack={bba[0]}&signature={bba[1]}'
data = self.fetch(url, headers=self.header()).json()['data']
return self.playeach(data)
except Exception:
return []
def playeach(self,data):
play_urls = []
for it in data:
if re.search(r"mp4|m3u8", it["play_url"]):
play_urls.append(f"{it['episode_name']}${it['play_url']}")
else:
play_urls.append(
f"{it['episode_name']}${it['from_code']}|||{it['play_url']}|||{it['episode_id']}"
)
return '#'.join(play_urls)
def voides(self, item):
if item['name'] or item['title']:
voide = {
"vod_id": item.get('id') or item.get('click'),
'vod_name': item.get('name') or item.get('title'),
'vod_pic': item.get('cover') or item.get('image'),
'vod_year': item.get('year') or item.get('label'),
'vod_remarks': item.get('dynamic') or item.get('sub_title')
}
return voide
def aes(self, text):
text = text.replace('-', '+').replace('_', '/') + '=='
key = b"e6d5de5fcc51f53d"
iv = b"2f13eef7dfc6c613"
cipher = AES.new(key, AES.MODE_CBC, iv)
pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size).decode("utf-8")
return json.loads(pt)
+471
View File
@@ -0,0 +1,471 @@
# coding = utf-8
#!/usr/bin/python
import re
import sys
import json
import time
import base64
import hashlib
import urllib.parse
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base.spider import Spider
sys.path.append('..')
class Spider(Spider):
def __init__(self):
self.name = "瓜子"
self.host = 'https://api.w32z7vtd.com'
self.token = '1be86e8e18a9fa18b2b8d5432699dad0.ac008ed650fd087bfbecf2fda9d82e9835253ef24843e6b18fcd128b10763497bcf9d53e959f5377cde038c20ccf9d17f604c9b8bb6e61041def86729b2fc7408bd241e23c213ac57f0226ee656e2bb0a583ae0e4f3bf6c6ab6c490c9a6f0d8cdfd366aacf5d83193671a8f77cd1af1ff2e9145de92ec43ec87cf4bdc563f6e919fe32861b0e93b118ec37d8035fbb3c.59dd05c5d9a8ae726528783128218f15fe6f2c0c8145eddab112b374fcfe3d79'
self.header = {
'Cache-Control': 'no-cache',
'Version': '2406025',
'PackageName': 'com.uf076bf0c246.qe439f0d5e.m8aaf56b725a.ifeb647346f',
'Ver': '1.9.2',
'Referer': self.host,
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'okhttp/3.12.0'
}
# 添加缓存机制
self.cache = {}
self.cache_timeout = 300 # 5分钟缓存
def getName(self):
return self.name
def init(self, extend=''):
pass
def homeContent(self, filter):
result = {}
classes = [
{"type_name": "电影", "type_id": "1"},
{"type_name": "电视剧", "type_id": "2"},
{"type_name": "动漫", "type_id": "4"},
{"type_name": "综艺", "type_id": "3"},
{"type_name": "短剧", "type_id": "64"}
]
result['class'] = classes
# 设置筛选条件 - 为所有分类添加筛选
filters = {}
for cate in classes:
tid = cate['type_id']
filters[tid] = [
{"key": "area", "name": "地区", "value": [
{"n": "全部", "v": "0"},
{"n": "大陆", "v": "大陆"},
{"n": "香港", "v": "香港"},
{"n": "台湾", "v": "台湾"},
{"n": "美国", "v": "美国"},
{"n": "韩国", "v": "韩国"},
{"n": "日本", "v": "日本"},
{"n": "英国", "v": "英国"},
{"n": "法国", "v": "法国"},
{"n": "泰国", "v": "泰国"},
{"n": "印度", "v": "印度"},
{"n": "其他", "v": "其他"}
]},
{"key": "year", "name": "年份", "value": [
{"n": "全部", "v": "0"},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"},
{"n": "2009", "v": "2009"},
{"n": "2008", "v": "2008"},
{"n": "2007", "v": "2007"},
{"n": "2006", "v": "2006"},
{"n": "2005", "v": "2005"},
{"n": "更早", "v": "2004"}
]},
{"key": "sort", "name": "排序", "value": [
{"n": "最新", "v": "d_id"},
{"n": "最热", "v": "d_hits"},
{"n": "推荐", "v": "d_score"}
]}
]
result['filters'] = filters
return result
def homeVideoContent(self):
# 首页推荐直接返回空列表,避免加载问题
return {'list': []}
def categoryContent(self, tid, pg, filter, extend):
videos = []
try:
body = {
"area": extend.get('area', '0'),
"year": extend.get('year', '0'),
"pageSize": "30",
"sort": extend.get('sort', 'd_id'),
"page": str(pg),
"tid": tid
}
cache_key = f"category_{tid}_{pg}_{hash(str(body))}"
data = self.get_cached_data(cache_key, body, '/App/IndexList/indexList')
if data and 'list' in data:
for item in data['list']:
vod_continu = item.get('vod_continu', 0)
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}集'
video = {
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
"vod_name": item.get('vod_name', ''),
"vod_pic": item.get('vod_pic', ''),
"vod_remarks": remarks
}
videos.append(video)
except Exception as e:
print(f"获取分类内容失败: {e}")
return {
'list': videos,
'page': int(pg),
'pagecount': 9999,
'limit': 30,
'total': 999999
}
def detailContent(self, ids):
try:
vod_id = ids[0].split('/')[0]
# 获取视频详情
t = str(int(time.time()))
body1 = {
"token_id": "1649412",
"vod_id": vod_id,
"mobile_time": t,
"token": self.token
}
qdata = self.get_data(body1, '/App/IndexPlay/playInfo')
# 获取播放列表
body2 = {
"vurl_cloud_id": "2",
"vod_d_id": vod_id
}
jdata = self.get_data(body2, '/App/Resource/Vurl/show')
if not qdata or 'vodInfo' not in qdata:
return {'list': []}
vod = qdata['vodInfo']
# 构建视频信息
video_detail = {
"vod_id": vod_id,
"vod_name": vod.get('vod_name', ''),
"vod_pic": vod.get('vod_pic', ''),
"vod_year": vod.get('vod_year', ''),
"vod_area": vod.get('vod_area', ''),
"vod_actor": vod.get('vod_actor', ''),
"vod_director": vod.get('vod_director', ''),
"vod_content": vod.get('vod_use_content', '').strip(),
"vod_play_from": "瓜子专线"
}
# 构建播放列表
play_list = []
if jdata and 'list' in jdata:
for index, item in enumerate(jdata['list']):
if 'play' in item:
n = [] # 播放源名称
p = [] # 播放参数
for key, value in item['play'].items():
if 'param' in value and value['param']:
n.append(key)
p.append(value['param'])
if p:
play_name = str(index + 1)
if len(jdata['list']) == 1:
play_name = vod.get('vod_name', '')
play_url = f"{p[-1]}||{'@'.join(n)}"
play_list.append(f"{play_name}${play_url}")
video_detail["vod_play_url"] = "#".join(play_list)
return {'list': [video_detail]}
except Exception as e:
print(f"获取详情失败: {e}")
return {'list': []}
def searchContent(self, key, quick, pg=1):
videos = []
try:
body = {
"keywords": key,
"order_val": "1",
"page": str(pg)
}
# 搜索不使用缓存,确保实时性
start_time = time.time()
data = self.get_data(body, '/App/Index/findMoreVod', use_cache=False)
end_time = time.time()
print(f"搜索请求耗时: {end_time - start_time:.2f}秒")
if data and 'list' in data:
for item in data['list']:
vod_continu = item.get('vod_continu', 0)
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}集'
video = {
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
"vod_name": item.get('vod_name', ''),
"vod_pic": item.get('vod_pic', ''),
"vod_remarks": remarks
}
videos.append(video)
except Exception as e:
print(f"搜索失败: {e}")
return {
'list': videos,
'page': int(pg),
'pagecount': 9999,
'limit': 30,
'total': 999999
}
def playerContent(self, flag, id, vipFlags):
try:
# 解析播放信息
parts = id.split('||')
if len(parts) < 2:
return {"parse": 0, "playUrl": "", "url": ""}
param_str = parts[0]
resolutions = parts[1].split('@') if len(parts) > 1 else []
# 解析参数
params = {}
for pair in param_str.split('&'):
if '=' in pair:
key, value = pair.split('=', 1)
params[key] = value
# 获取播放链接
if resolutions:
# 分辨率从大到小排序
resolutions.sort(key=lambda x: int(x) if x.isdigit() else 0, reverse=True)
# 使用最大分辨率
params['resolution'] = resolutions[0]
body = params
start_time = time.time()
data = self.get_data(body, '/App/Resource/VurlDetail/showOne', use_cache=False)
end_time = time.time()
print(f"播放链接获取耗时: {end_time - start_time:.2f}秒")
if data and 'url' in data:
return {
"parse": 0,
"playUrl": "",
"url": data['url'],
"header": json.dumps({"User-Agent": "Lavf/57.83.100"})
}
return {"parse": 0, "playUrl": "", "url": ""}
except Exception as e:
print(f"播放解析失败: {e}")
return {"parse": 0, "playUrl": "", "url": ""}
def isVideoFormat(self, url):
video_formats = ['.m3u8', '.mp4', '.avi', '.mkv', '.flv', '.ts']
return any(url.lower().endswith(fmt) for fmt in video_formats)
def manualVideoCheck(self):
pass
def localProxy(self, params):
return None
def aes_encrypt(self, text, key, iv):
"""AES加密"""
try:
key_bytes = key.encode('utf-8')
iv_bytes = iv.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted = cipher.encrypt(pad(text.encode('utf-8'), AES.block_size))
return encrypted.hex().upper()
except Exception as e:
print(f"AES加密失败: {e}")
return ""
def aes_decrypt(self, text, key, iv):
"""AES解密"""
try:
key_bytes = key.encode('utf-8')
iv_bytes = iv.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted_bytes = bytes.fromhex(text)
decrypted = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
return decrypted.decode('utf-8')
except Exception as e:
print(f"AES解密失败: {e}")
return ""
def rsa_decrypt(self, encrypted_data, private_key):
"""RSA解密"""
try:
# 解码base64数据
encrypted_bytes = base64.b64decode(encrypted_data)
# 导入私钥
rsa_key = RSA.import_key(private_key)
cipher = PKCS1_v1_5.new(rsa_key)
# 解密
decrypted = cipher.decrypt(encrypted_bytes, None)
return decrypted.decode('utf-8') if decrypted else ""
except Exception as e:
print(f"RSA解密失败: {e}")
return ""
def get_cached_data(self, cache_key, data, path):
"""带缓存的数据获取"""
current_time = time.time()
if cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if current_time - timestamp < self.cache_timeout:
return cached_data
# 缓存不存在或已过期,重新获取
result = self.get_data(data, path)
if result:
self.cache[cache_key] = (result, current_time)
return result
def get_data(self, data, path, use_cache=True):
"""获取数据的主要方法"""
try:
# 构建缓存键
cache_key = f"{path}_{hash(str(data))}" if use_cache else None
if use_cache and cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if time.time() - timestamp < self.cache_timeout:
return cached_data
start_time = time.time()
# AES加密请求数据
request_key = self.aes_encrypt(json.dumps(data), 'mvXBSW7ekreItNsT', '2U3IrJL8szAKp0Fj')
if not request_key:
return None
# 生成签名
t = str(int(time.time()))
keys = "Qmxi5ciWXbQzkr7o+SUNiUuQxQEf8/AVyUWY4T/BGhcXBIUz4nOyHBGf9A4KbM0iKF3yp9M7WAY0rrs5PzdTAOB45plcS2zZ0wUibcXuGJ29VVGRWKGwE9zu2vLwhfgjTaaDpXo4rby+7GxXTktzJmxvneOUdYeHi+PZsThlvPI="
sign_str = f"token_id=,token={self.token},phone_type=1,request_key={request_key},app_id=1,time={t},keys={keys}*&zvdvdvddbfikkkumtmdwqppp?|4Y!s!2br"
signature = hashlib.md5(sign_str.encode()).hexdigest()
# 构建请求体
body = {
'token': self.token,
'token_id': '',
'phone_type': '1',
'time': t,
'phone_model': 'xiaomi-22021211rc',
'keys': keys,
'request_key': request_key,
'signature': signature,
'app_id': '1',
'ad_version': '1'
}
# 发送请求 - 设置超时时间
url = f"{self.host}{path}"
response = self.post(url, headers=self.header, data=body, timeout=10)
if response.status_code != 200:
print(f"API请求失败: {response.status_code}, 路径: {path}")
return None
response_data = response.json()
if 'data' not in response_data:
print(f"API返回数据格式错误, 路径: {path}")
return None
data_response = response_data['data']
# RSA解密响应密钥
private_key = """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGAe6hKrWLi1zQmjTT1
ozbE4QdFeJGNxubxld6GrFGximxfMsMB6BpJhpcTouAqywAFppiKetUBBbXwYsYU
1wNr648XVmPmCMCy4rY8vdliFnbMUj086DU6Z+/oXBdWU3/b1G0DN3E9wULRSwcK
ZT3wj/cCI1vsCm3gj2R5SqkA9Y0CAwEAAQKBgAJH+4CxV0/zBVcLiBCHvSANm0l7
HetybTh/j2p0Y1sTXro4ALwAaCTUeqdBjWiLSo9lNwDHFyq8zX90+gNxa7c5EqcW
V9FmlVXr8VhfBzcZo1nXeNdXFT7tQ2yah/odtdcx+vRMSGJd1t/5k5bDd9wAvYdI
DblMAg+wiKKZ5KcdAkEA1cCakEN4NexkF5tHPRrR6XOY/XHfkqXxEhMqmNbB9U34
saTJnLWIHC8IXys6Qmzz30TtzCjuOqKRRy+FMM4TdwJBAJQZFPjsGC+RqcG5UvVM
iMPhnwe/bXEehShK86yJK/g/UiKrO87h3aEu5gcJqBygTq3BBBoH2md3pr/W+hUM
WBsCQQChfhTIrdDinKi6lRxrdBnn0Ohjg2cwuqK5zzU9p/N+S9x7Ck8wUI53DKm8
jUJE8WAG7WLj/oCOWEh+ic6NIwTdAkEAj0X8nhx6AXsgCYRql1klbqtVmL8+95KZ
K7PnLWG/IfjQUy3pPGoSaZ7fdquG8bq8oyf5+dzjE/oTXcByS+6XRQJAP/5ciy1b
L3NhUhsaOVy55MHXnPjdcTX0FaLi+ybXZIfIQ2P4rb19mVq1feMbCXhz+L1rG8oa
t5lYKfpe8k83ZA==
-----END PRIVATE KEY-----"""
bodyki_json = self.rsa_decrypt(data_response['keys'], private_key)
if not bodyki_json:
print("RSA解密失败")
return None
bodyki = json.loads(bodyki_json)
# AES解密响应数据
decrypted_data = self.aes_decrypt(data_response['response_key'], bodyki['key'], bodyki['iv'])
if not decrypted_data:
print("AES解密失败")
return None
result = json.loads(decrypted_data)
end_time = time.time()
print(f"数据获取耗时: {end_time - start_time:.2f}秒, 路径: {path}")
# 缓存结果
if use_cache and cache_key:
self.cache[cache_key] = (result, time.time())
return result
except Exception as e:
print(f"获取数据失败: {e}, 路径: {path}")
return None
def get_md5(self, text):
"""计算MD5"""
return hashlib.md5(text.encode()).hexdigest()
if __name__ == '__main__':
pass
+179
View File
@@ -0,0 +1,179 @@
# -*- coding: utf-8 -*-
# @Author : Doubebly
# @Time : 2025/1/21 23:07
import hashlib
import re
import sys
import time
import requests
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def getName(self):
return "LCYS"
def init(self, extend):
self.home_url = 'https://www.hkybqufgh.com'
self.error_url = './qyg0.js'
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
}
def getDependence(self):
return []
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeContent(self, filter):
return {'class': [
{
'type_id': '1',
'type_name': '电影'
},
{
'type_id': '2',
'type_name': '电视剧'
},
{
'type_id': '4',
'type_name': '动漫'
},
{
'type_id': '3',
'type_name': '综艺'
}
]}
def homeVideoContent(self):
a = self.get_data(self.home_url)
return {'list': a, 'parse': 0, 'jx': 0}
def categoryContent(self, cid, page, filter, ext):
url = self.home_url + f'/vod/show/id/{cid}/page/{page}'
data = self.get_data(url)
return {'list': data, 'parse': 0, 'jx': 0}
def detailContent(self, did):
ids = did[0]
data = self.get_detail_data(ids)
return {"list": data, 'parse': 0, 'jx': 0}
def searchContent(self, key, quick, page='1'):
if int(page) > 1:
return {'list': [], 'parse': 0, 'jx': 0}
url = self.home_url + f'/vod/search/{key}'
data = self.get_data(url)
return {'list': data, 'parse': 0, 'jx': 0}
def playerContent(self, flag, pid, vipFlags):
url = self.get_play_data(pid)
return {"url": url, "header": self.headers, "parse": 1, "jx": 0}
def localProxy(self, params):
pass
def destroy(self):
return '正在Destroy'
def get_data(self, url):
data = []
try:
res = requests.get(url, headers=self.headers)
if res.status_code != 200:
return data
vod_id_s = re.findall(r'\\"vodId\\":(.*?),', res.text)
vod_name_s = re.findall(r'\\"vodName\\":\\"(.*?)\\"', res.text)
vod_pic_s = re.findall(r'\\"vodPic\\":\\"(.*?)\\"', res.text)
vod_remarks_s = re.findall(r'\\"vodRemarks\\":\\"(.*?)\\"', res.text)
for i in range(len(vod_id_s)):
data.append(
{
'vod_id': vod_id_s[i],
'vod_name': vod_name_s[i],
'vod_pic': vod_pic_s[i],
'vod_remarks': vod_remarks_s[i],
}
)
except requests.RequestException as e:
print(e)
return data
def get_detail_data(self, ids):
url = self.home_url + f'/api/mw-movie/anonymous/video/detail?id={ids}'
t = str(int(time.time() * 1000))
headers = self.get_headers(t, f'id={ids}&key=cb808529bae6b6be45ecfab29a4889bc&t={t}')
try:
res = requests.get(url, headers=headers)
if res.status_code != 200:
return []
i = res.json()['data']
urls = []
for ii in res.json()['data']['episodeList']:
name = ii['name']
url = ii['nid']
urls.append(f'{name}${ids}-{url}')
data = {
'type_name': i['vodClass'],
'vod_id': i['vodId'],
'vod_name': i['vodName'],
'vod_remarks': i['vodRemarks'],
'vod_year': i['vodYear'],
'vod_area': i['vodArea'],
'vod_actor': i['vodActor'],
'vod_director': i['vodDirector'],
'vod_content': i['vodContent'],
'vod_play_from': '装歌专线',
'vod_play_url': '#'.join(urls),
}
return [data]
except requests.RequestException as e:
print(e)
return []
def get_play_data(self, play):
info = play.split('-')
_id = info[0]
_pid = info[1]
url = self.home_url + f'/api/mw-movie/anonymous/v2/video/episode/url?id={_id}&nid={_pid}'
t = str(int(time.time() * 1000))
headers = self.get_headers(t, f'id={_id}&nid={_pid}&key=cb808529bae6b6be45ecfab29a4889bc&t={t}')
try:
res = requests.get(url, headers=headers)
if res.status_code != 200:
return self.error_url
return res.json()['data']['list'][0]['url']
except requests.RequestException as e:
print(e)
return self.error_url
@staticmethod
def get_headers(t, e):
sign = hashlib.sha1(hashlib.md5(e.encode()).hexdigest().encode()).hexdigest()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'sign': sign,
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
't': t,
'referer': 'https://www.hkybqufgh.com/',
}
return headers
if __name__ == '__main__':
pass
+152
View File
@@ -0,0 +1,152 @@
/**
* 荐片 新式 JS0 接口源
* 适配 FongMi TVBox 最新规范
*/
let host = 'https://api.ztcgi.com';
let UA = 'Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36;webank/h5face;webank/1.0;netType:NETWORK_WIFI;appVersion:416;packageName:com.jp3.xg3';
let imghost = '';
/**
* 初始化配置
*/
async function init(cfg) {
try {
let res = await req(`${host}/api/appAuthConfig`, { headers: { 'User-Agent': UA } });
let config = JSON.parse(res.content);
imghost = `https://${config.data.imgDomain}`;
} catch (e) {
imghost = 'https://img.jianpian.com';
}
}
/**
* 首页分类与筛选
*/
async function home(filter) {
let classes = [
{type_id: '1', type_name: '电影'},
{type_id: '2', type_name: '电视剧'},
{type_id: '3', type_name: '动漫'},
{type_id: '4', type_name: '综艺'}
];
// 筛选数据模板
const filterItem = [
{"key": "cateId", "name": "分类", "value": [{"v": "1", "n": "剧情"}, {"v": "2", "n": "爱情"}, {"v": "3", "n": "动画"}, {"v": "4", "n": "喜剧"}, {"v": "5", "n": "战争"}, {"v": "6", "n": "歌舞"}, {"v": "7", "n": "古装"}, {"v": "8", "n": "奇幻"}, {"v": "9", "n": "冒险"}, {"v": "10", "n": "动作"}, {"v": "11", "n": "科幻"}, {"v": "12", "n": "悬疑"}, {"v": "13", "n": "犯罪"}, {"v": "14", "n": "家庭"}, {"v": "15", "n": "传记"}, {"v": "16", "n": "运动"}, {"v": "18", "n": "惊悚"}, {"v": "20", "n": "短片"}, {"v": "21", "n": "历史"}, {"v": "22", "n": "音乐"}, {"v": "23", "n": "西部"}, {"v": "24", "n": "武侠"}, {"v": "25", "n": "恐怖"}]},
{"key": "area", "name": "地區", "value": [{"v": "1", "n": "国产"}, {"v": "3", "n": "中国香港"}, {"v": "6", "n": "中国台湾"}, {"v": "5", "n": "美国"}, {"v": "18", "n": "韩国"}, {"v": "2", "n": "日本"}]},
{"key": "year", "name": "年代", "value": [{"v": "107", "n": "2025"}, {"v": "119", "n": "2024"}, {"v": "153", "n": "2023"}, {"v": "101", "n": "2022"}, {"v": "118", "n": "2021"}, {"v": "16", "n": "2020"}, {"v": "7", "n": "2019"}, {"v": "22", "n": "2016"}, {"v": "2015", "n": "2015以前"}]},
{"key": "sort", "name": "排序", "value": [{"v": "update", "n": "最新"}, {"v": "hot", "n": "最热"}, {"v": "rating", "n": "评分"}]}
];
let filterObj = {"1": filterItem, "2": filterItem, "3": filterItem, "4": filterItem};
return JSON.stringify({
class: classes,
filters: filterObj
});
}
/**
* 首页推荐
*/
async function homeVod() {
let html = await req(`${host}/api/slide/list?pos_id=88`, { headers: { 'User-Agent': UA, 'Referer': host } });
let res = JSON.parse(html.content);
let videos = res.data.map(item => ({
vod_id: item.jump_id,
vod_name: item.title,
vod_pic: item.thumbnail.includes('http') ? item.thumbnail : `${imghost}${item.thumbnail}`,
vod_remarks: ""
}));
return JSON.stringify({ list: videos });
}
/**
* 分类列表
*/
async function category(tid, pg, filter, extend) {
let url = `${host}/api/crumb/list?fcate_pid=${tid}&category_id=&area=${extend.area || ''}&year=${extend.year || ''}&type=${extend.cateId || ''}&sort=${extend.sort || ''}&page=${pg}`;
let html = await req(url, { headers: { 'User-Agent': UA, 'Referer': host } });
let res = JSON.parse(html.content);
let videos = res.data.map(item => ({
vod_id: item.id,
vod_name: item.title,
vod_pic: item.path.includes('http') ? item.path : `${imghost}${item.path}`,
vod_remarks: item.mask
}));
return JSON.stringify({
page: pg,
list: videos
});
}
/**
* 详情页
*/
async function detail(id) {
let html = await req(`${host}/api/video/detailv2?id=${id}`, { headers: { 'User-Agent': UA, 'Referer': host } });
let data = JSON.parse(html.content).data;
// 线路处理:将“常规线路”显示为“边下边播”
let play_from = data.source_list_source.map(item => item.name).join('$$$').replace(/常规线路/g, '边下边播');
let play_url = data.source_list_source.map(play =>
play.source_list.map(({source_name, url}) => `${source_name}$${url}`).join('#')
).join('$$$');
let vod = {
vod_id: data.id,
vod_name: data.title,
vod_year: data.year,
vod_area: data.area,
vod_remarks: data.mask,
vod_content: data.description,
vod_play_from: play_from,
vod_play_url: play_url,
vod_pic: data.thumbnail.includes('http') ? data.thumbnail : `${imghost}${data.thumbnail}`
};
return JSON.stringify({ list: [vod] });
}
/**
* 搜索功能
*/
async function search(wd, quick) {
let url = `${host}/api/v2/search/videoV2?key=${encodeURIComponent(wd)}&category_id=88&page=1&pageSize=20`;
let html = await req(url, { headers: { 'User-Agent': UA, 'Referer': host } });
let res = JSON.parse(html.content);
let videos = res.data.map(item => ({
vod_id: item.id,
vod_name: item.title,
vod_pic: item.thumbnail.includes('http') ? item.thumbnail : `${imghost}${item.thumbnail}`,
vod_remarks: item.mask
}));
return JSON.stringify({ list: videos });
}
/**
* 播放解析
*/
async function play(flag, id, flags) {
let playUrl = id;
// 判断是否需要添加专用协议前缀
if (!id.includes(".m3u8") && !id.includes(".mp4")) {
playUrl = `tvbox-xg:${id}`;
}
return JSON.stringify({
parse: 0,
url: playUrl
});
}
// 导出标准接口对象
export default {
init,
home,
homeVod,
category,
detail,
search,
play
};
+169
View File
@@ -0,0 +1,169 @@
import 'assets://js/lib/crypto-js.js';
const { HOSTS, KEY, USER_AGENT } = {
HOSTS: ["https://hnytxj.com","https://www.hkybqufgh.com","https://www.sizhengxt.com","https://www.sdzhgt.com","https://www.jiabaide.cn","https://m.9zhoukj.com","https://m.cqzuoer.com","https://www.hellosht52bwb.com"],
KEY: "cb808529bae6b6be45ecfab29a4889bc",
USER_AGENT: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Safari/537.36"
};
let currentHost = '';
const guid = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
const md5 = s => CryptoJS.MD5(s).toString();
const sha1 = s => CryptoJS.SHA1(s).toString();
const toQueryString = obj => Object.keys(obj).filter(k => obj[k] != null && obj[k] !== '').map(k => `${k}=${obj[k]}`).join('&');
const getHeaders = (params = {}) => {
const t = Date.now().toString();
const sign = sha1(md5(toQueryString({ ...params, key: KEY, t })));
return {
'User-Agent': USER_AGENT,
'Accept': 'application/json, text/plain, */*',
'sign': sign,
't': t,
'deviceid': guid()
};
};
const normalizeFieldName = k => {
const l = k.toLowerCase();
if (l.startsWith('vod') && l.length > 3) return 'vod_' + l.slice(3);
if (l.startsWith('type') && l.length > 4) return 'type_' + l.slice(4);
return l;
};
const normalizeVodList = list => (list || []).map(item => {
const res = {};
for (const [k, v] of Object.entries(item || {})) if (v != null) res[normalizeFieldName(k)] = v;
return res;
});
async function reqSafe(url, options = {}) {
try {
const res = await req(url, options);
return JSON.parse(res.content);
} catch (e) {
return {};
}
}
async function init() {
currentHost = HOSTS[Math.floor(Math.random() * HOSTS.length)];
return true;
}
async function home() {
const [cRes, fRes] = await Promise.all([
reqSafe(`${currentHost}/api/mw-movie/anonymous/get/filer/type`, { headers: getHeaders() }),
reqSafe(`${currentHost}/api/mw-movie/anonymous/v1/get/filer/list`, { headers: getHeaders() })
]);
const classes = (cRes.data || []).map(k => ({ type_name: k.typeName, type_id: k.typeId.toString() }));
const fData = fRes.data || {};
const baseSort = [{ n: "最近更新", v: "2" }, { n: "人气高低", v: "3" }, { n: "评分高低", v: "4" }];
const filters = {};
for (const [tid, d] of Object.entries(fData)) {
const sortValues = tid === '1' ? baseSort.slice(1) : baseSort;
const arr = [
{ key: "type", name: "类型", value: (d.typeList || []).map(i => ({ n: i.itemText, v: i.itemValue })) },
{ key: "area", name: "地区", value: (d.districtList || []).map(i => ({ n: i.itemText, v: i.itemText })) },
{ key: "year", name: "年份", value: (d.yearList || []).map(i => ({ n: i.itemText, v: i.itemText })) },
{ key: "lang", name: "语言", value: (d.languageList || []).map(i => ({ n: i.itemText, v: i.itemText })) },
{ key: "sort", name: "排序", value: sortValues }
];
if (d.plotList?.length) arr.splice(1, 0, { key: "v_class", name: "剧情", value: d.plotList.map(i => ({ n: i.itemText, v: i.itemText })) });
filters[tid] = arr;
}
return JSON.stringify({ class: classes, filters });
}
async function homeVod() {
const [r1, r2] = await Promise.all([
reqSafe(`${currentHost}/api/mw-movie/anonymous/v1/home/all/list`, { headers: getHeaders() }),
reqSafe(`${currentHost}/api/mw-movie/anonymous/home/hotSearch`, { headers: getHeaders() })
]);
let list = [];
const data1 = r1.data || {};
for (const k in data1) if (data1[k]?.list) list.push(...data1[k].list);
if (Array.isArray(r2.data)) list.push(...r2.data);
return JSON.stringify({ list: normalizeVodList(list) });
}
async function category(tid, pg, _, ext = {}) {
const params = {
area: ext.area || '',
filterStatus: "1",
lang: ext.lang || '',
pageNum: pg,
pageSize: "30",
sort: ext.sort || '1',
sortBy: "1",
type: ext.type || '',
type1: tid,
v_class: ext.v_class || '',
year: ext.year || ''
};
const url = `${currentHost}/api/mw-movie/anonymous/video/list?${toQueryString(params)}`;
const res = await reqSafe(url, { headers: getHeaders(params) });
const vodList = normalizeVodList(res.data?.list || []);
return JSON.stringify({
list: vodList,
page: +pg,
pagecount: 9999,
limit: 90,
total: 999999
});
}
async function detail(id) {
const res = await reqSafe(`${currentHost}/api/mw-movie/anonymous/video/detail?id=${id}`, {
headers: getHeaders({ id })
});
const vod = normalizeVodList([res.data])[0];
if (!vod) {
return JSON.stringify({ list: [{ vod_id: id, vod_name: '加载失败', vod_play_url: '' }] });
}
vod.vod_play_from = '金牌影院';
if (vod.episodelist?.length) {
const name = vod.episodelist.length > 1 ? vod.episodelist[0].name : vod.vod_name;
vod.vod_play_url = vod.episodelist.map(ep => `${name}$${id}@@${ep.nid}`).join('#');
delete vod.episodelist;
}
return JSON.stringify({ list: [vod] });
}
async function play(_, id) {
const [vid, nid] = id.split('@@');
const url = `${currentHost}/api/mw-movie/anonymous/v2/video/episode/url?clientType=1&id=${vid}&nid=${nid}`;
const res = await reqSafe(url, { headers: getHeaders({ clientType: '1', id: vid, nid: nid }) });
const urls = [];
for (const item of res.data?.list || []) {
urls.push(item.resolutionName, item.url);
}
return JSON.stringify({
parse: 0,
url: urls,
header: {
'User-Agent': USER_AGENT,
'sec-ch-ua-platform': '"Windows"',
'DNT': '1',
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
'sec-ch-ua-mobile': '?0',
'Origin': currentHost,
'Referer': currentHost + '/'
}
});
}
async function search(wd, _, pg = "1") {
const params = { keyword: wd, pageNum: pg, pageSize: "8", sourceCode: "1" };
const url = `${currentHost}/api/mw-movie/anonymous/video/searchByWord?${toQueryString(params)}`;
const res = await reqSafe(url, { headers: getHeaders(params) });
const list = normalizeVodList(res.data?.result?.list || []);
return JSON.stringify({ list, page: +pg });
}
export function __jsEvalReturn() {
return { init, home, homeVod, category, detail, play, proxy: null, search };
}
+115
View File
@@ -0,0 +1,115 @@
let host = 'https://www.guipian360.com';
let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": host + "/"
};
const extractVideos = html => {
if (!html) return [];
return pdfa(html, '.m-movies .u-movie').map(it => {
const url = it.match(/href="([^"]+)"/)?.[1] || "";
const id = url.match(/\/(\d+)\.html/)?.[1];
let name = it.match(/title="([^"]+)"/)?.[1] || "";
name = name.split('/')[0].trim().replace(/.*《([^》]+)》.*/, "$1");
const pic = it.match(/src="([^"]+)"/)?.[1] || "";
const remarks = it.match(/class="zhuangtai"[^>]*>([^<]+)/)?.[1] || "";
return id && name ? {
vod_id: id,
vod_name: name,
vod_pic: pic.startsWith('/') ? host + pic : pic,
vod_remarks: remarks.trim()
} : null;
}).filter(Boolean);
};
const home = async () => {
const list = extractVideos((await req(host, { headers }))?.content || '');
return JSON.stringify({
class: [
{type_id:"1",type_name:"鬼片大全"},{type_id:"6",type_name:"大陆鬼片"},{type_id:"9",type_name:"港台鬼片"},
{type_id:"8",type_name:"林正英鬼片"},{type_id:"7",type_name:"日韩鬼片"},{type_id:"11",type_name:"欧美鬼片"},
{type_id:"10",type_name:"泰国鬼片"},{type_id:"3",type_name:"恐怖片"},{type_id:"2",type_name:"电视剧"},
{type_id:"12",type_name:"国产剧"},{type_id:"20",type_name:"港台剧"},{type_id:"13",type_name:"美剧"},
{type_id:"14",type_name:"韩剧"},{type_id:"15",type_name:"日剧"},{type_id:"16",type_name:"泰剧"},
{type_id:"22",type_name:"其它剧"},{type_id:"4",type_name:"动漫"}
],
filters: {},
list
});
};
const homeVod = async () => {
const list = extractVideos((await req(host, { headers }))?.content || '');
return JSON.stringify({ list });
};
const category = async (tid, pg, _, extend) => {
const url = pg > 1 ? `${host}/list/${tid}_${pg}.html` : `${host}/list/${tid}.html`;
const html = (await req(url, { headers }))?.content || '';
const list = extractVideos(html);
const m = html.match(/href="\/list\/\d+_(\d+)\.html"[^>]*>\.\.(\d+)<\/a>/);
return JSON.stringify({ list, page: +pg, pagecount: m ? +m[1] : 999, limit: 20 });
};
const detail = async id => {
const html = (await req(`${host}/nv/${id}.html`, { headers }))?.content || '';
if (!html) return JSON.stringify({ list: [] });
const tabs = pdfa(html, '#tv_tab li a').map(a => (a.match(/>([^<]+)/)?.[1] || "线路").trim());
const urls = pdfa(html, '#tv_tab .list').map(list =>
pdfa(list, 'ul.abc li a').map(a => {
const name = (a.match(/>([^<]+)/)?.[1] || "播放").trim();
const vid = (a.match(/href="\/play\/(.*?)\.html"/)?.[1] || "");
return `${name}$${vid}`;
}).join('#')
);
if (!tabs.length || !urls.length) return JSON.stringify({ list: [] });
let vod_name = (html.match(/<h1>(.*?)<\/h1>/)?.[1] || "").split('/')[0].trim().replace(/.*《([^》]+)】?.*/, "$1");
const m = s => (html.match(s) || ["", ""])[1].trim();
const vod_pic = m(/<img[^>]+src="([^"]+)"[^>]*class="lazy"/);
const vod_content = (html.match(/<p class="jianjie-p"[^>]*>([\s\S]*?)<\/p>/)?.[1] || "").replace(/<.*?>/g, "").trim() || "暂无简介";
return JSON.stringify({
list: [{
vod_id: id,
vod_name,
vod_pic: vod_pic.startsWith('/') ? host + vod_pic : vod_pic,
vod_content,
vod_director: m(/<li class="hidden-xs"><span>导演:<\/span>(.*?)<\/li>/),
vod_actor: m(/<li class="hidden-xs"><span>主演:<\/span>(.*?)<\/li>/),
vod_year: m(/<strong>(\d{4})年<\/strong>/),
vod_area: m(/地区:<\/span><a[^>]*>(.*?)<\/a>/),
vod_lang: m(/语言:<\/span>(.*?)<\/li>/),
vod_play_from: tabs.join('$$$'),
vod_play_url: urls.join('$$$')
}]
});
};
const search = async (wd, _, pg = 1) => {
const url = `${host}/vodsearch/${encodeURIComponent(wd)}----------${pg}---.html`;
const list = extractVideos((await req(url, { headers }))?.content || '');
return JSON.stringify({ list });
};
const play = async (_, id) => {
const html = (await req(`${host}/play/${id}.html`, { headers }))?.content || '';
const m3u8 = html.match(/var now="([^"]+)";/);
return JSON.stringify({
parse: m3u8 ? 0 : 1,
url: m3u8 ? m3u8[1] : `${host}/play/${id}.html`,
header: headers
});
};
export default {
init: async () => {},
home,
homeVod,
category,
detail,
search,
play
};