diff --git a/base/R.py b/base/R.py new file mode 100644 index 0000000000000000000000000000000000000000..cbd41c27ba0591b832bea6da932cb52640bf113f --- /dev/null +++ b/base/R.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : R.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from flask import jsonify + +class copy_utils: + + @staticmethod + def obj_to_dic(obj): + ''' + 将传入的data对象转成字典 + ''' + result = {} + for temp in obj.__dict__: + if temp.startswith('_') or temp == 'metadata': + continue + result[temp] = getattr(obj, temp) + return result + + @staticmethod + def obj_to_list(list_obj): + ''' + 将传入的data对象转成List,list中的元素是字典 + ''' + result = [] + for obj in list_obj: + result.append(copy_utils.obj_to_dic(obj)) + return result + + +class R(object): + + @classmethod + def ok(self,msg='操作成功',data=None): + if not data: + data = [] + result = {"code": 200, "msg": msg, "data": data} + return jsonify(result) + + @classmethod + def error(self,msg="系统异常",code=404): + result = {"code": code, "msg": msg} + return jsonify(result) + + @classmethod + def success(self,msg='操作成功', data=None): + return self.ok(msg,data) + + @classmethod + def failed(self,msg="系统异常", code=404): + return self.error(msg,code) \ No newline at end of file diff --git a/base/__init__.py b/base/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c2639ce37283207665f5bba7caf01ee0ba3500d5 --- /dev/null +++ b/base/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : __init__.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from . import database +from . import config +from . import R diff --git a/base/config.py b/base/config.py new file mode 100644 index 0000000000000000000000000000000000000000..6883e17832b727ed774bd7d73b2c766e0cfbfaf8 --- /dev/null +++ b/base/config.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : config.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +DIALECT = 'mysql' +DRIVER = 'pymysql' +USERNAME = 'gp' +PASSWORD = '123456' +HOST = '127.0.0.1' +PORT = '3306' +DATABASE = 'pira' +# DB_URI = '{}+{}://{}:{}@{}:{}/{}?charset=utf8'.format(DIALECT, DRIVER, USERNAME, PASSWORD, HOST, PORT, DATABASE) +DB_URI = 'sqlite:///models/rules.db?charset=utf8&check_same_thread=False' +SQLALCHEMY_DATABASE_URI = DB_URI +SQLALCHEMY_TRACK_MODIFICATIONS = False +SQLALCHEMY_ECHO = False # 打印sql语句 +JSON_AS_ASCII = False # jsonify返回的中文正常显示 +PLAY_URL = 'http://cms.nokia.press' # 匹配远程解析服务器链接 远程接口主页地址,后面不能有/ +PLAY_URL = PLAY_URL.rstrip('/') +HTTP_HOST = '0.0.0.0' +HTTP_PORT = '5705' +PLAY_DISABLE = False # 全局禁用播放解析 +LAZYPARSE_MODE = 1 # 播放解析模式(0 本地 1 局域网 2远程 仅在全局禁用为False的时候生效) +WALL_PAPER_ENABLE = True # 启用自定义壁纸 +WALL_PAPER = "https://picsum.photos/1280/720/?blur=10" # 自定义壁纸,可注释 +SUP_PORT = 9001 # supervisord 服务端口 +RETRY_CNT = 3 # 验证码重试次数 +# OCR_API = 'http://192.168.3.224:9000/api/ocr_img' # 验证码识别接口,传参数data +OCR_API = 'http://dm.mudery.com:10000' # 验证码识别接口,传参数data +UNAME = 'admin' # 管理员账号 +PWD = 'drpy' # 管理员密码 +MAX_CONTENT_LENGTH = 1 * 1024 * 1024/100 # 100 kB +LIVE_MODE = 0 # 0 本地 1外网 +LIVE_URL = 'https://gitcode.net/qq_26898231/TVBox/-/raw/main/live/zb.txt' # 初始化外网直播地址(后续在管理界面改) +CATE_EXCLUDE = '首页|留言|APP|下载|资讯|新闻|动态' # 动态分类过滤 +# {% if config.WALL_PAPER %}"wallpaper":"{{ config.WALL_PAPER }}",{% endif %} diff --git a/base/database.py b/base/database.py new file mode 100644 index 0000000000000000000000000000000000000000..0f82ebe1396f1d8cef6c02a245893df2dc18f3bd --- /dev/null +++ b/base/database.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : databse.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from flask_sqlalchemy import SQLAlchemy +db = SQLAlchemy() \ No newline at end of file diff --git a/controllers/__init__.py b/controllers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8123dbed8b44d2a947fdc1072ef658ec46860bc8 --- /dev/null +++ b/controllers/__init__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : __init__.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from . import home +from . import admin +from . import service +from . import vod +from . import cms +from . import cls +from . import classes \ No newline at end of file diff --git a/controllers/admin.py b/controllers/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..6610954ed44c3e1f1c8bb96ec4614b0125be3c19 --- /dev/null +++ b/controllers/admin.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : admin.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 +import os + +from flask import Blueprint,request,render_template,jsonify,make_response +from controllers.service import storage_service +from base.R import R +from utils.web import verfy_token +from utils.update import getLocalVer,getOnlineVer,download_new_version,download_lives +from utils import parser +from utils.web import getParmas +from js.rules import getRules +from utils.parser import runJScode +from werkzeug.utils import secure_filename +import js2py +from utils.web import md5 + +admin = Blueprint("admin", __name__) + +# @admin.route("/",methods=['get']) +# def index(): +# return R.ok(msg='欢迎进入首页',data=None) + +# @admin.route("/info",methods=['get']) +# def info_all(): +# data = storage_service.query_all() +# return R.ok(data=data) + +@admin.route('/') +def admin_index(): # 管理员界面 + lsg = storage_service() + live_url = lsg.getItem('LIVE_URL') + print(f'live_url:',live_url) + if not verfy_token(): + return render_template('login.html') + live_url = lsg.getItem('LIVE_URL') + return render_template('admin.html', rules=getRules('js'), ver=getLocalVer(), live_url=live_url) + +@admin.route("/view/",methods=['GET']) +def admin_view_rule(name): + if not name or not name.split('.')[-1] in ['js','txt','py','json']: + return R.error(f'非法猥亵,未指定文件名。必须包含js|txt|json|py') + try: + return parser.toJs(name,'js') + except Exception as e: + return R.error(f'非法猥亵\n{e}') + +@admin.route('/clear/') +def admin_clear_rule(name): + if not name or not name.split('.')[-1] in ['js','txt','py','json']: + return R.error(f'非法猥亵,未指定文件名。必须包含js|txt|json|py') + if not verfy_token(): + return render_template('login.html') + + file_path = os.path.abspath(f'js/{name}') + print(file_path) + if not os.path.exists(file_path): + return R.error('服务端没有此文件!'+file_path) + os.remove(file_path) + return R.ok('成功删除文件:'+file_path) + +@admin.route('/get_ver') +def admin_get_ver(): + if not verfy_token(): + # return render_template('login.html') + return R.error('请登录后再试') + + return jsonify({'local_ver':getLocalVer(),'online_ver':getOnlineVer()}) + +@admin.route('/update_ver') +def admin_update_ver(): + if not verfy_token(): + return R.failed('请登录后再试') + msg = download_new_version() + return R.success(msg) + +@admin.route('/update_lives') +def admin_update_lives(): + url = getParmas('url') + if not url: + return R.failed('未提供被同步的直播源远程地址!') + if not verfy_token(): + return R.failed('请登录后再试') + live_url = url + success = download_lives(live_url) + if success: + return R.success(f'直播源{live_url}同步成功') + else: + return R.failed(f'直播源{live_url}同步失败') + +@admin.route('/write_live_url') +def admin_write_live_url(): + url = getParmas('url') + if not url: + return R.failed('未提供修改后的直播源地址!') + if not verfy_token(): + return R.failed('请登录后再试') + lsg = storage_service() + id = lsg.setItem('LIVE_URL',url) + msg = f'已修改的配置记录id为:{id}' + return R.success(msg) + +@admin.route('/upload', methods=['GET', 'POST']) +def upload_file(): + if not verfy_token(): + return render_template('login.html') + if request.method == 'POST': + try: + file = request.files['file'] + filename = secure_filename(file.filename) + print(f'推荐安全文件命名:{filename}') + savePath = f'js/{file.filename}' + if os.path.exists(savePath): + return R.failed(f'上传失败,文件已存在,请先查看删除再试') + with open('js/模板.js', encoding='utf-8') as f2: + before = f2.read() + upcode = file.stream.read().decode('utf-8') + check_to_run = before + upcode + # print(check_to_run) + try: + # js2py.eval_js(check_to_run) + loader, _ = runJScode(check_to_run) + rule = loader.eval('rule') + if not rule: + return R.failed('文件上传失败,检测到上传的文件不是drpy框架支持的源代码') + except: + return R.failed('文件上传失败,检测到上传的文件不是drpy框架支持的源代码') + print(savePath) + file.seek(0) # 读取后变成空文件,重新赋能 + file.save(savePath) + return R.success('文件上传成功') + except Exception as e: + return R.failed(f'文件上传失败!{e}') + else: + # return render_template('upload.html') + return R.failed('文件上传失败') + +@admin.route('/login',methods=['GET','POST']) +def login_api(): + username = getParmas('username') + password = getParmas('password') + autologin = getParmas('autologin') + if not all([username,password]): + return R.failed('账号密码字段必填') + token = md5(f'{username};{password}') + check = verfy_token(token=token) + if check: + # response = make_response(redirect('/admin')) + response = make_response(R.success('登录成功')) + response.set_cookie('token', token) + return response + else: + return R.failed('登录失败,用户名或密码错误') \ No newline at end of file diff --git a/controllers/classes.py b/controllers/classes.py new file mode 100644 index 0000000000000000000000000000000000000000..b14cd81f38123ab8f1d24fe6d586b16b606bec12 --- /dev/null +++ b/controllers/classes.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : classes.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from base.database import db +from utils.log import logger +from models.ruleclass import RuleClass + +def getClasses(): + if not db: + msg = '未提供数据库连接' + logger.info(msg) + return [] + res = db.session.query(RuleClass).all() + return [rc.name for rc in res] + +def getClassInfo(cls): + if not db: + msg = f'未提供数据库连接,获取{cls}详情失败' + logger.info(msg) + return None + logger.info(f'开始查询{cls}的分类详情') + res = db.session.query(RuleClass).filter(RuleClass.name == cls).first() + if res: + logger.info(str(res)) + return str(res) + else: + return f'数据库不存在{cls}的分类缓存' \ No newline at end of file diff --git a/controllers/cls.py b/controllers/cls.py new file mode 100644 index 0000000000000000000000000000000000000000..e7050c9b99717d63e6cf6598b45a6c3f4a00b04e --- /dev/null +++ b/controllers/cls.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : CLS.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from flask import Blueprint +from controllers.classes import getClasses,getClassInfo +from base.R import R +from utils.log import logger +from base.database import db +from models.ruleclass import RuleClass + +cls = Blueprint("cls", __name__) + +@cls.route('/get/') +def getClassInfoApi(cls): + info = getClassInfo(cls) + return R.ok(info) + +@cls.route('/clear/') +def clearClassApi(cls): + logger.info(f'开始查询{cls}的分类详情') + res = db.session.query(RuleClass).filter(RuleClass.name == cls) + if res: + res.delete() + db.session.commit() + return R.success(f'已清除{cls}的分类缓存') + else: + return R.failed(f'数据库不存在{cls}的分类缓存') \ No newline at end of file diff --git a/controllers/cms.py b/controllers/cms.py new file mode 100644 index 0000000000000000000000000000000000000000..ae6fae288209768ff05b3e3ed9a0448b8c60d38f --- /dev/null +++ b/controllers/cms.py @@ -0,0 +1,895 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : cms.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/8/25 + +import requests +import re +import math +from utils.web import * +from utils.system import getHost +from utils.config import playerConfig +from utils.log import logger +from utils.encode import base64Encode,baseDecode,fetch,post,request,getCryptoJS,getPreJs,buildUrl,getHome +from utils.encode import verifyCode,setDetail,join,urljoin2 +from utils.safePython import safePython +from utils.parser import runPy,runJScode,JsObjectWrapper +from utils.htmlParser import jsoup +from urllib.parse import urljoin +from concurrent.futures import ThreadPoolExecutor # 引入线程池 +from flask import url_for,redirect +from easydict import EasyDict as edict + +py_ctx = { +'requests':requests,'print':print,'base64Encode':base64Encode,'baseDecode':baseDecode, +'log':logger.info,'fetch':fetch,'post':post,'request':request,'getCryptoJS':getCryptoJS, +'buildUrl':buildUrl,'getHome':getHome,'setDetail':setDetail,'join':join,'urljoin2':urljoin2 +} +# print(getCryptoJS()) + +class CMS: + def __init__(self, rule, db=None, RuleClass=None, PlayParse=None,new_conf=None): + if new_conf is None: + new_conf = {} + self.title = rule.get('title', '') + self.id = rule.get('id', self.title) + self.lazy = rule.get('lazy', False) + self.play_disable = new_conf.get('PLAY_DISABLE',False) + self.retry_count = new_conf.get('RETRY_CNT',3) + self.lazy_mode = new_conf.get('LAZYPARSE_MODE') + self.ocr_api = new_conf.get('OCR_API') + self.cate_exclude = new_conf.get('CATE_EXCLUDE','') + try: + self.vod = redirect(url_for('vod')).headers['Location'] + except: + self.vod = '/vod' + # if not self.play_disable and self.lazy: + if not self.play_disable: + self.play_parse = rule.get('play_parse', False) + try: + play_url = getHost(self.lazy_mode) + except: + play_url = getHost(1,5705) + # play_url = new_conf.get('PLAY_URL',getHost(2)) + if not play_url.startswith('http'): + play_url = 'http://'+play_url + if self.play_parse: + # self.play_url = play_url + self.vod + '?play_url=' + self.play_url = f'{play_url}{self.vod}?rule={self.id}&play_url=' + # logger.info(f'cms重定向链接:{self.play_url}') + else: + self.play_url = '' + else: + self.play_parse = False + self.play_url = '' + # logger.info('播放免嗅地址: '+self.play_url) + + self.db = db + self.RuleClass = RuleClass + self.PlayParse = PlayParse + host = rule.get('host','').rstrip('/') + timeout = rule.get('timeout',5000) + homeUrl = rule.get('homeUrl','/') + url = rule.get('url','') + detailUrl = rule.get('detailUrl','') + searchUrl = rule.get('searchUrl','') + default_headers = getHeaders(host) + self_headers = rule.get('headers',{}) + default_headers.update(self_headers) + headers = default_headers + cookie = self.getCookie() + # print(f'{self.title}cookie:{cookie}') + if cookie: + headers['cookie'] = cookie + limit = rule.get('limit',6) + encoding = rule.get('编码', 'utf-8') + self.limit = min(limit,30) + keys = headers.keys() + for k in headers.keys(): + if str(k).lower() == 'user-agent': + v = headers[k] + if v == 'MOBILE_UA': + headers[k] = MOBILE_UA + elif v == 'PC_UA': + headers[k] = PC_UA + elif v == 'UC_UA': + headers[k] = UC_UA + lower_keys = list(map(lambda x:x.lower(),keys)) + if not 'user-agent' in lower_keys: + headers['User-Agent'] = UA + if not 'referer' in lower_keys: + headers['Referer'] = host + # print(headers) + self.headers = headers + self.host = host + self.homeUrl = urljoin(host,homeUrl) if host and homeUrl else homeUrl + if url.find('[') >-1 and url.find(']') > -1: + u1 = url.split('[')[0] + u2 = url.split('[')[1].split(']')[0] + self.url = urljoin(host,u1)+'['+urljoin(host,u2)+']' if host and url else url + else: + self.url = urljoin(host, url) if host and url else url + + self.detailUrl = urljoin(host,detailUrl) if host and detailUrl else detailUrl + self.searchUrl = urljoin(host,searchUrl) if host and searchUrl else searchUrl + self.class_name = rule.get('class_name','') + self.class_url = rule.get('class_url','') + self.class_parse = rule.get('class_parse','') + self.filter_name = rule.get('filter_name', '') + self.filter_url = rule.get('filter_url', '') + self.filter_parse = rule.get('filter_parse', '') + self.double = rule.get('double',False) + self.一级 = rule.get('一级','') + self.二级 = rule.get('二级','') + self.搜索 = rule.get('搜索','') + self.推荐 = rule.get('推荐','') + self.encoding = encoding + self.timeout = round(int(timeout)/1000,2) + self.filter = rule.get('filter',[]) + self.extend = rule.get('extend',[]) + self.d = self.getObject() + + def getName(self): + return self.title + + def getObject(self): + o = edict({ + 'jsp':jsoup(self.url), + 'getParse':self.getParse, + 'saveParse':self.saveParse, + 'headers':self.headers, + 'encoding':self.encoding, + 'name':self.title, + 'timeout':self.timeout, + }) + return o + + def regexp(self,prule,text,pos=None): + ret = re.search(prule,text).groups() + if pos != None and isinstance(pos,int): + return ret[pos] + else: + return ret + + def test(self,text,string): + searchObj = re.search(rf'{text}', string, re.M | re.I) + # print(searchObj) + # global vflag + if searchObj: + # vflag = searchObj.group() + pass + return searchObj + + def blank(self): + result = { + 'list': [] + } + return result + + def blank_vod(self): + return { + "vod_id": "", + "vod_name": "", + "vod_pic": "", + "type_name": "", + "vod_year": "", + "vod_area": "", + "vod_remarks": "", + "vod_actor": "", + "vod_director": "", + "vod_content": "" + } + + def jsoup(self): + jsp = jsoup(self.url) + pdfh = jsp.pdfh + pdfa = jsp.pdfa + pd = jsp.pd + pjfh = jsp.pjfh + pjfa = jsp.pjfa + pj = jsp.pj + + pq = jsp.pq + return pdfh,pdfa,pd,pq + + def getClasses(self): + if not self.db: + msg = '未提供数据库连接' + print(msg) + return [] + name = self.getName() + # self.db.metadata.clear() + # RuleClass = rule_classes.init(self.db) + res = self.db.session.query(self.RuleClass).filter(self.RuleClass.name == name).first() + # _logger.info('xxxxxx') + if res: + if not all([res.class_name,res.class_url]): + return [] + cls = res.class_name.split('&') + cls2 = res.class_url.split('&') + classes = [{'type_name':cls[i],'type_id':cls2[i]} for i in range(len(cls))] + # _logger.info(classes) + logger.info(f"{self.getName()}使用缓存分类:{classes}") + return classes + else: + return [] + + def getCookie(self): + name = self.getName() + if not self.db: + msg = f'{name}未提供数据库连接' + print(msg) + return False + res = self.db.session.query(self.RuleClass).filter(self.RuleClass.name == name).first() + if res: + return res.cookie or None + else: + return None + + def saveCookie(self,cookie): + name = self.getName() + if not self.db: + msg = f'{name}未提供数据库连接' + print(msg) + return False + res = self.db.session.query(self.RuleClass).filter(self.RuleClass.name == name).first() + if res: + res.cookie = cookie + self.db.session.add(res) + else: + res = self.RuleClass(name=name, cookie=cookie) + self.db.session.add(res) + try: + self.db.session.commit() + logger.info(f'{name}已保存cookie:{cookie}') + except Exception as e: + return f'保存cookie发生了错误:{e}' + + def saveClass(self, classes): + if not self.db: + msg = '未提供数据库连接' + print(msg) + return msg + name = self.getName() + class_name = '&'.join([cl['type_name'] for cl in classes]) + class_url = '&'.join([cl['type_id'] for cl in classes]) + # data = RuleClass.query.filter(RuleClass.name == '555影视').all() + # self.db.metadata.clear() + # RuleClass = rule_classes.init(self.db) + res = self.db.session.query(self.RuleClass).filter(self.RuleClass.name == name).first() + # print(res) + if res: + res.class_name = class_name + res.class_url = class_url + self.db.session.add(res) + msg = f'{self.getName()}修改成功:{res.id}' + else: + res = self.RuleClass(name=name, class_name=class_name, class_url=class_url) + self.db.session.add(res) + res = self.db.session.query(self.RuleClass).filter(self.RuleClass.name == name).first() + msg = f'{self.getName()}新增成功:{res.id}' + + try: + self.db.session.commit() + logger.info(msg) + except Exception as e: + return f'发生了错误:{e}' + + def getParse(self,play_url): + if not self.db: + msg = '未提供数据库连接' + print(msg) + return '' + name = self.getName() + # self.db.metadata.clear() + # RuleClass = rule_classes.init(self.db) + res = self.db.session.query(self.PlayParse).filter(self.PlayParse.play_url == play_url).first() + # _logger.info('xxxxxx') + if res: + real_url = res.real_url + logger.info(f"{name}使用缓存播放地址:{real_url}") + return real_url + else: + return '' + + def saveParse(self, play_url,real_url): + if not self.db: + msg = '未提供数据库连接' + print(msg) + return msg + name = self.getName() + # data = RuleClass.query.filter(RuleClass.name == '555影视').all() + # self.db.metadata.clear() + # RuleClass = rule_classes.init(self.db) + res = self.db.session.query(self.PlayParse).filter(self.PlayParse.play_url == play_url).first() + # print(res) + if res: + res.real_url = real_url + self.db.session.add(res) + msg = f'{name}服务端免嗅修改成功:{res.id}' + else: + res = self.PlayParse(play_url=play_url, real_url=real_url) + self.db.session.add(res) + res = self.db.session.query(self.PlayParse).filter(self.PlayParse.play_url == play_url).first() + msg = f'{name}服务端免嗅新增成功:{res.id}' + + try: + self.db.session.commit() + logger.info(msg) + except Exception as e: + return f'{name}发生了错误:{e}' + + + def homeContent(self,fypage=1): + # yanaifei + # https://yanetflix.com/vodtype/dianying.html + t1 = time() + result = {} + classes = [] + video_result = self.blank() + + if self.class_url and self.class_name: + class_names = self.class_name.split('&') + class_urls = self.class_url.split('&') + cnt = min(len(class_urls), len(class_names)) + for i in range(cnt): + classes.append({ + 'type_name': class_names[i], + 'type_id': class_urls[i] + }) + # print(self.url) + print(self.headers) + has_cache = False + if self.homeUrl.startswith('http'): + # print(self.homeUrl) + # print(self.class_parse) + try: + if self.class_parse: + t2 = time() + cache_classes = self.getClasses() + logger.info(f'{self.getName()}读取缓存耗时:{get_interval(t2)}毫秒') + if len(cache_classes) > 0: + classes = cache_classes + # print(cache_classes) + has_cache = True + # logger.info(f'是否有缓存分类:{has_cache}') + if has_cache and not self.推荐: + pass + else: + new_classes = [] + r = requests.get(self.homeUrl, headers=self.headers, timeout=self.timeout) + r.encoding = self.encoding + html = r.text + # print(html) + # print(self.headers) + if self.class_parse and not has_cache: + p = self.class_parse.split(';') + # print(p) + jsp = jsoup(self.url) + pdfh = jsp.pdfh + pdfa = jsp.pdfa + pd = jsp.pd + items = pdfa(html,p[0]) + # print(len(items)) + # print(items) + for item in items: + title = pdfh(item, p[1]) + # 过滤排除掉标题名称 + if self.cate_exclude and jsp.test(self.cate_exclude, title): + continue + url = pd(item, p[2]) + # print(url) + tag = url + if len(p) > 3 and p[3].strip(): + tag = self.regexp(p[3].strip(),url,0) + new_classes.append({ + 'type_name': title, + 'type_id': tag + }) + if len(new_classes) > 0: + classes.extend(new_classes) + self.saveClass(classes) + video_result = self.homeVideoContent(html,fypage) + except Exception as e: + logger.info(f'{self.getName()}主页发生错误:{e}') + + result['class'] = classes + if self.filter: + result['filters'] = playerConfig['filter'] + result.update(video_result) + # print(result) + logger.info(f'{self.getName()}获取首页总耗时(包含读取缓存):{get_interval(t1)}毫秒') + return result + + def homeVideoContent(self,html,fypage=1): + if not self.推荐: + return self.blank() + + p = self.推荐.split(';') # 解析 + if not self.double and len(p) < 5: + return self.blank() + if self.double and len(p) < 6: + return self.blank() + result = {} + videos = [] + jsp = jsoup(self.homeUrl) + is_json = str(p[0]).startswith('json:') + pdfh = jsp.pjfh if is_json else jsp.pdfh + pdfa = jsp.pjfa if is_json else jsp.pdfa + pd = jsp.pj if is_json else jsp.pd + # print(html) + try: + if self.double: + items = pdfa(html, p[0]) + for item in items: + items2 = pdfa(item,p[1]) + for item2 in items2: + try: + title = pdfh(item2, p[2]) + img = pd(item2, p[3]) + desc = pdfh(item2, p[4]) + links = [pd(item2, p5) if not self.detailUrl else pdfh(item2, p5) for p5 in p[5].split('+')] + link = '$'.join(links) + content = '' if len(p) < 7 else pdfh(item2, p[6]) + videos.append({ + "vod_id": link, + "vod_name": title, + "vod_pic": img, + "vod_remarks": desc, + "vod_content": content, + "type_id": 1, + "type_name": "首页推荐", + }) + except: + pass + else: + items = pdfa(html, p[0].replace('json:','')) + # print(items) + for item in items: + try: + title = pdfh(item, p[1]) + img = pd(item, p[2]) + desc = pdfh(item, p[3]) + # link = pd(item, p[4]) + links = [pd(item, p5) if not self.detailUrl else pdfh(item, p5) for p5 in p[4].split('+')] + link = '$'.join(links) + content = '' if len(p) < 6 else pdfh(item, p[5]) + videos.append({ + "vod_id": link, + "vod_name": title, + "vod_pic": img, + "vod_remarks": desc, + "vod_content": content, + "type_id": 1, + "type_name": "首页推荐", + }) + except: + pass + # result['list'] = videos[min((fypage-1)*self.limit,len(videos)-1):min(fypage*self.limit,len(videos))] + result['list'] = videos + result['code'] = 1 + result['msg'] = '数据列表' + result['page'] = fypage + result['pagecount'] = math.ceil(len(videos)/self.limit) + result['limit'] = self.limit + result['total'] = len(videos) + result['now_count'] = len(result['list']) + return result + except Exception as e: + logger.info(f'首页内容获取失败:{e}') + return self.blank() + + def categoryContent(self, fyclass, fypage): + """ + 一级带分类的数据返回 + :param fyclass: 分类标识 + :param fypage: 页码 + :return: cms一级数据 + """ + + result = {} + # urlParams = ["", "", "", "", "", "", "", "", "", "", "", ""] + # urlParams = [""] * 12 + # urlParams[0] = tid + # urlParams[8] = str(pg) + # for key in self.extend: + # urlParams[int(key)] = self.extend[key] + # params = '-'.join(urlParams) + # print(params) + # url = self.url + '/{0}.html'.format + t1 = time() + pg = str(fypage) + url = self.url.replace('fyclass',fyclass).replace('fypage',pg) + if fypage == 1 and self.test('[\[\]]',url): + url = url.split('[')[1].split(']')[0] + p = self.一级.split(';') # 解析 + if len(p) < 5: + return self.blank() + + jsp = jsoup(self.url) + is_json = str(p[0]).startswith('json:') + pdfh = jsp.pjfh if is_json else jsp.pdfh + pdfa = jsp.pjfa if is_json else jsp.pdfa + pd = jsp.pj if is_json else jsp.pd + # print(pdfh(r.text,'body a.module-poster-item.module-item:eq(1)&&Text')) + # print(pdfh(r.text,'body a.module-poster-item.module-item:eq(0)')) + # print(pdfh(r.text,'body a.module-poster-item.module-item:first')) + + videos = [] + items = [] + try: + r = requests.get(url, headers=self.headers, timeout=self.timeout) + r.encoding = self.encoding + print(r.url) + # html = r.text + html = r.json() if is_json else r.text + # print(html) + items = pdfa(html,p[0].replace('json:','',1)) + except: + pass + # print(items) + for item in items: + # print(item) + try: + title = pdfh(item, p[1]) + img = pd(item, p[2]) + desc = pdfh(item, p[3]) + links = [pd(item, p4) if not self.detailUrl else pdfh(item, p4) for p4 in p[4].split('+')] + link = '$'.join(links) + content = '' if len(p) < 6 else pdfh(item, p[5]) + # sid = self.regStr(sid, "/video/(\\S+).html") + videos.append({ + "vod_id": f'{fyclass}${link}' if self.detailUrl else link,# 分类,播放链接 + "vod_name": title, + "vod_pic": img, + "vod_remarks": desc, + "vod_content": content, + }) + except Exception as e: + print(f'发生了错误:{e}') + pass + result['list'] = videos + result['page'] = fypage + result['pagecount'] = 9999 + result['limit'] = 9999 + result['total'] = 999999 + logger.info(f'{self.getName()}获取分类{fyclass}第{fypage}页耗时:{get_interval(t1)}毫秒,共计{round(len(str(result)) / 1000, 2)} kb') + + return result + + def detailOneVod(self,id,fyclass=''): + detailUrl = str(id) + vod = {} + if not detailUrl.startswith('http'): + url = self.detailUrl.replace('fyid', detailUrl).replace('fyclass',fyclass) + else: + url = detailUrl + # print(url) + try: + p = self.二级 # 解析 + if p == '*': + vod = self.blank_vod() + vod['vod_play_from'] = '道长在线' + vod['vod_remarks'] = self.play_url+detailUrl + vod['vod_actor'] = '没有二级,只有一级链接直接嗅探播放' + vod['vod_content'] = detailUrl + vod['vod_play_url'] = '嗅探播放$'+detailUrl + return vod + + if not isinstance(p,dict) and not isinstance(p,str) and not str(p).startswith('js:'): + return vod + + jsp = jsoup(self.url) + + is_json = p.get('is_json',False) if isinstance(p,dict) else False # 二级里加is_json参数 + is_js = isinstance(p,str) and str(p).startswith('js:') # 是js + if is_js: + headers['Referer'] = getHome(url) + py_ctx.update({ + 'input': url, + 'fetch_params': {'headers': headers, 'timeout': self.d.timeout, 'encoding': self.d.encoding}, + 'd': self.d, + 'getParse': self.d.getParse, + 'saveParse': self.d.saveParse, + 'jsp':jsp,'setDetail':setDetail, + }) + ctx = py_ctx + # print(ctx) + jscode = getPreJs() + p.replace('js:','',1) + # print(jscode) + loader, _ = runJScode(jscode, ctx=ctx) + # print(loader.toString()) + vod = loader.eval('vod') + if isinstance(vod,JsObjectWrapper): + vod = vod.to_dict() + else: + vod = {} + # print(type(vod)) + # print(vod) + else: + pdfh = jsp.pjfh if is_json else jsp.pdfh + pdfa = jsp.pjfa if is_json else jsp.pdfa + pd = jsp.pj if is_json else jsp.pd + pq = jsp.pq + obj = {} + vod_name = '' + r = requests.get(url, headers=self.headers, timeout=self.timeout) + r.encoding = self.encoding + # html = r.text + html = r.json() if is_json else r.text + # print(html) + if p.get('title'): + p1 = p['title'].split(';') + vod_name = pdfh(html,p1[0]).replace('\n',' ') + # title = '\n'.join([pdfh(html,i).replace('\n',' ') for i in p1]) + title = '\n'.join([','.join([pdfh(html, pp1).strip() for pp1 in i.split('+')]) for i in p1]) + # print(title) + obj['title'] = title + if p.get('desc'): + p1 = p['desc'].split(';') + desc = '\n'.join([pdfh(html,i).replace('\n',' ') for i in p1]) + obj['desc'] = desc + + if p.get('content'): + p1 = p['content'].split(';') + content = '\n'.join([pdfh(html,i).replace('\n',' ') for i in p1]) + obj['content'] = content + + if p.get('img'): + p1 = p['img'].split(';') + img = '\n'.join([pdfh(html,i).replace('\n',' ') for i in p1]) + obj['img'] = img + + vod = { + "vod_id": detailUrl, + "vod_name": vod_name, + "vod_pic": obj.get('img',''), + "type_name": obj.get('title',''), + "vod_year": "", + "vod_area": "", + "vod_remarks": obj.get('desc',''), + "vod_actor": "", + "vod_director": "", + "vod_content": obj.get('content','') + } + + vod_play_from = '$$$' + playFrom = [] + if p.get('tabs'): + # print(p['tabs'].split(';')[0]) + vodHeader = pdfa(html,p['tabs'].split(';')[0]) + # print(f'线路列表数:{len((vodHeader))}') + # print(vodHeader) + if not is_json: + vodHeader = [pq(v).text() for v in vodHeader] + else: + vodHeader = ['道长在线'] + + # print(vodHeader) + + for v in vodHeader: + playFrom.append(v) + vod_play_from = vod_play_from.join(playFrom) + + vod_play_url = '$$$' + vod_tab_list = [] + if p.get('lists'): + for i in range(len(vodHeader)): + tab_name = str(vodHeader[i]) + tab_ext = p['tabs'].split(';')[1] if len(p['tabs'].split(';')) > 1 else '' + p1 = p['lists'].replace('#idv',tab_name).replace('#id',str(i)) + tab_ext = tab_ext.replace('#idv',tab_name).replace('#id',str(i)) + vodList = pdfa(html,p1) # 1条线路的选集列表 + # print(vodList) + # vodList = [pq(i).text()+'$'+pd(i,'a&&href') for i in vodList] # 拼接成 名称$链接 + if self.play_parse: # 自动base64编码 + vodList = [(pdfh(html,tab_ext) if tab_ext else tab_name)+'$'+self.play_url+base64Encode(i) for i in vodList] if is_json else\ + [pq(i).text()+'$'+self.play_url+base64Encode(pd(i,'a&&href')) for i in vodList] # 拼接成 名称$链接 + else: + vodList = [(pdfh(html, tab_ext) if tab_ext else tab_name) + '$' + self.play_url + i for i in + vodList] if is_json else \ + [pq(i).text() + '$' + self.play_url + pd(i, 'a&&href') for i in vodList] # 拼接成 名称$链接 + vlist = '#'.join(vodList) # 拼多个选集 + vod_tab_list.append(vlist) + vod_play_url = vod_play_url.join(vod_tab_list) + # print(vod_play_url) + vod['vod_play_from'] = vod_play_from + # print(vod_play_from) + vod['vod_play_url'] = vod_play_url + # print(vod_play_url) + except Exception as e: + logger.info(f'{self.getName()}获取单个详情页{detailUrl}出错{e}') + # print(vod) + return vod + + def detailContent(self, fypage, array): + """ + cms二级数据 + :param array: + :return: + """ + t1 = time() + array = array if len(array) <= self.limit else array[(fypage-1)*self.limit:min(self.limit*fypage,len(array))] + thread_pool = ThreadPoolExecutor(min(self.limit,len(array))) # 定义线程池来启动多线程执行此任务 + obj_list = [] + try: + for vod_url in array: + # print(vod_url) + vod_class = '' + if vod_url.find('$') > -1: + tmp = vod_url.split('$') + vod_class = tmp[0] + vod_url = tmp[1] + obj = thread_pool.submit(self.detailOneVod, vod_url,vod_class) + obj_list.append(obj) + thread_pool.shutdown(wait=True) # 等待所有子线程并行完毕 + vod_list = [obj.result() for obj in obj_list] + result = { + 'list': vod_list + } + logger.info(f'{self.getName()}获取详情页耗时:{get_interval(t1)}毫秒,共计{round(len(str(result)) / 1000, 2)} kb') + except Exception as e: + result = { + 'list': [] + } + logger.info(f'{self.getName()}获取详情页耗时:{get_interval(t1)}毫秒,发生错误:{e}') + # print(result) + return result + + def searchContent(self, key, fypage=1): + pg = str(fypage) + if not self.searchUrl: + return self.blank() + url = self.searchUrl.replace('**', key).replace('fypage',pg) + logger.info(f'{self.getName()}搜索链接:{url}') + if not self.搜索: + return self.blank() + p = self.一级.split(';') if self.搜索 == '*' and self.一级 else self.搜索.split(';') # 解析 + if len(p) < 5: + return self.blank() + jsp = jsoup(self.url) + is_json = str(p[0]).startswith('json:') + pdfh = jsp.pjfh if is_json else jsp.pdfh + pdfa = jsp.pjfa if is_json else jsp.pdfa + pd = jsp.pj if is_json else jsp.pd + pq = jsp.pq + videos = [] + try: + r = requests.get(url, headers=self.headers,timeout=self.timeout) + r.encoding = self.encoding + # html = r.text + html = r.json() if is_json else r.text + # print(html) + if not is_json and html.find('输入验证码') > -1: + cookie = verifyCode(url,self.headers,self.timeout,self.retry_count,self.ocr_api) + # cookie = '' + if not cookie: + return { + 'list': videos + } + self.saveCookie(cookie) + self.headers['cookie'] = cookie + r = requests.get(url, headers=self.headers, timeout=self.timeout) + r.encoding = self.encoding + html = r.text + + items = pdfa(html,p[0].replace('json:','',1)) + print(items) + videos = [] + for item in items: + # print(item) + try: + title = pdfh(item, p[1]) + img = pd(item, p[2]) + desc = pdfh(item, p[3]) + # link = '$'.join([pd(item, p4) for p4 in p[4].split('+')]) + links = [pd(item, p4) if not self.detailUrl else pdfh(item, p4) for p4 in p[4].split('+')] + link = '$'.join(links) + content = '' if len(p) < 6 else pdfh(item, p[5]) + # sid = self.regStr(sid, "/video/(\\S+).html") + videos.append({ + "vod_id": link, + "vod_name": title, + "vod_pic": img, + "vod_remarks": desc, + "vod_content": content, + }) + except: + pass + # print(videos) + except Exception as e: + logger.info(f'搜索{self.getName()}发生错误:{e}') + result = { + 'list': videos + } + return result + + def playContent(self, play_url,jxs=None,flag=None): + # logger.info('播放免嗅地址: ' + self.play_url) + if not jxs: + jxs = [] + try: + play_url = baseDecode(play_url) # 自动base64解码 + except: + pass + if self.lazy: + print(f'{play_url}->开始执行免嗅代码{type(self.lazy)}->{self.lazy}') + t1 = time() + try: + if type(self.lazy) == JsObjectWrapper: + logger.info(f'lazy非纯文本免嗅失败耗时:{get_interval(t1)}毫秒,播放地址:{play_url}') + + elif not str(self.lazy).startswith('js:'): + pycode = runPy(self.lazy) + if pycode: + # print(pycode) + pos = pycode.find('def lazyParse') + if pos < 0: + return play_url + pyenv = safePython(self.lazy,pycode[pos:]) + lazy_url = pyenv.action_task_exec('lazyParse',[play_url,self.d]) + logger.info(f'py免嗅耗时:{get_interval(t1)}毫秒,播放地址:{lazy_url}') + if isinstance(lazy_url,str) and lazy_url.startswith('http'): + play_url = lazy_url + else: + jscode = str(self.lazy).split('js:')[1] + # jscode = f'var input={play_url};{jscode}' + # print(jscode) + headers['Referer'] = getHome(play_url) + py_ctx.update({ + 'input': play_url, + 'fetch_params':{'headers':headers,'timeout':self.d.timeout,'encoding':self.d.encoding}, + 'd': self.d, + 'jxs':jxs, + 'getParse':self.d.getParse, + 'saveParse':self.d.saveParse, + 'pdfh': self.d.jsp.pdfh, + 'pdfa': self.d.jsp.pdfa, 'pd': self.d.jsp.pd, + }) + ctx = py_ctx + # print(ctx) + jscode = getPreJs() + jscode + # print(jscode) + loader,_ = runJScode(jscode,ctx=ctx) + # print(loader.toString()) + play_url = loader.eval('input') + if isinstance(play_url,JsObjectWrapper): + play_url = play_url.to_dict() + # print(type(play_url)) + # print(play_url) + logger.info(f'js免嗅耗时:{get_interval(t1)}毫秒,播放地址:{play_url}') + except Exception as e: + logger.info(f'免嗅耗时:{get_interval(t1)}毫秒,并发生错误:{e}') + return play_url + else: + logger.info(f'播放重定向到:{play_url}') + return play_url + +if __name__ == '__main__': + print(urljoin('https://api.web.360kan.com/v1/f', + '//0img.hitv.com/preview/sp_images/2022/01/28/202201281528074643023.jpg')) + # exit() + from utils import parser + # js_path = f'js/玩偶姐姐.js' + # js_path = f'js/555影视.js' + with open('../js/模板.js', encoding='utf-8') as f: + before = f.read() + js_path = f'js/360影视.js' + ctx, js_code = parser.runJs(js_path,before=before) + ruleDict = ctx.rule.to_dict() + # lazy = ctx.eval('lazy') + # print(lazy) + # ruleDict['id'] = rule # 把路由请求的id装到字典里,后面播放嗅探才能用 + + cms = CMS(ruleDict) + print(cms.title) + print(cms.homeContent()) + # print(cms.categoryContent('5',1)) + # print(cms.categoryContent('latest',1)) + # print(cms.detailContent(['https://www.2345ka.com/v/45499.html'])) + # print(cms.detailContent(1,['https://cokemv.me/voddetail/40573.html'])) + # cms.categoryContent('dianying',1) + # print(cms.detailContent(['67391'])) + # print(cms.searchContent('斗罗大陆')) + print(cms.searchContent('独行月球')) \ No newline at end of file diff --git a/controllers/home.py b/controllers/home.py new file mode 100644 index 0000000000000000000000000000000000000000..237c1fd5c7e8491b26ab69b19257b8d5c7e403e7 --- /dev/null +++ b/controllers/home.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : index.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 +import json +import os + +from flask import Blueprint,abort,render_template,url_for,redirect,make_response +from controllers.service import storage_service +from controllers.classes import getClasses,getClassInfo +from utils.web import getParmas +from utils.files import getPics +from js.rules import getRules +from base.R import R +from utils.system import cfg,getHost,is_linux +from utils import parser +from utils.log import logger +from utils.files import getAlist,get_live_url +from js.rules import getJxs +import random + +home = Blueprint("home", __name__,static_folder='/static') + +@home.route('/') +def forbidden(): # put application's code here + abort(403) + +@home.route('/favicon.ico') # 设置icon +def favicon(): + # return home.send_static_file('img/favicon.svg') + return redirect('/static/img/favicon.svg') + # 对于当前文件所在路径,比如这里是static下的favicon.ico + # return send_from_directory(os.path.join(app.root_path, 'static'), 'img/favicon.svg', mimetype='image/vnd.microsoft.icon') + +@home.route('/index') +def index(): + sup_port = cfg.get('SUP_PORT', False) + manager0 = ':'.join(getHost(0).split(':')[0:2]) + manager1 = ':'.join(getHost(1).split(':')[0:2]) + manager2 = ':'.join(getHost(2).split(':')[0:2]).replace('https','http') + if sup_port: + manager0 += f':{sup_port}' + manager1 += f':{sup_port}' + manager2 += f':{sup_port}' + return render_template('index.html',getHost=getHost,manager0=manager0,manager1=manager1,manager2=manager2,is_linux=is_linux()) + +@home.route('/rules/clear') +def rules_to_clear(): + return render_template('rules_to_clear.html',rules=getRules(),classes=getClasses()) + +@home.route('/rules/view') +def rules_to_view(): + return render_template('rules_to_view.html',rules=getRules(),classes=getClasses()) + +@home.route('/pics') +def random_pics(): + id = getParmas('id') + # print(f'id:{id}') + pics = getPics() + print(pics) + if len(pics) > 0: + if id and f'images/{id}.jpg' in pics: + pic = f'images/{id}.jpg' + else: + pic = random.choice(pics) + file = open(pic, "rb").read() + response = make_response(file) + response.headers['Content-Type'] = 'image/jpeg' + return response + else: + return redirect(cfg.WALL_PAPER) + +@home.route('/clear') +def clear_rule(): + rule = getParmas('rule') + if not rule: + return R.failed('规则字段必填') + cache_path = os.path.abspath(f'cache/{rule}.js') + if not os.path.exists(cache_path): + return R.failed('服务端没有此规则的缓存文件!'+cache_path) + os.remove(cache_path) + return R.success('成功删除文件:'+cache_path) + +@home.route("/plugin/",methods=['GET']) +def plugin(name): + # name=道长影视模板.js + if not name or not name.split('.')[-1] in ['js','txt','py','json']: + return R.failed(f'非法猥亵,未指定文件名。必须包含js|txt|json|py') + try: + return parser.toJs(name) + except Exception as e: + return R.failed(f'非法猥亵\n{e}') + +@home.route('/lives') +def get_lives(): + live_path = 'js/直播.txt' + if not os.path.exists(live_path): + with open(live_path,mode='w+',encoding='utf-8') as f: + f.write('') + + with open(live_path,encoding='utf-8') as f: + live_text = f.read() + response = make_response(live_text) + response.headers['Content-Type'] = 'text/plain; charset=utf-8' + return response + +@home.route('/liveslib') +def get_liveslib(): + live_path = 'js/custom_spider.jar' + if not os.path.exists(live_path): + with open(live_path,mode='w+',encoding='utf-8') as f: + f.write('') + + with open(live_path,mode='rb') as f: + live_text = f.read() + response = make_response(live_text) + filename = 'custom_spider.jar' + response.headers['Content-Type'] = 'application/octet-stream' + response.headers['Content-Disposition'] = f'attachment;filename="{filename}"' + return response + +@home.route('/config/') +def config_render(mode): + # print(dict(app.config)) + if mode == 1: + jyw_ip = getHost(mode) + logger.info(jyw_ip) + new_conf = cfg + host = getHost(mode) + jxs = getJxs() + alists = getAlist() + alists_str = json.dumps(alists, ensure_ascii=False) + live_url = get_live_url(new_conf,mode) + # html = render_template('config.txt',rules=getRules('js'),host=host,mode=mode,jxs=jxs,base64Encode=base64Encode,config=new_conf) + html = render_template('config.txt',rules=getRules('js'),host=host,mode=mode,jxs=jxs,alists=alists,alists_str=alists_str,live_url=live_url,config=new_conf) + response = make_response(html) + response.headers['Content-Type'] = 'application/json; charset=utf-8' + return response + +@home.route('/configs') +def config_gen(): + # 生成文件 + os.makedirs('txt',exist_ok=True) + new_conf = cfg + jxs = getJxs() + alists = getAlist() + alists_str = json.dumps(alists,ensure_ascii=False) + set_local = render_template('config.txt',rules=getRules('js'),alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,0),mode=0,host=getHost(0),jxs=jxs) + print(set_local) + set_area = render_template('config.txt',rules=getRules('js'),alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,1),mode=1,host=getHost(1),jxs=jxs) + set_online = render_template('config.txt',rules=getRules('js'),alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,2),mode=1,host=getHost(2),jxs=jxs) + with open('txt/pycms0.json','w+',encoding='utf-8') as f: + set_dict = json.loads(set_local) + f.write(json.dumps(set_dict,ensure_ascii=False,indent=4)) + with open('txt/pycms1.json','w+',encoding='utf-8') as f: + set_dict = json.loads(set_area) + f.write(json.dumps(set_dict,ensure_ascii=False,indent=4)) + + with open('txt/pycms2.json','w+',encoding='utf-8') as f: + set_dict = json.loads(set_online) + f.write(json.dumps(set_dict,ensure_ascii=False,indent=4)) + files = [os.path.abspath(rf'txt\pycms{i}.json') for i in range(3)] + # print(files) + return R.success('猫配置生成完毕,文件位置在:\n'+'\n'.join(files)) + +@home.route("/info",methods=['get']) +def info_all(): + data = storage_service.query_all() + return R.ok(data=data) \ No newline at end of file diff --git a/controllers/service.py b/controllers/service.py new file mode 100644 index 0000000000000000000000000000000000000000..0c74244928fdee725bbe8d39f89f766980fa82d6 --- /dev/null +++ b/controllers/service.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : service.py.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from base.R import copy_utils +from models.storage import Storage +from utils.system import cfg + +class storage_service(object): + + @staticmethod + def query_all(): + # 查询所有 + res = Storage.query.all() + return copy_utils.obj_to_list(res) + + def __init__(self): + if not self.getItem('LIVE_URL'): + print('开始初始化lsg') + self.setItem('LIVE_URL', cfg.get('LIVE_URL')) + + @classmethod + def getItem(self, key, value=''): + return Storage.getItem(key,value) + + @classmethod + def setItem(self,key, value): + return Storage.setItem(key, value) + + @classmethod + def clearItem(self,key): + return Storage.clearItem(key) \ No newline at end of file diff --git a/controllers/vod.py b/controllers/vod.py new file mode 100644 index 0000000000000000000000000000000000000000..464997ca9dda60504b03d65b82f9449bc9ffe432 --- /dev/null +++ b/controllers/vod.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : vod.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Date : 2022/9/6 + +from flask import Blueprint,request,render_template,jsonify,make_response,redirect +from time import time +from utils.web import getParmas,get_interval,cfg +from js.rules import getRuleLists,getJxs +from base.R import R +from utils.log import logger +from utils import parser +from controllers.cms import CMS +from base.database import db +from models.ruleclass import RuleClass +from models.playparse import PlayParse +vod = Blueprint("vod", __name__) + +@vod.route('/vod') +def vod_home(): + t0 = time() + rule = getParmas('rule') + ext = getParmas('ext') + if not ext.startswith('http') and not rule: + return R.failed('规则字段必填') + rule_list = getRuleLists() + if not ext.startswith('http') and not rule in rule_list: + msg = f'服务端本地仅支持以下规则:{",".join(rule_list)}' + return R.failed(msg) + # logger.info(f'检验耗时:{get_interval(t0)}毫秒') + t1 = time() + js_path = f'js/{rule}.js' if not ext.startswith('http') else ext + with open('js/模板.js', encoding='utf-8') as f: + before = f.read() + # logger.info(f'js读取耗时:{get_interval(t1)}毫秒') + logger.info(f'参数检验js读取共计耗时:{get_interval(t0)}毫秒') + t2 = time() + ctx, js_code = parser.runJs(js_path,before=before) + if not js_code: + return R.failed('爬虫规则加载失败') + + # rule = ctx.eval('rule') + # print(type(ctx.rule.lazy()),ctx.rule.lazy().toString()) + ruleDict = ctx.rule.to_dict() + ruleDict['id'] = rule # 把路由请求的id装到字典里,后面播放嗅探才能用 + # print(ruleDict) + # print(rule) + # print(type(rule)) + # print(ruleDict) + logger.info(f'js装载耗时:{get_interval(t2)}毫秒') + # print(ruleDict) + # print(rule) + cms = CMS(ruleDict,db,RuleClass,PlayParse,cfg) + wd = getParmas('wd') + ac = getParmas('ac') + quick = getParmas('quick') + play = getParmas('play') # 类型为4的时候点击播放会带上来 + flag = getParmas('flag') # 类型为4的时候点击播放会带上来 + filter = getParmas('filter') + t = getParmas('t') + pg = getParmas('pg','1') + pg = int(pg) + ids = getParmas('ids') + q = getParmas('q') + play_url = getParmas('play_url') + + if play: + jxs = getJxs() + play_url = play.split('play_url=')[1] + play_url = cms.playContent(play_url, jxs,flag) + if isinstance(play_url, str): + # return redirect(play_url) + # return jsonify({'parse': 0, 'playUrl': play_url, 'jx': 0, 'url': play_url}) + # return jsonify({'parse': 0, 'playUrl': play_url, 'jx': 0, 'url': ''}) + return jsonify({'parse': 0, 'playUrl': '', 'jx': 0, 'url': play_url}) + elif isinstance(play_url, dict): + return jsonify(play_url) + else: + return play_url + + if play_url: # 播放 + jxs = getJxs() + play_url = cms.playContent(play_url,jxs) + if isinstance(play_url,str): + return redirect(play_url) + elif isinstance(play_url,dict): + return jsonify(play_url) + else: + return play_url + + if ac and t: # 一级 + data = cms.categoryContent(t,pg) + # print(data) + return jsonify(data) + if ac and ids: # 二级 + id_list = ids.split(',') + # print('app:377',len(id_list)) + # print(id_list) + data = cms.detailContent(pg,id_list) + # print(data) + return jsonify(data) + if wd: # 搜索 + data = cms.searchContent(wd) + # print(data) + return jsonify(data) + + # return jsonify({'rule':rule,'js_code':js_code}) + home_data = cms.homeContent(pg) + return jsonify(home_data) \ No newline at end of file