home.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # File : index.py
  4. # Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
  5. # Date : 2022/9/6
  6. import json
  7. import ujson
  8. import os
  9. import re
  10. from flask import Blueprint,abort,render_template,render_template_string,url_for,redirect,make_response,send_from_directory,request
  11. from controllers.service import storage_service,rules_service,parse_service
  12. from controllers.classes import getClasses,getClassInfo
  13. from utils.files import getPics,custom_merge,getAlist,get_live_url,get_multi_rules,getCustonDict
  14. from js.rules import getRules,getPys
  15. from utils.encode import parseText,base64Encode,base64Decode
  16. from base.R import R
  17. from utils.system import getHost,is_linux
  18. from utils.cfg import cfg
  19. from utils import parser
  20. from utils.ua import time,get_interval
  21. from utils.log import logger
  22. from utils.update import getLocalVer,getHotSuggest
  23. from js.rules import getJxs
  24. import random
  25. from utils.web import getParmas,verfy_token
  26. from utils.common_api import js_render
  27. import functools
  28. home = Blueprint("home", __name__,static_folder='/static')
  29. @home.route('/')
  30. def forbidden(): # put application's code here
  31. abort(403)
  32. @home.route('/favicon.ico') # 设置icon
  33. def favicon():
  34. # return home.send_static_file('img/favicon.svg')
  35. return redirect('/static/img/favicon.svg')
  36. # 对于当前文件所在路径,比如这里是static下的favicon.ico
  37. # return send_from_directory(os.path.join(app.root_path, 'static'), 'img/favicon.svg', mimetype='image/vnd.microsoft.icon')
  38. @home.route('/index')
  39. def index():
  40. sup_port = cfg.get('SUP_PORT', 9001)
  41. lsg = storage_service()
  42. pid_url = lsg.getItem('PID_URL')
  43. manager0 = ':'.join(getHost(0).split(':')[0:2])
  44. manager1 = ':'.join(getHost(1).split(':')[0:2])
  45. manager2 = pid_url or ':'.join(getHost(2).split(':')[0:2]).replace('https','http')
  46. if sup_port:
  47. manager0 += f':{sup_port}'
  48. manager1 += f':{sup_port}'
  49. if not pid_url:
  50. manager2 += f':{sup_port}'
  51. # print(manager2)
  52. ver = getLocalVer()
  53. return render_template('index.html',ver=ver,getHost=getHost,manager0=manager0,manager1=manager1,manager2=manager2,is_linux=is_linux())
  54. @home.route('/rules/clear')
  55. def rules_to_clear():
  56. return render_template('rules_to_clear.html',rules=getRules(),classes=getClasses())
  57. @home.route('/rules/view')
  58. def rules_to_view():
  59. return render_template('rules_to_view.html',rules=getRules(),classes=getClasses())
  60. @home.route('/pics')
  61. def random_pics():
  62. id = getParmas('id')
  63. # print(f'id:{id}')
  64. pics = getPics()
  65. # print(pics)
  66. new_conf = cfg
  67. lsg = storage_service()
  68. store_conf_dict = lsg.getStoreConfDict()
  69. new_conf.update(store_conf_dict)
  70. if not new_conf.WALL_PAPER and len(pics) > 0:
  71. if id and f'images/{id}.jpg' in pics:
  72. pic = f'images/{id}.jpg'
  73. else:
  74. pic = random.choice(pics)
  75. file = open(pic, "rb").read()
  76. response = make_response(file)
  77. response.headers['Content-Type'] = 'image/jpeg'
  78. return response
  79. else:
  80. return redirect(new_conf.WALL_PAPER)
  81. @home.route('/clear')
  82. def clear_rule():
  83. rule = getParmas('rule')
  84. if not rule:
  85. return R.failed('规则字段必填')
  86. cache_path = os.path.abspath(f'cache/{rule}.js')
  87. if not os.path.exists(cache_path):
  88. return R.failed('服务端没有此规则的缓存文件!'+cache_path)
  89. os.remove(cache_path)
  90. return R.success('成功删除文件:'+cache_path)
  91. @home.route("/plugin/<name>",methods=['GET'])
  92. def plugin(name):
  93. # name=道长影视模板.js
  94. if not name or not name.split('.')[-1] in ['js','txt','py','json']:
  95. return R.failed(f'非法猥亵,未指定文件名。必须包含js|txt|json|py')
  96. try:
  97. return parser.toJs(name)
  98. except Exception as e:
  99. return R.failed(f'非法猥亵\n{e}')
  100. @home.route('/files/<name>')
  101. def get_files(name):
  102. base_path = 'base/files'
  103. os.makedirs(base_path,exist_ok=True)
  104. file_path = os.path.join(base_path, f'{name}')
  105. if not os.path.exists(file_path):
  106. return R.failed(f'{file_path}文件不存在')
  107. with open(file_path, mode='rb') as f:
  108. file_byte = f.read()
  109. response = make_response(file_byte)
  110. filename = name
  111. response.headers['Content-Type'] = 'application/octet-stream'
  112. response.headers['Content-Disposition'] = f'attachment;filename="{filename}"'
  113. return response
  114. @home.route('/txt/<path:filename>')
  115. def custom_static_txt(filename):
  116. # 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
  117. # print(filename)
  118. return send_from_directory('txt', filename)
  119. @home.route('/libs/<path:filename>')
  120. def custom_static_libs(filename):
  121. # 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
  122. # print(filename)
  123. return send_from_directory('libs', filename)
  124. # @home.route('/js/<path:filename>')
  125. # def custom_static_js(filename):
  126. # # 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
  127. # # print(filename)
  128. # return send_from_directory('js', filename)
  129. @home.route('/js/<path:name>',methods=['GET'])
  130. def custom_static_js(name):
  131. # 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
  132. # print(name)
  133. return js_render(name)
  134. # @home.route('/txt/<name>')
  135. # def get_txt_files(name):
  136. # base_path = 'txt'
  137. # os.makedirs(base_path,exist_ok=True)
  138. # file_path = os.path.join(base_path, f'{name}')
  139. # if not os.path.exists(file_path):
  140. # return R.failed(f'{file_path}文件不存在')
  141. #
  142. # with open(file_path, mode='r',encoding='utf-8') as f:
  143. # file_byte = f.read()
  144. # response = make_response(file_byte)
  145. # response.headers['Content-Type'] = 'text/plain; charset=utf-8'
  146. # return response
  147. @home.route('/lives')
  148. def get_lives():
  149. # ?path=base/live.txt
  150. path = getParmas('path')
  151. live_path = path or 'base/直播.txt'
  152. if not re.search('(txt|json|conf)$',live_path,re.M|re.S) or not re.search('^(txt|base)',live_path,re.M|re.S):
  153. abort(403)
  154. if not os.path.exists(live_path):
  155. # with open(live_path,mode='w+',encoding='utf-8') as f:
  156. # f.write('')
  157. return ''
  158. with open(live_path,encoding='utf-8') as f:
  159. live_text = f.read()
  160. if len(live_text) > 100 and live_text.find('http') < 0:
  161. try:
  162. live_text = base64Decode(live_text)
  163. logger.info(f'{path} base64解码完毕')
  164. except Exception as e:
  165. logger.info(f'{path} base64解码失败:{e}')
  166. response = make_response(live_text)
  167. response.headers['Content-Type'] = 'text/plain; charset=utf-8'
  168. return response
  169. @home.route('/liveslib')
  170. def get_liveslib():
  171. live_path = 'js/custom_spider.jar'
  172. if not os.path.exists(live_path):
  173. with open(live_path,mode='w+',encoding='utf-8') as f:
  174. f.write('')
  175. with open(live_path,mode='rb') as f:
  176. live_text = f.read()
  177. response = make_response(live_text)
  178. filename = 'custom_spider.jar'
  179. response.headers['Content-Type'] = 'application/octet-stream'
  180. response.headers['Content-Disposition'] = f'attachment;filename="{filename}"'
  181. return response
  182. @home.route('/hotsugg')
  183. def get_hot_search():
  184. s_from = getParmas('from')
  185. size = getParmas('size')
  186. data = getHotSuggest(s_from,size)
  187. return R.success('获取成功',data)
  188. def merged_hide(merged_config):
  189. t1 = time()
  190. store_rule = rules_service()
  191. hide_rules = store_rule.getHideRules()
  192. hide_rule_names = list(map(lambda x: x['name'], hide_rules))
  193. # print(hide_rule_names)
  194. all_cnt = len(merged_config['sites'])
  195. def filter_show(x):
  196. name = x['api'].split('rule=')[1].split('&')[0] if 'rule=' in x['api'] else x['key'].replace('dr_','')
  197. # print(name)
  198. if not str(x['key']).startswith('dr_') and name == 'drpy':
  199. name = x['key']
  200. return name not in hide_rule_names
  201. merged_config['sites'] = list(filter(filter_show, merged_config['sites']))
  202. logger.info(f'数据库筛选隐藏规则耗时{get_interval(t1)}毫秒,共计{all_cnt}条规则,隐藏后可渲染{len(merged_config["sites"])}条规则')
  203. @home.route('/config/<int:mode>')
  204. def config_render(mode):
  205. # print(dict(app.config))
  206. tt = time()
  207. UA = request.headers['User-Agent']
  208. ver = getParmas('ver')
  209. logger.info(f'ver:{ver},UA:{UA}')
  210. if ver not in ['1','2']:
  211. ISTVB = 'okhttp/3' in UA
  212. elif ver == '1':
  213. ISTVB = False
  214. elif ver == '2':
  215. ISTVB = True
  216. # print(ISTVB)
  217. if mode == 1:
  218. jyw_ip = getHost(mode)
  219. logger.info(jyw_ip)
  220. new_conf = cfg
  221. lsg = storage_service()
  222. store_conf_dict = lsg.getStoreConfDict()
  223. new_conf.update(store_conf_dict)
  224. # print(new_conf)
  225. # print(type(new_conf),new_conf)
  226. host = getHost(mode)
  227. # ali_token = lsg.getItem('ALI_TOKEN')
  228. ali_token = new_conf.ALI_TOKEN
  229. xr_mode = new_conf.XR_MODE
  230. js_proxy = new_conf.JS_PROXY
  231. js0_password = new_conf.JS0_PASSWORD
  232. js_mode = int(new_conf.JS_MODE or 0)
  233. print(f'{type(js_mode)} jsmode:{js_mode}')
  234. # print(ali_token)
  235. customConfig = getCustonDict(host,ali_token,js0_password)
  236. # print(customConfig)
  237. jxs = getJxs(host=host)
  238. use_py = lsg.getItem('USE_PY')
  239. pys = getPys() if use_py else []
  240. # print(pys)
  241. alists = getAlist()
  242. alists_str = json.dumps(alists, ensure_ascii=False)
  243. live_url = get_live_url(new_conf,mode)
  244. rules = getRules('js',js_mode)
  245. rules = get_multi_rules(rules)
  246. # html = render_template('config.txt',rules=getRules('js'),host=host,mode=mode,jxs=jxs,base64Encode=base64Encode,config=new_conf)
  247. html = render_template('config.txt',js0_password=js0_password,UA=UA,xr_mode=xr_mode,ISTVB=ISTVB,pys=pys,rules=rules,host=host,mode=mode,js_mode=js_mode,jxs=jxs,alists=alists,alists_str=alists_str,live_url=live_url,config=new_conf)
  248. merged_config = custom_merge(parseText(html),customConfig)
  249. # print(merged_config['sites'])
  250. merged_hide(merged_config)
  251. # response = make_response(html)
  252. # print(len(merged_config['sites']))
  253. print(merged_config['sites'])
  254. merged_config['sites'] = sort_sites_by_order(merged_config['sites'],js_mode)
  255. # print(merged_config['parses'])
  256. parses = sort_parses_by_order(merged_config['parses'],host)
  257. # print(parses)
  258. merged_config['parses'] = parses
  259. config_text = json.dumps(merged_config,ensure_ascii=False,indent=1)
  260. # 依赖代理逻辑修改,改为admin/view去动态代理
  261. # if js_proxy:
  262. # # print('js_proxy:',js_proxy)
  263. # if '=>' in js_proxy:
  264. # oldsrc = js_proxy.split('=>')[0]
  265. # newsrc = js_proxy.split('=>')[1]
  266. # print(f'js1源代理已启用,全局替换{oldsrc}为{newsrc}')
  267. # config_text = config_text.replace(oldsrc,newsrc)
  268. response = make_response(config_text)
  269. # response = make_response(str(merged_config))
  270. response.headers['Content-Type'] = 'application/json; charset=utf-8'
  271. logger.info(f'自动生成动态配置共计耗时:{get_interval(tt)}毫秒')
  272. return response
  273. def comp(x, y):
  274. if x['order'] > y['order']:
  275. return 1
  276. elif x['order'] < y['order']:
  277. return - 1
  278. else:
  279. if x['write_date'] < y['write_date']:
  280. return 1
  281. elif x['write_date'] > y['write_date']:
  282. return -1
  283. else:
  284. return 0
  285. def sort_sites_by_order(sites,js_mode=0):
  286. rules = rules_service()
  287. rule_list = rules.query_all()
  288. # print(rule_list)
  289. rule_names = list(map(lambda x: x['name'], rule_list))
  290. # print(rule_names)
  291. # print(sites)
  292. for i in range(len(sites)):
  293. # sites[i]['id'] = i+1
  294. site_name = sites[i]['api'].split('rule=')[1].split('&')[0] if 'rule=' in sites[i]['api'] else sites[i]['key']
  295. if js_mode and str(site_name).startswith('dr'):
  296. site_name = site_name.replace('dr_','')
  297. if not str(sites[i]['key']).startswith('dr_') and site_name == 'drpy':
  298. site_name = sites[i]['key']
  299. # print(sites[i])
  300. # print(site_name)
  301. if site_name in rule_names:
  302. site_rule = rule_list[rule_names.index(site_name)]
  303. sites[i]['state'] = 1 if site_rule['state'] is None else site_rule['state']
  304. sites[i]['order'] = 0 if site_rule['order'] is None else site_rule['order']
  305. sites[i]['write_date'] = 0 if site_rule['write_date'] is None else site_rule['write_date'].timestamp()
  306. else:
  307. sites[i]['state'] = 1
  308. sites[i]['order'] = 0
  309. sites[i]['write_date'] = 0
  310. # sites[i]['site_name'] = site_name
  311. # print(sites)
  312. # sites.sort(key=lambda x: x['order'], reverse=False)
  313. sites.sort(key=functools.cmp_to_key(comp), reverse=False)
  314. # print(sites)
  315. for site in sites:
  316. del site['state']
  317. del site['order']
  318. del site['write_date']
  319. return sites
  320. def sort_parses_by_order(parses,host):
  321. t1 = time()
  322. parse = parse_service()
  323. parse_list = parse.query_all()
  324. parse_url_list = list(map(lambda x: x['url'], parse_list))
  325. new_parses = []
  326. new_parses_url = []
  327. for i in range(len(parses)):
  328. # parses[i]['id'] = i + 1
  329. # 去重
  330. if parses[i]['url'] in new_parses_url:
  331. # print(f"重复的解析:{parses[i]['name']},{parses[i]['url']}")
  332. continue
  333. if str(parses[i]['url']).startswith(host):
  334. parses[i]['url'] = parses[i]['url'].replace(host,'')
  335. if parses[i]['url'] in parse_url_list:
  336. parse_rule = parse_list[parse_url_list.index(parses[i]['url'])]
  337. parses[i]['state'] = 1 if parse_rule['state'] is None else parse_rule['state']
  338. parses[i]['order'] = 0 if parse_rule['order'] is None else parse_rule['order']
  339. parses[i]['write_date'] = 0 if parse_rule['write_date'] is None else parse_rule['write_date'].timestamp()
  340. else:
  341. parses[i]['state'] = 1
  342. parses[i]['order'] = 0
  343. parses[i]['write_date'] = 0
  344. if not parses[i].get('header'):
  345. parses[i]['header'] = {'User-Agent': 'Mozilla/5.0'}
  346. if str(parses[i]['url']).startswith('/'):
  347. parses[i]['url'] = host + parses[i]['url']
  348. new_parses.append(parses[i])
  349. new_parses_url.append(parses[i]['url'])
  350. new_parses.sort(key=functools.cmp_to_key(comp), reverse=False)
  351. # print(sites)
  352. for par in new_parses:
  353. del par['state']
  354. del par['order']
  355. del par['write_date']
  356. # print(new_parses)
  357. logger.info(f'{len(new_parses)}/{len(parses)}条解析解析排序耗时:{get_interval(t1)}毫秒')
  358. return new_parses
  359. @home.route('/configs')
  360. def config_gen():
  361. if not verfy_token():
  362. return R.failed('请登录后再试')
  363. # 生成文件
  364. os.makedirs('txt',exist_ok=True)
  365. new_conf = cfg
  366. lsg = storage_service()
  367. store_conf_dict = lsg.getStoreConfDict()
  368. new_conf.update(store_conf_dict)
  369. try:
  370. use_py = lsg.getItem('USE_PY')
  371. js_mode = int(new_conf.JS_MODE or 0)
  372. js0_password = new_conf.JS0_PASSWORD
  373. pys = getPys() if use_py else False
  374. alists = getAlist()
  375. alists_str = json.dumps(alists,ensure_ascii=False)
  376. rules = getRules('js',js_mode)
  377. rules = get_multi_rules(rules)
  378. host0 = getHost(0)
  379. jxs = getJxs(host=host0)
  380. set_local = render_template('config.txt',js0_password=js0_password,pys=pys,rules=rules,alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,0),mode=0,js_mode=js_mode,host=host0,jxs=jxs)
  381. # print(set_local)
  382. host1 = getHost(1)
  383. jxs = getJxs(host=host1)
  384. set_area = render_template('config.txt',js0_password=js0_password,pys=pys,rules=rules,alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,1),mode=1,js_mode=js_mode,host=host1,jxs=jxs)
  385. host2 = getHost(2) or host1
  386. # print('远程地址:'+host2)
  387. jxs = getJxs(host=host2)
  388. set_online = render_template('config.txt',js0_password=js0_password,pys=pys,rules=rules,alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,2),mode=1,js_mode=js_mode,host=host2,jxs=jxs)
  389. ali_token = new_conf.ALI_TOKEN
  390. # parses = []
  391. with open('txt/pycms0.json','w+',encoding='utf-8') as f:
  392. customConfig = getCustonDict(host0,ali_token,js0_password)
  393. set_dict = custom_merge(parseText(set_local), customConfig)
  394. merged_hide(set_dict)
  395. set_dict['sites'] = sort_sites_by_order(set_dict['sites'], js_mode)
  396. # if not parses:
  397. # print('生成静态配置时初始化排序parses')
  398. # parses = sort_parses_by_order(set_dict['parses'])
  399. # set_dict['parses'] = parses
  400. set_dict['parses'] = sort_parses_by_order(set_dict['parses'],host0)
  401. # set_dict = json.loads(set_local)
  402. f.write(json.dumps(set_dict,ensure_ascii=False,indent=4))
  403. with open('txt/pycms1.json','w+',encoding='utf-8') as f:
  404. customConfig = getCustonDict(host1,ali_token,js0_password)
  405. set_dict = custom_merge(parseText(set_area), customConfig)
  406. merged_hide(set_dict)
  407. set_dict['sites'] = sort_sites_by_order(set_dict['sites'], js_mode)
  408. set_dict['parses'] = sort_parses_by_order(set_dict['parses'],host1)
  409. # set_dict = json.loads(set_area)
  410. f.write(json.dumps(set_dict,ensure_ascii=False,indent=4))
  411. with open('txt/pycms2.json','w+',encoding='utf-8') as f:
  412. customConfig = getCustonDict(host2,ali_token,js0_password)
  413. set_dict = custom_merge(parseText(set_online), customConfig)
  414. merged_hide(set_dict)
  415. set_dict['sites'] = sort_sites_by_order(set_dict['sites'], js_mode)
  416. set_dict['parses'] = sort_parses_by_order(set_dict['parses'],host2)
  417. # set_dict = json.loads(set_online)
  418. f.write(json.dumps(set_dict,ensure_ascii=False,indent=4))
  419. files = [os.path.abspath(rf'txt\pycms{i}.json') for i in range(3)]
  420. # print(files)
  421. return R.success('猫配置生成完毕,文件位置在:\n'+'\n'.join(files))
  422. except Exception as e:
  423. return R.failed(f'配置文件生成错误:\n{e}')
  424. @home.route("/info",methods=['get'])
  425. def info_all():
  426. data = storage_service.query_all()
  427. return R.ok(data=data)