mirror of
https://github.com/ls125781003/tvboxtg.git
synced 2026-11-02 17:53:22 +00:00
0628
This commit is contained in:
1 parent
8284dab46d
commit
3f10d7691e
57 files changed
+32224
-11646
No files matched your search
@@ -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
|
||||
@@ -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}}
|
||||
Vendored
+3108
File diff suppressed because it is too large.
Load diff
+607
@@ -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('&')
|
||||
.split('<').join('<')
|
||||
.split('>').join('>')
|
||||
.split('"').join('"');
|
||||
},
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+5858
File diff suppressed because it is too large.
Load diff
Vendored
+1439
File diff suppressed because it is too large.
Load diff
+552
@@ -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
@@ -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
@@ -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(" "," ")
|
||||
#取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
@@ -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
@@ -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(/( |[\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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
};
|
||||
Reference in new issue
Block a user