master
qist 1 day ago
parent 3aa7a57bb8
commit d95555f1b1

@ -31,16 +31,17 @@ jobs:
cd tools/ cd tools/
pip install demjson3 --break-system-packages pip install demjson3 --break-system-packages
python fty.py python fty.py
python hebing.py dianshi.json tvbox_cleaned.json python xiao.py ../xiaosa/api.json dianshi.json
python hebing.py jsm.json tvbox_cleaned.json python xiao.py ../xiaosa/api.json jsm.json
python xiao.py ../xiaosa/api.json dianshi_merged.json \cp -pdr tvbox_cleaned.json ../fty.json
python xiao.py ../xiaosa/api.json jsm_merged.json \cp -pdr dianshi_with_app_sites.json ../dianshi.json
\cp -pdr dianshi_merged_with_app_sites.json ../dianshi.json \cp -pdr jsm_with_app_sites.json ../jsm.json
\cp -pdr jsm_merged_with_app_sites.json ../jsm.json
\cp -pdr fan.txt ../jar/fan.txt \cp -pdr fan.txt ../jar/fan.txt
\cp -pdr ../xiaosa/spider.jar ../jar/spider.jar
git clone --depth=1 --recursive https://github.com/fantaiying7/EXT.git git clone --depth=1 --recursive https://github.com/fantaiying7/EXT.git
\cp -pdr EXT/* ../FTY/ \cp -pdr EXT/* ../FTY/
rm -rf dianshi_merged_with_app_sites.json jsm_merged_with_app_sites.json dianshi_merged.json jsm_merged.json tvbox_cleaned.json fan.txt EXT python copy_xbpq.py dianshi_with_app_sites.json
rm -rf dianshi_with_app_sites.json jsm_with_app_sites.json tvbox_cleaned.json fan.txt EXT
cd ../ cd ../
shell: bash shell: bash
- name: Git push assets to "release" branch - name: Git push assets to "release" branch

4
.gitignore vendored

@ -2,8 +2,8 @@ tv.txt
.vscode/ .vscode/
qiyiguo.js qiyiguo.js
mytvsuper.m3u mytvsuper.m3u
dianshi_merged_with_app_sites.json dianshi_with_app_sites.json
jsm_merged_with_app_sites.json jsm_with_app_sites.json
dianshi_merged.json dianshi_merged.json
jsm_merged.json jsm_merged.json
tvbox_cleaned.json tvbox_cleaned.json

@ -0,0 +1,70 @@
[
{
"share_name": "优品阁",
"share_id": "uWa9gbM3RJ7"
},
{
"share_name": "阿里1T",
"share_id": "mxAfB6eRgY4"
},
{
"share_name": "平凡中的",
"share_id": "4ydLxf7VgH7"
},
{
"share_name": "tacit0924",
"share_id": "DNgnCudf4cD?pwd=6666"
},
{
"share_name": "黄妈",
"share_id": "4bGRVUdUtct"
},
{
"share_name": "YYDSVIP",
"share_id": "dieULBdYP3D"
},
{
"share_name": "优源阁",
"share_id": "RnjUi1urdb2"
},
{
"share_name": "风流动漫",
"share_id": "WdaaeX7HK44"
},
{
"share_name": "风流剧集",
"share_id": "kgxWjZsK6bq"
},
{
"share_name": "xiaaluo",
"share_id": "sg8CdGUwmUr"
},
{
"share_name": "4K影视",
"share_id": "wHPKUENKFsS"
},
{
"share_name": "诺兰全集",
"share_id": "gf2GebXnZHh"
},
{
"share_name": "纪录片",
"share_id": "fSNHaYST47s"
},
{
"share_name": "掌灯者|港",
"share_id": "wHPKUENKFsS"
},
{
"share_name": "掌灯者|一",
"share_id": "wHPKUENKFsS"
},
{
"share_name": "掌灯者|二",
"share_id": "wHPKUENKFsS"
},
{
"share_name": "掌灯者|三",
"share_id": "wHPKUENKFsS"
}
]

10945
lib/cheerio.min.js vendored

File diff suppressed because one or more lines are too long

3201
lib/cloud.min.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

3202
lib/drpy2.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,607 @@
/*!
* Jinja Templating for JavaScript v0.1.8
* https://github.com/sstur/jinja-js
*
* This is a slimmed-down Jinja2 implementation [http://jinja.pocoo.org/]
*
* In the interest of simplicity, it deviates from Jinja2 as follows:
* - Line statements, cycle, super, macro tags and block nesting are not implemented
* - auto escapes html by default (the filter is "html" not "e")
* - Only "html" and "safe" filters are built in
* - Filters are not valid in expressions; `foo|length > 1` is not valid
* - Expression Tests (`if num is odd`) not implemented (`is` translates to `==` and `isnot` to `!=`)
*
* Notes:
* - if property is not found, but method '_get' exists, it will be called with the property name (and cached)
* - `{% for n in obj %}` iterates the object's keys; get the value with `{% for n in obj %}{{ obj[n] }}{% endfor %}`
* - subscript notation `a[0]` takes literals or simple variables but not `a[item.key]`
* - `.2` is not a valid number literal; use `0.2`
*
*/
/*global require, exports, module, define */
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jinja = {}));
})(this, (function(jinja) {
"use strict";
var STRINGS = /'(\\.|[^'])*'|"(\\.|[^"'"])*"/g;
var IDENTS_AND_NUMS = /([$_a-z][$\w]*)|([+-]?\d+(\.\d+)?)/g;
var NUMBER = /^[+-]?\d+(\.\d+)?$/;
//non-primitive literals (array and object literals)
var NON_PRIMITIVES = /\[[@#~](,[@#~])*\]|\[\]|\{([@i]:[@#~])(,[@i]:[@#~])*\}|\{\}/g;
//bare identifiers such as variables and in object literals: {foo: 'value'}
var IDENTIFIERS = /[$_a-z][$\w]*/ig;
var VARIABLES = /i(\.i|\[[@#i]\])*/g;
var ACCESSOR = /(\.i|\[[@#i]\])/g;
var OPERATORS = /(===?|!==?|>=?|<=?|&&|\|\||[+\-\*\/%])/g;
//extended (english) operators
var EOPS = /(^|[^$\w])(and|or|not|is|isnot)([^$\w]|$)/g;
var LEADING_SPACE = /^\s+/;
var TRAILING_SPACE = /\s+$/;
var START_TOKEN = /\{\{\{|\{\{|\{%|\{#/;
var TAGS = {
'{{{': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}\}/,
'{{': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}/,
'{%': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?%\}/,
'{#': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?#\}/
};
var delimeters = {
'{%': 'directive',
'{{': 'output',
'{#': 'comment'
};
var operators = {
and: '&&',
or: '||',
not: '!',
is: '==',
isnot: '!='
};
var constants = {
'true': true,
'false': false,
'null': null
};
function Parser() {
this.nest = [];
this.compiled = [];
this.childBlocks = 0;
this.parentBlocks = 0;
this.isSilent = false;
}
Parser.prototype.push = function(line) {
if (!this.isSilent) {
this.compiled.push(line);
}
};
Parser.prototype.parse = function(src) {
this.tokenize(src);
return this.compiled;
};
Parser.prototype.tokenize = function(src) {
var lastEnd = 0,
parser = this,
trimLeading = false;
matchAll(src, START_TOKEN, function(open, index, src) {
//here we match the rest of the src against a regex for this tag
var match = src.slice(index + open.length).match(TAGS[open]);
match = (match ? match[0] : '');
//here we sub out strings so we don't get false matches
var simplified = match.replace(STRINGS, '@');
//if we don't have a close tag or there is a nested open tag
if (!match || ~simplified.indexOf(open)) {
return index + 1;
}
var inner = match.slice(0, 0 - open.length);
//check for white-space collapse syntax
if (inner.charAt(0) === '-') var wsCollapseLeft = true;
if (inner.slice(-1) === '-') var wsCollapseRight = true;
inner = inner.replace(/^-|-$/g, '').trim();
//if we're in raw mode and we are not looking at an "endraw" tag, move along
if (parser.rawMode && (open + inner) !== '{%endraw') {
return index + 1;
}
var text = src.slice(lastEnd, index);
lastEnd = index + open.length + match.length;
if (trimLeading) text = trimLeft(text);
if (wsCollapseLeft) text = trimRight(text);
if (wsCollapseRight) trimLeading = true;
if (open === '{{{') {
//liquid-style: make {{{x}}} => {{x|safe}}
open = '{{';
inner += '|safe';
}
parser.textHandler(text);
parser.tokenHandler(open, inner);
});
var text = src.slice(lastEnd);
if (trimLeading) text = trimLeft(text);
this.textHandler(text);
};
Parser.prototype.textHandler = function(text) {
this.push('write(' + JSON.stringify(text) + ');');
};
Parser.prototype.tokenHandler = function(open, inner) {
var type = delimeters[open];
if (type === 'directive') {
this.compileTag(inner);
} else if (type === 'output') {
var extracted = this.extractEnt(inner, STRINGS, '@');
//replace || operators with ~
extracted.src = extracted.src.replace(/\|\|/g, '~').split('|');
//put back || operators
extracted.src = extracted.src.map(function(part) {
return part.split('~').join('||');
});
var parts = this.injectEnt(extracted, '@');
if (parts.length > 1) {
var filters = parts.slice(1).map(this.parseFilter.bind(this));
this.push('filter(' + this.parseExpr(parts[0]) + ',' + filters.join(',') + ');');
} else {
this.push('filter(' + this.parseExpr(parts[0]) + ');');
}
}
};
Parser.prototype.compileTag = function(str) {
var directive = str.split(' ')[0];
var handler = tagHandlers[directive];
if (!handler) {
throw new Error('Invalid tag: ' + str);
}
handler.call(this, str.slice(directive.length).trim());
};
Parser.prototype.parseFilter = function(src) {
src = src.trim();
var match = src.match(/[:(]/);
var i = match ? match.index : -1;
if (i < 0) return JSON.stringify([src]);
var name = src.slice(0, i);
var args = src.charAt(i) === ':' ? src.slice(i + 1) : src.slice(i + 1, -1);
args = this.parseExpr(args, {
terms: true
});
return '[' + JSON.stringify(name) + ',' + args + ']';
};
Parser.prototype.extractEnt = function(src, regex, placeholder) {
var subs = [],
isFunc = typeof placeholder == 'function';
src = src.replace(regex, function(str) {
var replacement = isFunc ? placeholder(str) : placeholder;
if (replacement) {
subs.push(str);
return replacement;
}
return str;
});
return {
src: src,
subs: subs
};
};
Parser.prototype.injectEnt = function(extracted, placeholder) {
var src = extracted.src,
subs = extracted.subs,
isArr = Array.isArray(src);
var arr = (isArr) ? src : [src];
var re = new RegExp('[' + placeholder + ']', 'g'),
i = 0;
arr.forEach(function(src, index) {
arr[index] = src.replace(re, function() {
return subs[i++];
});
});
return isArr ? arr : arr[0];
};
//replace complex literals without mistaking subscript notation with array literals
Parser.prototype.replaceComplex = function(s) {
var parsed = this.extractEnt(s, /i(\.i|\[[@#i]\])+/g, 'v');
parsed.src = parsed.src.replace(NON_PRIMITIVES, '~');
return this.injectEnt(parsed, 'v');
};
//parse expression containing literals (including objects/arrays) and variables (including dot and subscript notation)
//valid expressions: `a + 1 > b.c or c == null`, `a and b[1] != c`, `(a < b) or (c < d and e)`, 'a || [1]`
Parser.prototype.parseExpr = function(src, opts) {
opts = opts || {};
//extract string literals -> @
var parsed1 = this.extractEnt(src, STRINGS, '@');
//note: this will catch {not: 1} and a.is; could we replace temporarily and then check adjacent chars?
parsed1.src = parsed1.src.replace(EOPS, function(s, before, op, after) {
return (op in operators) ? before + operators[op] + after : s;
});
//sub out non-string literals (numbers/true/false/null) -> #
// the distinction is necessary because @ can be object identifiers, # cannot
var parsed2 = this.extractEnt(parsed1.src, IDENTS_AND_NUMS, function(s) {
return (s in constants || NUMBER.test(s)) ? '#' : null;
});
//sub out object/variable identifiers -> i
var parsed3 = this.extractEnt(parsed2.src, IDENTIFIERS, 'i');
//remove white-space
parsed3.src = parsed3.src.replace(/\s+/g, '');
//the rest of this is simply to boil the expression down and check validity
var simplified = parsed3.src;
//sub out complex literals (objects/arrays) -> ~
// the distinction is necessary because @ and # can be subscripts but ~ cannot
while (simplified !== (simplified = this.replaceComplex(simplified)));
//now @ represents strings, # represents other primitives and ~ represents non-primitives
//replace complex variables (those with dot/subscript accessors) -> v
while (simplified !== (simplified = simplified.replace(/i(\.i|\[[@#i]\])+/, 'v')));
//empty subscript or complex variables in subscript, are not permitted
simplified = simplified.replace(/[iv]\[v?\]/g, 'x');
//sub in "i" for @ and # and ~ and v (now "i" represents all literals, variables and identifiers)
simplified = simplified.replace(/[@#~v]/g, 'i');
//sub out operators
simplified = simplified.replace(OPERATORS, '%');
//allow 'not' unary operator
simplified = simplified.replace(/!+[i]/g, 'i');
var terms = opts.terms ? simplified.split(',') : [simplified];
terms.forEach(function(term) {
//simplify logical grouping
while (term !== (term = term.replace(/\(i(%i)*\)/g, 'i')));
if (!term.match(/^i(%i)*/)) {
throw new Error('Invalid expression: ' + src + " " + term);
}
});
parsed3.src = parsed3.src.replace(VARIABLES, this.parseVar.bind(this));
parsed2.src = this.injectEnt(parsed3, 'i');
parsed1.src = this.injectEnt(parsed2, '#');
return this.injectEnt(parsed1, '@');
};
Parser.prototype.parseVar = function(src) {
var args = Array.prototype.slice.call(arguments);
var str = args.pop(),
index = args.pop();
//quote bare object identifiers (might be a reserved word like {while: 1})
if (src === 'i' && str.charAt(index + 1) === ':') {
return '"i"';
}
var parts = ['"i"'];
src.replace(ACCESSOR, function(part) {
if (part === '.i') {
parts.push('"i"');
} else if (part === '[i]') {
parts.push('get("i")');
} else {
parts.push(part.slice(1, -1));
}
});
return 'get(' + parts.join(',') + ')';
};
//escapes a name to be used as a javascript identifier
Parser.prototype.escName = function(str) {
return str.replace(/\W/g, function(s) {
return '$' + s.charCodeAt(0).toString(16);
});
};
Parser.prototype.parseQuoted = function(str) {
if (str.charAt(0) === "'") {
str = str.slice(1, -1).replace(/\\.|"/, function(s) {
if (s === "\\'") return "'";
return s.charAt(0) === '\\' ? s : ('\\' + s);
});
str = '"' + str + '"';
}
//todo: try/catch or deal with invalid characters (linebreaks, control characters)
return JSON.parse(str);
};
//the context 'this' inside tagHandlers is the parser instance
var tagHandlers = {
'if': function(expr) {
this.push('if (' + this.parseExpr(expr) + ') {');
this.nest.unshift('if');
},
'else': function() {
if (this.nest[0] === 'for') {
this.push('}, function() {');
} else {
this.push('} else {');
}
},
'elseif': function(expr) {
this.push('} else if (' + this.parseExpr(expr) + ') {');
},
'endif': function() {
this.nest.shift();
this.push('}');
},
'for': function(str) {
var i = str.indexOf(' in ');
var name = str.slice(0, i).trim();
var expr = str.slice(i + 4).trim();
this.push('each(' + this.parseExpr(expr) + ',' + JSON.stringify(name) + ',function() {');
this.nest.unshift('for');
},
'endfor': function() {
this.nest.shift();
this.push('});');
},
'raw': function() {
this.rawMode = true;
},
'endraw': function() {
this.rawMode = false;
},
'set': function(stmt) {
var i = stmt.indexOf('=');
var name = stmt.slice(0, i).trim();
var expr = stmt.slice(i + 1).trim();
this.push('set(' + JSON.stringify(name) + ',' + this.parseExpr(expr) + ');');
},
'block': function(name) {
if (this.isParent) {
++this.parentBlocks;
var blockName = 'block_' + (this.escName(name) || this.parentBlocks);
this.push('block(typeof ' + blockName + ' == "function" ? ' + blockName + ' : function() {');
} else if (this.hasParent) {
this.isSilent = false;
++this.childBlocks;
blockName = 'block_' + (this.escName(name) || this.childBlocks);
this.push('function ' + blockName + '() {');
}
this.nest.unshift('block');
},
'endblock': function() {
this.nest.shift();
if (this.isParent) {
this.push('});');
} else if (this.hasParent) {
this.push('}');
this.isSilent = true;
}
},
'extends': function(name) {
name = this.parseQuoted(name);
var parentSrc = this.readTemplateFile(name);
this.isParent = true;
this.tokenize(parentSrc);
this.isParent = false;
this.hasParent = true;
//silence output until we enter a child block
this.isSilent = true;
},
'include': function(name) {
name = this.parseQuoted(name);
var incSrc = this.readTemplateFile(name);
this.isInclude = true;
this.tokenize(incSrc);
this.isInclude = false;
}
};
//liquid style
tagHandlers.assign = tagHandlers.set;
//python/django style
tagHandlers.elif = tagHandlers.elseif;
var getRuntime = function runtime(data, opts) {
var defaults = {
autoEscape: 'toJson'
};
var _toString = Object.prototype.toString;
var _hasOwnProperty = Object.prototype.hasOwnProperty;
var getKeys = Object.keys || function(obj) {
var keys = [];
for (var n in obj)
if (_hasOwnProperty.call(obj, n)) keys.push(n);
return keys;
};
var isArray = Array.isArray || function(obj) {
return _toString.call(obj) === '[object Array]';
};
var create = Object.create || function(obj) {
function F() {}
F.prototype = obj;
return new F();
};
var toString = function(val) {
if (val == null) return '';
return (typeof val.toString == 'function') ? val.toString() : _toString.call(val);
};
var extend = function(dest, src) {
var keys = getKeys(src);
for (var i = 0, len = keys.length; i < len; i++) {
var key = keys[i];
dest[key] = src[key];
}
return dest;
};
//get a value, lexically, starting in current context; a.b -> get("a","b")
var get = function() {
var val, n = arguments[0],
c = stack.length;
while (c--) {
val = stack[c][n];
if (typeof val != 'undefined') break;
}
for (var i = 1, len = arguments.length; i < len; i++) {
if (val == null) continue;
n = arguments[i];
val = (_hasOwnProperty.call(val, n)) ? val[n] : (typeof val._get == 'function' ? (val[n] = val._get(n)) : null);
}
return (val == null) ? '' : val;
};
var set = function(n, val) {
stack[stack.length - 1][n] = val;
};
var push = function(ctx) {
stack.push(ctx || {});
};
var pop = function() {
stack.pop();
};
var write = function(str) {
output.push(str);
};
var filter = function(val) {
for (var i = 1, len = arguments.length; i < len; i++) {
var arr = arguments[i],
name = arr[0],
filter = filters[name];
if (filter) {
arr[0] = val;
//now arr looks like [val, arg1, arg2]
val = filter.apply(data, arr);
} else {
throw new Error('Invalid filter: ' + name);
}
}
if (opts.autoEscape && name !== opts.autoEscape && name !== 'safe') {
//auto escape if not explicitly safe or already escaped
val = filters[opts.autoEscape].call(data, val);
}
output.push(val);
};
var each = function(obj, loopvar, fn1, fn2) {
if (obj == null) return;
var arr = isArray(obj) ? obj : getKeys(obj),
len = arr.length;
var ctx = {
loop: {
length: len,
first: arr[0],
last: arr[len - 1]
}
};
push(ctx);
for (var i = 0; i < len; i++) {
extend(ctx.loop, {
index: i + 1,
index0: i
});
fn1(ctx[loopvar] = arr[i]);
}
if (len === 0 && fn2) fn2();
pop();
};
var block = function(fn) {
push();
fn();
pop();
};
var render = function() {
return output.join('');
};
data = data || {};
opts = extend(defaults, opts || {});
var filters = extend({
html: function(val) {
return toString(val)
.split('&').join('&amp;')
.split('<').join('&lt;')
.split('>').join('&gt;')
.split('"').join('&quot;');
},
safe: function(val) {
return val;
},
toJson: function(val) {
if (typeof val === 'object') {
return JSON.stringify(val);
}
return toString(val);
}
}, opts.filters || {});
var stack = [create(data || {})],
output = [];
return {
get: get,
set: set,
push: push,
pop: pop,
write: write,
filter: filter,
each: each,
block: block,
render: render
};
};
var runtime;
jinja.compile = function(markup, opts) {
opts = opts || {};
var parser = new Parser();
parser.readTemplateFile = this.readTemplateFile;
var code = [];
code.push('function render($) {');
code.push('var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;');
code.push.apply(code, parser.parse(markup));
code.push('return $.render();');
code.push('}');
code = code.join('\n');
if (opts.runtime === false) {
var fn = new Function('data', 'options', 'return (' + code + ')(runtime(data, options))');
} else {
runtime = runtime || (runtime = getRuntime.toString());
fn = new Function('data', 'options', 'return (' + code + ')((' + runtime + ')(data, options))');
}
return {
render: fn
};
};
jinja.render = function(markup, data, opts) {
var tmpl = jinja.compile(markup);
return tmpl.render(data, opts);
};
jinja.templateFiles = [];
jinja.readTemplateFile = function(name) {
var templateFiles = this.templateFiles || [];
var templateFile = templateFiles[name];
if (templateFile == null) {
throw new Error('Template file not found: ' + name);
}
return templateFile;
};
/*!
* Helpers
*/
function trimLeft(str) {
return str.replace(LEADING_SPACE, '');
}
function trimRight(str) {
return str.replace(TRAILING_SPACE, '');
}
function matchAll(str, reg, fn) {
//copy as global
reg = new RegExp(reg.source, 'g' + (reg.ignoreCase ? 'i' : '') + (reg.multiline ? 'm' : ''));
var match;
while ((match = reg.exec(str))) {
var result = fn(match[0], match.index, str);
if (typeof result == 'number') {
reg.lastIndex = result;
}
}
}
}));

504
lib/jinja.min.js vendored

@ -0,0 +1,504 @@
(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+)?$/;
var NON_PRIMITIVES = /\[[@#~](,[@#~])*\]|\[\]|\{([@i]:[@#~])(,[@i]:[@#~])*\}|\{\}/g;
var IDENTIFIERS = /[$_a-z][$\w]*/gi;
var VARIABLES = /i(\.i|\[[@#i]\])*/g;
var ACCESSOR = /(\.i|\[[@#i]\])/g;
var OPERATORS = /(===?|!==?|>=?|<=?|&&|\|\||[+\-\*\/%])/g;
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) {
var match = src.slice(index + open.length).match(TAGS[open]);
match = match ? match[0] : "";
var simplified = match.replace(STRINGS, "@");
if (!match || ~simplified.indexOf(open)) {
return index + 1
}
var inner = match.slice(0, 0 - open.length);
if (inner.charAt(0) === "-") var wsCollapseLeft = true;
if (inner.slice(-1) === "-") var wsCollapseRight = true;
inner = inner.replace(/^-|-$/g, "").trim();
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 === "{{{") {
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, "@");
extracted.src = extracted.src.replace(/\|\|/g, "~").split("|");
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]
};
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")
};
Parser.prototype.parseExpr = function(src, opts) {
opts = opts || {};
var parsed1 = this.extractEnt(src, STRINGS, "@");
parsed1.src = parsed1.src.replace(EOPS, function(s, before, op, after) {
return op in operators ? before + operators[op] + after : s
});
var parsed2 = this.extractEnt(parsed1.src, IDENTS_AND_NUMS, function(s) {
return s in constants || NUMBER.test(s) ? "#" : null
});
var parsed3 = this.extractEnt(parsed2.src, IDENTIFIERS, "i");
parsed3.src = parsed3.src.replace(/\s+/g, "");
var simplified = parsed3.src;
while (simplified !== (simplified = this.replaceComplex(simplified)));
while (simplified !== (simplified = simplified.replace(/i(\.i|\[[@#i]\])+/, "v")));
simplified = simplified.replace(/[iv]\[v?\]/g, "x");
simplified = simplified.replace(/[@#~v]/g, "i");
simplified = simplified.replace(OPERATORS, "%");
simplified = simplified.replace(/!+[i]/g, "i");
var terms = opts.terms ? simplified.split(",") : [simplified];
terms.forEach(function(term) {
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();
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(",") + ")"
};
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 + '"'
}
return JSON.parse(str)
};
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;
this.isSilent = true
},
include: function(name) {
name = this.parseQuoted(name);
var incSrc = this.readTemplateFile(name);
this.isInclude = true;
this.tokenize(incSrc);
this.isInclude = false
}
};
tagHandlers.assign = tagHandlers.set;
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
};
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;
val = filter.apply(data, arr)
} else {
throw new Error("Invalid filter: " + name)
}
}
if (opts.autoEscape && name !== opts.autoEscape && name !== "safe") {
val = filters[opts.autoEscape].call(data, val)
}
output.push(val)
};
var each = function(obj, loopvar, fn1, fn2) {
if (obj == null) return;
var arr = isArray(obj) ? obj : getKeys(obj),
len = arr.length;
var ctx = {
loop: {
length: len,
first: arr[0],
last: arr[len - 1]
}
};
push(ctx);
for (var i = 0; i < len; i++) {
extend(ctx.loop, {
index: i + 1,
index0: i
});
fn1(ctx[loopvar] = arr[i])
}
if (len === 0 && fn2) fn2();
pop()
};
var block = function(fn) {
push();
fn();
pop()
};
var render = function() {
return output.join("")
};
data = data || {};
opts = extend(defaults, opts || {});
var filters = extend({
html: function(val) {
return toString(val).split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;").split('"').join("&quot;")
},
safe: function(val) {
return val
},
toJson: function(val) {
if (typeof val === "object") {
return JSON.stringify(val)
}
return toString(val)
}
}, opts.filters || {});
var stack = [create(data || {})],
output = [];
return {
get: get,
set: set,
push: push,
pop: pop,
write: write,
filter: filter,
each: each,
block: block,
render: render
}
};
var runtime;
jinja.compile = function(markup, opts) {
opts = opts || {};
var parser = new Parser;
parser.readTemplateFile = this.readTemplateFile;
var code = [];
code.push("function render($) {");
code.push("var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;");
code.push.apply(code, parser.parse(markup));
code.push("return $.render();");
code.push("}");
code = code.join("\n");
if (opts.runtime === false) {
var fn = new Function("data", "options", "return (" + code + ")(runtime(data, options))")
} else {
runtime = runtime || (runtime = getRuntime.toString());
fn = new Function("data", "options", "return (" + code + ")((" + runtime + ")(data, options))")
}
return {
render: fn
}
};
jinja.render = function(markup, data, opts) {
var tmpl = jinja.compile(markup);
return tmpl.render(data, opts)
};
jinja.templateFiles = [];
jinja.readTemplateFile = function(name) {
var templateFiles = this.templateFiles || [];
var templateFile = templateFiles[name];
if (templateFile == null) {
throw new Error("Template file not found: " + name)
}
return templateFile
};
function trimLeft(str) {
return str.replace(LEADING_SPACE, "")
}
function trimRight(str) {
return str.replace(TRAILING_SPACE, "")
}
function matchAll(str, reg, fn) {
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
}
}
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1439
lib/pako.min.js vendored

File diff suppressed because it is too large Load Diff

3057
lib/quark.min.js vendored

File diff suppressed because it is too large Load Diff

1
lib/uri.min.js vendored

File diff suppressed because one or more lines are too long

@ -0,0 +1,107 @@
import json
import os
import shutil
import sys
def load_json(path):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def find_index_by_key(sites, key):
for i, item in enumerate(sites):
if isinstance(item, dict) and item.get("key") == key:
return i
return -1
def extract_local_paths(value):
"""提取字符串中的本地路径(支持 ./ 和 ../),忽略 URL"""
if not isinstance(value, str):
return []
paths = []
# 先按 ? 切分,保留路径及查询参数里的路径
parts = value.split("?")
for part in parts:
# 再按常见分隔符切
for token in part.replace("&", " ").replace("$", " ").split():
token = token.strip()
if token.startswith("./") or token.startswith("../"):
# 去掉可能的尾部参数
token = token.split("&", 1)[0]
token = token.split("$", 1)[0]
paths.append(token)
return paths
def iter_paths_between(sites, start_key, end_key):
start_idx = find_index_by_key(sites, start_key)
end_idx = find_index_by_key(sites, end_key)
if start_idx == -1 or end_idx == -1 or start_idx >= end_idx:
return []
subset = sites[start_idx + 1 : end_idx]
all_paths = []
for item in subset:
if not isinstance(item, dict):
continue
# api 仅拷贝 .py
api_val = item.get("api")
if isinstance(api_val, str):
for p in extract_local_paths(api_val):
if p.endswith(".py"):
all_paths.append(p)
# ext 拷贝所有本地路径
ext_val = item.get("ext")
if isinstance(ext_val, str):
all_paths.extend(extract_local_paths(ext_val))
# 去重,保留顺序
seen = set()
deduped = []
for p in all_paths:
if p not in seen:
seen.add(p)
deduped.append(p)
return deduped
def main():
if len(sys.argv) < 2:
print("用法: python copy_xbpq.py <json路径>")
print("示例: python copy_xbpq.py jsm_with_app_sites.json")
sys.exit(1)
json_path = sys.argv[1]
data = load_json(json_path)
sites = data.get("sites", [])
paths = iter_paths_between(sites, "cbh", "push_agent")
if not paths:
print("⚠️ 未找到 cbh 到 push_agent 之间的 ./XBPQ/ 路径")
return
base_dir = os.path.dirname(os.path.abspath(__file__))
src_base = os.path.normpath(os.path.join(base_dir, "..", "xiaosa"))
dst_base = os.path.normpath(os.path.join(base_dir, ".."))
copied = 0
missing = 0
for path in paths:
rel_path = path.replace("./", "", 1)
rel_path = rel_path.replace("../", "", 1)
src = os.path.join(src_base, rel_path)
dst = os.path.join(dst_base, rel_path)
if not os.path.exists(src):
print(f"⚠️ 源文件不存在: {src}")
missing += 1
continue
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
print(f"✅ 已覆盖: {dst}")
copied += 1
print(f"完成: 复制 {copied} 个,缺失 {missing}")
if __name__ == "__main__":
main()

@ -1,5 +1,5 @@
{ {
"spider":"./jar/fan.txt;md5;8432d174d72d5b608ae1bcd16d966847", "spider":"./jar/spider.jar;md5;8432d174d72d5b608ae1bcd16d966847",
"lives":[ "lives":[
{"name":"live","boot": false,"type":0,"url":"./tv.txt","playerType":2,"ua":"okhttp/3.8.1","timeout":20,"epg":"https://epg.112114.xyz/?ch={name}&date={date}","logo":"https://logo.wyfc.qzz.io/{name}.png"} {"name":"live","boot": false,"type":0,"url":"./tv.txt","playerType":2,"ua":"okhttp/3.8.1","timeout":20,"epg":"https://epg.112114.xyz/?ch={name}&date={date}","logo":"https://logo.wyfc.qzz.io/{name}.png"}
], ],
@ -7,10 +7,10 @@
"sites":[ "sites":[
{"key":"drpy_js_豆瓣","name":"搜索 | 豆瓣[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/drpy.js","searchable":1,"quickSearch":0,"changeable":0}, {"key":"drpy_js_豆瓣","name":"搜索 | 豆瓣[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/drpy.js","searchable":1,"quickSearch":0,"changeable":0},
{"key":"drpy_js_TVB云播","name":"影视 | TVB云播[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/TVB云播.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"drpy_js_TVB云播","name":"影视 | TVB云播[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/TVB云播.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"新闪雷","name":"新闪雷┃MP4","type":3,"api":"./FTY/drpy2.min.js","ext":"./js/新闪雷.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"新闪雷","name":"新闪雷┃MP4","type":3,"api":"./lib/drpy2.min.js","ext":"./js/新闪雷.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"闪雷","name":"闪雷┃MP4","type":3,"api":"./FTY/drpy2.min.js","ext":"./js/678.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"闪雷","name":"闪雷┃MP4","type":3,"api":"./lib/drpy2.min.js","ext":"./js/678.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"fyyy","name":"飞宇影院","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"http://ntfeiyu.com/nt/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$1#电视剧$2#综艺$3#动漫$4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"},"jar": "./jar/sx.jar;md5;6186f490eadf878ba5de21a7fa29e594"}, {"key":"fyyy","name":"飞宇影院","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"http://ntfeiyu.com/nt/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$1#电视剧$2#综艺$3#动漫$4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"cbh","name":"茶杯狐","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"https://www.cupfox7.com/cupfox/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$fenlei1#电视剧$fenlei2#综艺$fenlei3#动漫$fenlei4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"},"jar": "./jar/sx.jar;md5;6186f490eadf878ba5de21a7fa29e594"}, {"key":"cbh","name":"茶杯狐","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"https://www.cupfox7.com/cupfox/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$fenlei1#电视剧$fenlei2#综艺$fenlei3#动漫$fenlei4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"奇优","name":"影视 | ️奇优[直连]","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"ext":"./json/奇优.json","jar": "./jar/Yoursmile.jar;md5;97796a79a781055669f2d5442439dc5f"}, {"key":"奇优","name":"影视 | ️奇优[直连]","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"ext":"./json/奇优.json","jar": "./jar/Yoursmile.jar;md5;97796a79a781055669f2d5442439dc5f"},
{"key": "电影天堂","name": "影视 | 电影天堂","type": 1,"api": "http://caiji.dyttzyapi.com/api.php/provide/vod","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2}, {"key": "电影天堂","name": "影视 | 电影天堂","type": 1,"api": "http://caiji.dyttzyapi.com/api.php/provide/vod","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2},

@ -2,6 +2,7 @@ import requests
import re import re
import demjson3 as demjson import demjson3 as demjson
import json import json
import hashlib
# 创建全局 session 并设置浏览器 UA # 创建全局 session 并设置浏览器 UA
session = requests.Session() session = requests.Session()
@ -31,6 +32,14 @@ def extract_and_save_spider(json_text):
f.write(resp.content) f.write(resp.content)
print("✅ 已保存为 fan.txt") print("✅ 已保存为 fan.txt")
# 计算本地文件 MD5
def get_md5(filepath):
md5 = hashlib.md5()
with open(filepath, "rb") as f:
while chunk := f.read(8192):
md5.update(chunk)
return md5.hexdigest()
# 删除不需要的 sites 项 + 替换链接 # 删除不需要的 sites 项 + 替换链接
def clean_data(raw_text): def clean_data(raw_text):
# 统一把各种 GitHub 代理壳替换掉 # 统一把各种 GitHub 代理壳替换掉
@ -42,18 +51,18 @@ def clean_data(raw_text):
data = demjson.decode(raw_text) data = demjson.decode(raw_text)
keywords = [ # keywords = [
"", "饭太硬", "广告", "PanSso", "YpanSo", "xzso", "米搜", "夸搜", "Aliso", "YiSo" # "豆", "饭太硬", "广告", "PanSso", "YpanSo", "xzso", "米搜", "夸搜", "Aliso", "YiSo"
] # ]
original_count = len(data.get("sites", [])) # original_count = len(data.get("sites", []))
data["sites"] = [ # data["sites"] = [
s for s in data["sites"] # s for s in data["sites"]
if not any(kw in s.get("key", "") or kw in s.get("name", "") for kw in keywords) # if not any(kw in s.get("key", "") or kw in s.get("name", "") for kw in keywords)
] # ]
print(f"🧹 清理 {original_count - len(data['sites'])} 条 sites") # print(f"🧹 清理 {data - len(data['sites'])} 条 sites")
return data return data
# 格式美化保存 # 格式美化保存
@ -85,6 +94,10 @@ if __name__ == "__main__":
raw_text = fetch_raw_json() raw_text = fetch_raw_json()
extract_and_save_spider(raw_text) extract_and_save_spider(raw_text)
data = clean_data(raw_text) data = clean_data(raw_text)
# 更新 spider 为本地 fan.txt + 最新 MD5
md5_value = get_md5("fan.txt")
data["spider"] = f"./jar/fan.txt;md5;{md5_value}"
print(f"🔄 spider 已更新为: {data['spider']}")
save_json(data) save_json(data)
except Exception as e: except Exception as e:
print(f"❌ 错误: {e}") print(f"❌ 错误: {e}")

@ -1,5 +1,5 @@
{ {
"spider":"./jar/fan.txt;md5;8432d174d72d5b608ae1bcd16d966847", "spider":"./jar/spider.jar;md5;8432d174d72d5b608ae1bcd16d966847",
"lives":[ "lives":[
{"name":"migu","type":0,"url":"https://develop202.github.io/migu_video/interface.txt","playerType":1,"ua":"okhttp/3.8.1","timeout":20,"epg":"https://epg.112114.xyz/?ch={name}&date={date}","logo":"https://epg.112114.xyz/logo/{name}.png"}, {"name":"migu","type":0,"url":"https://develop202.github.io/migu_video/interface.txt","playerType":1,"ua":"okhttp/3.8.1","timeout":20,"epg":"https://epg.112114.xyz/?ch={name}&date={date}","logo":"https://epg.112114.xyz/logo/{name}.png"},
{"name":"live","type":0,"url":"https://epg.pw/test_channels.m3u","playerType":1,"ua":"okhttp/3.8.1","timeout":20,"epg":"https://epg.112114.xyz/?ch={name}&date={date}","logo":"https://epg.112114.xyz/logo/{name}.png"}, {"name":"live","type":0,"url":"https://epg.pw/test_channels.m3u","playerType":1,"ua":"okhttp/3.8.1","timeout":20,"epg":"https://epg.112114.xyz/?ch={name}&date={date}","logo":"https://epg.112114.xyz/logo/{name}.png"},
@ -9,9 +9,9 @@
"sites":[ "sites":[
{"key":"drpy_js_豆瓣","name":"搜索 | 豆瓣[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/drpy.js","searchable":1,"quickSearch":0,"changeable":0}, {"key":"drpy_js_豆瓣","name":"搜索 | 豆瓣[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/drpy.js","searchable":1,"quickSearch":0,"changeable":0},
{"key":"drpy_js_TVB云播","name":"影视 | TVB云播[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/TVB云播.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"drpy_js_TVB云播","name":"影视 | TVB云播[js]","type":3,"api":"./lib/drpy2.min.js","ext":"./js/TVB云播.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"新闪雷","name":"新闪雷┃MP4","type":3,"api":"./FTY/drpy2.min.js","ext":"./js/新闪雷.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"新闪雷","name":"新闪雷┃MP4","type":3,"api":"./lib/drpy2.min.js","ext":"./js/新闪雷.js","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"fyyy","name":"飞宇影院","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"http://ntfeiyu.com/nt/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$1#电视剧$2#综艺$3#动漫$4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"},"jar": "./jar/sx.jar;md5;6186f490eadf878ba5de21a7fa29e594"}, {"key":"fyyy","name":"飞宇影院","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"http://ntfeiyu.com/nt/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$1#电视剧$2#综艺$3#动漫$4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"cbh","name":"茶杯狐","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"www.cupfox7.com/cupfox/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$fenlei1#电视剧$fenlei2#综艺$fenlei3#动漫$fenlei4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"},"jar": "./jar/sx.jar;md5;6186f490eadf878ba5de21a7fa29e594"}, {"key":"cbh","name":"茶杯狐","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":{"分类url":"www.cupfox7.com/cupfox/{cateId}/area/{area}/by/{by}/class/{class}/lang/{lang}/page/{catePg}/year/{year}.html","分类":"电影$fenlei1#电视剧$fenlei2#综艺$fenlei3#动漫$fenlei4"},"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key":"奇优","name":"影视 | ️奇优[直连]","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"ext":"./json/奇优.json","jar": "./jar/Yoursmile.jar;md5;97796a79a781055669f2d5442439dc5f"}, {"key":"奇优","name":"影视 | ️奇优[直连]","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"ext":"./json/奇优.json","jar": "./jar/Yoursmile.jar;md5;97796a79a781055669f2d5442439dc5f"},
{"key": "电影天堂","name": "影视 | 电影天堂","type": 1,"api": "http://caiji.dyttzyapi.com/api.php/provide/vod","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2}, {"key": "电影天堂","name": "影视 | 电影天堂","type": 1,"api": "http://caiji.dyttzyapi.com/api.php/provide/vod","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2},

@ -1,15 +1,14 @@
import json import json
import requests
import sys import sys
import hashlib import hashlib
import os import os
# 默认 jar 路径和下载 URL如需下载 # jar 路径(用于计算 md5
default_jar = "./xiaosa/spider.jar" primary_jar_path = "jar/spider.jar"
default_jar_url = "../xiaosa/spider.jar" fallback_jar_path = "../xiaosa/spider.jar"
# 需要删除的站点 key # 需要删除的站点 key(在此填写即可删除)
remove_keys = {"巴士动漫"} # 可以加多个,例如 {"巴士动漫", "电影牛"} remove_keys = {"版本信息","腾讯视频","优酷视频","芒果视频","爱奇艺","三六零","豆瓣"} # 可以加多个,例如 {"巴士动漫", "电影牛"}
# 保存 JSON 文件(折叠字典数组为单行,空数组和基础数组一行) # 保存 JSON 文件(折叠字典数组为单行,空数组和基础数组一行)
class CompactJSONEncoder(json.JSONEncoder): class CompactJSONEncoder(json.JSONEncoder):
@ -38,11 +37,6 @@ def fetch_json(path_or_url):
if os.path.exists(path_or_url): if os.path.exists(path_or_url):
with open(path_or_url, "r", encoding="utf-8") as f: with open(path_or_url, "r", encoding="utf-8") as f:
return json.load(f) return json.load(f)
elif path_or_url.startswith("http://") or path_or_url.startswith("https://"):
resp = requests.get(path_or_url)
resp.raise_for_status()
return resp.json()
else:
raise ValueError(f"无效路径或 URL{path_or_url}") raise ValueError(f"无效路径或 URL{path_or_url}")
@ -53,35 +47,22 @@ def get_md5(filepath):
md5.update(chunk) md5.update(chunk)
return md5.hexdigest() return md5.hexdigest()
def replace_drpy_path(site):
def ensure_jar_with_md5(site): """将 ./js/drpy2.min.js 替换为 ./lib/drpy2.min.js"""
if not isinstance(site, dict): if not isinstance(site, dict):
return return
jar_val = site.get("jar") for field in ("api", "ext"):
if jar_val and ";md5;" in jar_val: val = site.get(field)
return # 已包含 md5 if isinstance(val, str) and val == "./js/drpy2.min.js":
if not os.path.exists(default_jar_url): site[field] = "./lib/drpy2.min.js"
print(f"⚠️ 找不到本地 jar 文件:{default_jar_url}")
return
md5_val = get_md5(default_jar_url)
site["jar"] = f"{default_jar};md5;{md5_val}"
def ensure_xyqhiker_ext_and_jar(site):
"""修正所有 csp_XYQHiker 站点的 ext 路径,并加上 jar md5"""
if not isinstance(site, dict):
return
if site.get("api") == "csp_XYQHiker":
ext_val = site.get("ext", "")
if ext_val.startswith("./XYQHiker/"):
site["ext"] = ext_val.replace("./XYQHiker/", "./xiaosa/XYQHiker/")
ensure_jar_with_md5(site)
def insert_sites_at_key(base_sites, insert_sites, key_marker): def insert_sites_at_key(base_sites, insert_sites, key_marker):
for i, item in enumerate(base_sites): for i, item in enumerate(base_sites):
if item.get("key") == key_marker: if item.get("key") == key_marker:
return base_sites[:i] + insert_sites + base_sites[i:] return base_sites[:i + 1] + insert_sites + base_sites[i + 1:]
print(f"⚠️ 未找到 key 为 {key_marker} 的插入点,追加到末尾") print(f"⚠️ 未找到 key 为 {key_marker} 的插入点,追加到末尾")
return base_sites + insert_sites return base_sites + insert_sites
@ -91,10 +72,16 @@ def remove_sites(sites, keys_to_remove):
return [s for s in sites if s.get("key") not in keys_to_remove] return [s for s in sites if s.get("key") not in keys_to_remove]
def dedupe_by_name(base_sites, insert_sites):
"""按 name 去重:若重名,优先保留 base_sites 中的条目"""
base_names = {s.get("name") for s in base_sites if isinstance(s, dict)}
return [s for s in insert_sites if s.get("name") not in base_names]
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print("用法: python script.py <远程json_url> <本地dianshi.json路径>") print("用法: python xiao.py <本地api.json路径> <本地dianshi.json路径>")
print("示例: python script.py https://raw.githubusercontent.com/qist/tvbox/master/xiaosa/api.json dianshi.json") print("示例: python xiao.py ../xiaosa/api.json dianshi.json")
sys.exit(1) sys.exit(1)
remote_url = sys.argv[1] remote_url = sys.argv[1]
@ -103,28 +90,33 @@ if __name__ == "__main__":
# 1. 下载远程 JSON # 1. 下载远程 JSON
data = fetch_json(remote_url) data = fetch_json(remote_url)
# 2. 筛选 sites只保留 name 含 APP # 2. 读取 sites不再筛选
sites = data.get("sites", []) sites = data.get("sites", [])
filtered_sites = [s for s in sites if isinstance(s, dict) and "name" in s and "APP" in s["name"]] filtered_sites = [s for s in sites if isinstance(s, dict)]
# 3. 为每个 APP site 添加 jar 字段和 md5 # 3. 不再单独追加 XYQHiker已包含在 sites 中)
for site in filtered_sites:
ensure_jar_with_md5(site)
# 3.1 处理所有 csp_XYQHiker # 3.1 不删除站点,仅移除每个站点的 jar 字段
for site in sites: before_count = len(filtered_sites)
if isinstance(site, dict) and site.get("api") == "csp_XYQHiker": removed_sites = []
ensure_xyqhiker_ext_and_jar(site) for site in filtered_sites:
filtered_sites.append(site) replace_drpy_path(site)
print(f"✅ 筛选并更新 {len(filtered_sites)} 个站点APP + XYQHiker含 jar+md5") if isinstance(site, dict) and "jar" in site:
site.pop("jar", None)
removed_count = before_count - len(filtered_sites)
print(f"✅ 更新 {len(filtered_sites)} 个站点")
# 4. 读取本地文件 # 4. 读取本地文件
with open(local_file, "r", encoding="utf-8") as f: with open(local_file, "r", encoding="utf-8") as f:
dianshi = json.load(f) dianshi = json.load(f)
# 5. 插入到 key="玩偶" 处 # 5. 插入到 key="cbh" 之后(按 name 去重,保留本地)
dianshi_sites = dianshi.get("sites", []) dianshi_sites = dianshi.get("sites", [])
dianshi["sites"] = insert_sites_at_key(dianshi_sites, filtered_sites, "玩偶") # 先按 key 删除来源站点
if remove_keys:
filtered_sites = [s for s in filtered_sites if s.get("key") not in remove_keys]
filtered_sites = dedupe_by_name(dianshi_sites, filtered_sites)
dianshi["sites"] = insert_sites_at_key(dianshi_sites, filtered_sites, "cbh")
# 6. 删除指定的站点 # 6. 删除指定的站点
before_count = len(dianshi["sites"]) before_count = len(dianshi["sites"])
@ -132,7 +124,16 @@ if __name__ == "__main__":
after_count = len(dianshi["sites"]) after_count = len(dianshi["sites"])
print(f"✅ 删除了 {before_count - after_count} 个指定站点: {', '.join(remove_keys)}") print(f"✅ 删除了 {before_count - after_count} 个指定站点: {', '.join(remove_keys)}")
# 7. 保存合并结果 # 7. 设置 spider 为 jar+md5统一在输出文件中
jar_path = primary_jar_path if os.path.exists(primary_jar_path) else fallback_jar_path
if os.path.exists(jar_path):
md5_val = get_md5(jar_path)
dianshi["spider"] = f"./jar/spider.jar;md5;{md5_val}"
print(f"🔄 spider 已更新为: {dianshi['spider']}")
else:
print(f"⚠️ 找不到 jar 文件,未更新 spider{primary_jar_path} / {fallback_jar_path}")
# 8. 保存合并结果(新文件)
output_file = f"{local_file.rsplit('.',1)[0]}_with_app_sites.json" output_file = f"{local_file.rsplit('.',1)[0]}_with_app_sites.json"
with open(output_file, "w", encoding="utf-8") as f: with open(output_file, "w", encoding="utf-8") as f:
json.dump(dianshi, f, ensure_ascii=False, indent=2, cls=CompactJSONEncoder) json.dump(dianshi, f, ensure_ascii=False, indent=2, cls=CompactJSONEncoder)

Loading…
Cancel
Save