增加后端代理,解决部分网页的播放问题

This commit is contained in:
晚风拂柳颜 2023-05-12 12:02:47 +08:00
parent 407d8aa791
commit cb03151232
7 changed files with 169 additions and 30 deletions

25
app.py
View File

@ -9,11 +9,12 @@ from flask_migrate import Migrate
from base import config from base import config
from base.database import db from base.database import db
from utils.log import logger from utils.log import logger
from utils.system import get_wlan_info,getHost from utils.system import get_wlan_info, getHost
from controllers import * from controllers import *
from js.rules import getRuleLists from js.rules import getRuleLists
import sys import sys
def create_flask_app(): def create_flask_app():
app = Flask(__name__, static_folder='static', static_url_path='/static') app = Flask(__name__, static_folder='static', static_url_path='/static')
app.config.from_object(config) # 单独的配置文件里写了这里就不用弄json中文显示了 app.config.from_object(config) # 单独的配置文件里写了这里就不用弄json中文显示了
@ -31,22 +32,28 @@ def create_flask_app():
# logger.info(f"自定义播放解析地址:{lsg.getItem('PLAY_URL')}") # logger.info(f"自定义播放解析地址:{lsg.getItem('PLAY_URL')}")
logger.info(f'当前操作系统{sys.platform}') logger.info(f'当前操作系统{sys.platform}')
rule_list = getRuleLists() rule_list = getRuleLists()
wlan_info,_ = get_wlan_info() wlan_info, _ = get_wlan_info()
logger.info(rule_list) logger.info(rule_list)
logger.info(f'局域网: {getHost(1, app.config.get("HTTP_PORT"))}/index\n本地: {getHost(0, app.config.get("HTTP_PORT"))}/index\nwlan_info:{wlan_info}') logger.info(
f'局域网: {getHost(1, app.config.get("HTTP_PORT"))}/index\n本地: {getHost(0, app.config.get("HTTP_PORT"))}/index\nwlan_info:{wlan_info}')
db.init_app(app) db.init_app(app)
db.app = app db.app = app
db.create_all(app=app) db.create_all(app=app)
return app return app
app = create_flask_app() app = create_flask_app()
migrate = Migrate(app, db) migrate = Migrate(app, db)
now_python_ver = ".".join([str(i) for i in sys.version_info[:3]]) now_python_ver = ".".join([str(i) for i in sys.version_info[:3]])
if sys.version_info < (3,9): if sys.version_info < (3, 9):
from gevent.pywsgi import WSGIServer from gevent.pywsgi import WSGIServer
# from gevent import monkey # from gevent import monkey
# monkey.patch_socket() # 开启socket异步 # monkey.patch_all() # 多线程,只能放在最开头,import其它包之前
from gevent import monkey
monkey.patch_socket() # 开启socket异步
print(f'当前python版本{now_python_ver}为3.9.0及以下,支持gevent') print(f'当前python版本{now_python_ver}为3.9.0及以下,支持gevent')
else: else:
print(f'当前python版本{now_python_ver}为3.9.0及以上,不支持gevent') print(f'当前python版本{now_python_ver}为3.9.0及以上,不支持gevent')
@ -54,10 +61,16 @@ else:
if __name__ == "__main__": if __name__ == "__main__":
http_port = int(app.config.get('HTTP_PORT', 5705)) http_port = int(app.config.get('HTTP_PORT', 5705))
http_host = app.config.get('HTTP_HOST', '0.0.0.0') http_host = app.config.get('HTTP_HOST', '0.0.0.0')
threaded = app.config.get('Thread')
if threaded is None:
threaded = True
# https://www.zhihu.com/question/64096559
print('threaded:',threaded)
# if sys.version_info < (3, 9) and not sys.platform.startswith('win'):
if sys.version_info < (3, 9): if sys.version_info < (3, 9):
# server = WSGIServer(('0.0.0.0', 5705), app, handler_class=WebSocketHandler,log=app.logger) # server = WSGIServer(('0.0.0.0', 5705), app, handler_class=WebSocketHandler,log=app.logger)
# server = WSGIServer(('0.0.0.0', 5705), app, handler_class=WebSocketHandler,log=None) # server = WSGIServer(('0.0.0.0', 5705), app, handler_class=WebSocketHandler,log=None)
server = WSGIServer((http_host, http_port), app, log=logger) server = WSGIServer((http_host, http_port), app, log=logger)
server.serve_forever() server.serve_forever()
else: else:
app.run(debug=False, host=http_host, port=http_port) app.run(debug=False, host=http_host, port=http_port, threaded=threaded)

View File

@ -56,3 +56,4 @@ JS_PROXY = 'http://localhost:5705/admin/view/=>https://ghproxy.net/https://raw.g
ALI_TOKEN = '' # 适用于初始配置的阿里云token ALI_TOKEN = '' # 适用于初始配置的阿里云token
ENV = '{"bili_cookie":""}' # 自定义环境变量 ENV = '{"bili_cookie":""}' # 自定义环境变量
UPDATE_PROXY = 'https://ghproxy.net/' # 检测升级代理 UPDATE_PROXY = 'https://ghproxy.net/' # 检测升级代理
Thread = True # 开启windows多线程调用

View File

@ -6,10 +6,11 @@
import functools import functools
import json import json
import os import os
from urllib.parse import urljoin
import requests
from flask import Blueprint, abort, request, render_template, send_from_directory, render_template_string, jsonify, \ from flask import Blueprint, abort, request, render_template, send_from_directory, render_template_string, jsonify, \
make_response, redirect, \ make_response, redirect, \
current_app current_app, url_for
from time import time from time import time
from utils.web import getParmas, get_interval from utils.web import getParmas, get_interval
from utils.cfg import cfg from utils.cfg import cfg
@ -38,12 +39,14 @@ def custom_static_cms(filename):
# print(filename) # print(filename)
return send_from_directory('templates/cms', filename) return send_from_directory('templates/cms', filename)
@web.route('/player/<path:filename>') @web.route('/player/<path:filename>')
def custom_static_player(filename): def custom_static_player(filename):
# 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}} # 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
# print(filename) # print(filename)
return send_from_directory('templates/player', filename) return send_from_directory('templates/player', filename)
@web.route('/<web_name>/<theme>') @web.route('/<web_name>/<theme>')
def web_index(web_name, theme): def web_index(web_name, theme):
ctx = {'web_name': web_name, 'key': '关键词', 'description': '描述'} ctx = {'web_name': web_name, 'key': '关键词', 'description': '描述'}
@ -67,7 +70,7 @@ def web_index(web_name, theme):
ctx['tid'] = tid ctx['tid'] = tid
ctx['tname'] = tname ctx['tname'] = tname
ctx['url'] = url ctx['url'] = url
print('tid:',tid) print('tid:', tid)
file_path = os.path.abspath(f'js/{web_name}.js') file_path = os.path.abspath(f'js/{web_name}.js')
print(file_path) print(file_path)
@ -86,4 +89,51 @@ def web_index(web_name, theme):
else: else:
return render_template(f'cms/{theme}/homeContent.html', ctx=ctx) return render_template(f'cms/{theme}/homeContent.html', ctx=ctx)
except Exception as e: except Exception as e:
return render_template('404.html', ctx=ctx, error=f'发生错误的原因可能是下面路径未找到:{e}') return render_template('404.html', ctx=ctx, error=f'发生错误的原因可能是下面路径未找到:{e}')
@web.route('/302redirect')
def get302UrlResponse():
url = getParmas('url')
if not url:
abort(403)
params = {}
if not url.startswith('http'):
url = urljoin(request.root_url, url)
# url = urljoin('http://localhost:5705/',url)
print(url)
items = url.split('vod?')[1].split('&')
for item in items:
params[item.split('=')[0]] = item.split('=')[1]
print(params)
# abort(403)
timeout = getParmas('timeout') or 5000
rurl = url
try:
timeout = int(timeout)
headers = {
# 'referer': url,
'user-agent': 'Mozilla/5.0'
}
print('开始调用接口:', url)
r = requests.get(url, headers=headers, timeout=timeout, verify=False)
rurl = r.url
# rurl = url_for('vod.vod_home', **params)
# print(rurl)
print('结束调用接口:', rurl)
return jsonify({
'url': rurl,
'redirect': rurl != url,
'data': r.text,
})
except Exception as e:
logger.info(f'发生了错误:{e}')
return jsonify({
'url': rurl,
'redirect': rurl != url,
'data': None,
'error': f'{e}',
})

View File

@ -1 +1 @@
3.9.41beta26 3.9.41beta27

View File

@ -277,7 +277,7 @@
{% for rule in rules.list %} {% for rule in rules.list %}
<div class="red"> <div class="red">
<div class="mz"><a class="view" href="javascript:void(0);">{{ rule.name }}</a></div> <div class="mz"><a class="view" href="javascript:void(0);">{{ rule.name }}</a></div>
<div class="sa"><a class="preview_home" href="/web/{{ rule.name }}/mxpro" value="{{ rule.name }}">🌐</a></div> <div class="sa"><a class="preview_home" href="/web/{{ rule.name }}/mxpro" value="{{ rule.name }}" target="_blank">🌐</a></div>
<div class="sj"><a class="view_home" href="javascript:void(0);" value="{{ rule.name }}">🏠</a></div> <div class="sj"><a class="view_home" href="javascript:void(0);" value="{{ rule.name }}">🏠</a></div>
<div class="ss"><a class="view_search" href="javascript:void(0);" value="{{ rule.name }}">🔍️</a></div> <div class="ss"><a class="view_search" href="javascript:void(0);" value="{{ rule.name }}">🔍️</a></div>
<div class="sc"><a class="clear" href="javascript:void(0);" value="{{ rule.name }}">🗑</a></div> <div class="sc"><a class="clear" href="javascript:void(0);" value="{{ rule.name }}">🗑</a></div>

View File

@ -22,6 +22,7 @@
<script src="/static/js/axios.min.js"></script> <script src="/static/js/axios.min.js"></script>
<script src="/static/js/eruda.js"></script> <script src="/static/js/eruda.js"></script>
<script type="text/javascript" src="/web/cms/mxpro/js/commonUtil.js"></script>
<script src="/web/cms/mxpro/js/commonUI.js"></script> <script src="/web/cms/mxpro/js/commonUI.js"></script>
<link rel="stylesheet" href="/web/cms/mxpro/css/commonUI.css" type="text/css" /> <link rel="stylesheet" href="/web/cms/mxpro/css/commonUI.css" type="text/css" />
<script>var maccms={"path":"","mid":"1","url":"5imv.cc","wapurl":"www.5imv.cc","mob_status":"2"};</script> <script>var maccms={"path":"","mid":"1","url":"5imv.cc","wapurl":"www.5imv.cc","mob_status":"2"};</script>
@ -243,39 +244,72 @@ const app = createApp({
const photoVisible = ref(false); const photoVisible = ref(false);
const iframeRef = ref(null); const iframeRef = ref(null);
var sniffer; var sniffer;
const {isVideo,getRealUrl} = commonUtil;
// get302UrlResponse('/vod?pwd=dzyyds&rule=007影视&ext=&play_url=https%3A//www.007ts.me/play/69636-3-1.html',(resp)=>{
// console.log('重定向到:',resp);
// });
// getRealUrl('/vod?pwd=dzyyds&rule=007影视&ext=&play_url=https%3A//www.007ts.me/play/69636-3-1.html',(res)=>{
// console.log('重定向到:',res);
// });
const methods = { const methods = {
async lazyPlay(url){ async lazyPlay(url){
iframeShow.value = true; iframeShow.value = true;
console.log('准备处理播放地址:'+url); console.log('准备处理播放地址:'+url);
clearInterval(sniffer); clearInterval(sniffer);
try { try {
if(/\.(m3u8|mp4)/.test(url)){ if(isVideo(url)){
console.log('直接播放'); console.log('直接播放');
methods.setPlayUrl(url); methods.setPlayUrl(url);
return return
} }
const res = await axios.get(url,{maxRedirects: 0}); const res =await getRealUrl(url,(resp)=>{
const { status, headers: { Location } } = res; // console.log('代理数据:',resp.data);
// console.log(status); return resp.data
console.log(res.request.responseURL); });
if (status === 302) { // console.log(res);
console.log(Location); let res_data = res.data;
try {
res_data = JSON.parse(res_data);
}catch (e) {
} }
console.log(res.data); // console.log(res_data);
if(typeof(res.data)==='string' && /#EXTM3U/.test(res.data)){ // console.log(res.url);
// console.log(res.redirect);
if(res.redirect){ // 重定向的直接设置播放器的值
if(isVideo(res.url)){ // 判断重定向的为直链
methods.setPlayUrl(res.url);
}else{
console.log('重定向待嗅探页面,但是由于跨域问题,只好内嵌播放页播放');
iframeSrc.value = res.url;
}
return
// throw new Error('重定向网页直接播放,尝试嗅探(存在跨域无法嗅探问题,暂时考虑直接内嵌人家的播放页)');
}
// const res = await axios.get(url,{maxRedirects: 0});
// const { status, headers: { Location } } = res;
// // console.log(status);
// console.log(res.request.responseURL);
// if (status === 302) {
// console.log(Location);
// }
// console.log(res.data);
console.log(res_data);
if(typeof(res_data)==='string'){
iframeRef.value.contentWindow.location.reload(); iframeRef.value.contentWindow.location.reload();
iframeSrc.value = url; iframeSrc.value = url;
throw new Error('重定向类文件无法直接播放,尝试嗅探'); throw new Error('返回的数据是文本无法直接播放,尝试嗅探');
} else if(!res.data.parse&&res.data.url){ } else if(!res_data.parse&&res_data.url){
methods.setPlayUrl(res.data.url); methods.setPlayUrl(res_data.url);
}else if(/url=/.test(res.data.url)){ }else if(/url=/.test(res_data.url)){
iframeSrc.value = res.data.url; iframeSrc.value = res_data.url;
} else if((res.data.parse||res.data.jx)&&res.data.url){ } else if((res_data.parse||res_data.jx)&&res_data.url){
console.log(ctx.value); console.log(ctx.value);
iframeSrc.value = res.data.url; iframeSrc.value = res_data.url;
if(confirm('该视频来自其它正版地址,drpy网页暂未实现解析功能,是否跳到正版站通过油猴插件等手段播放?')){ if(confirm('该视频来自其它正版地址,drpy网页暂未实现解析功能,是否跳到正版站通过油猴插件等手段播放?')){
open(res.data.url); open(res_data.url);
} }
} }
}catch (e) { }catch (e) {
@ -365,6 +399,7 @@ const app = createApp({
photoVisible:photoVisible, photoVisible:photoVisible,
iframeRef:iframeRef, iframeRef:iframeRef,
sniffer:sniffer, sniffer:sniffer,
isVideo,getRealUrl
} }
}, },
}); });
@ -392,6 +427,7 @@ eruda.init();
<!--<script charset="UTF-8" id="LA_COLLECT" src="/web/cms/mxpro/js/js-sdk-pro.min.js"></script>--> <!--<script charset="UTF-8" id="LA_COLLECT" src="/web/cms/mxpro/js/js-sdk-pro.min.js"></script>-->
<!--<script>LA.init({id:"JYQUFCtAOBTUMsNQ",ck:"JYQUFCtAOBTUMsNQ"})</script>--> <!--<script>LA.init({id:"JYQUFCtAOBTUMsNQ",ck:"JYQUFCtAOBTUMsNQ"})</script>-->
<script type="text/javascript" src="/web/cms/mxpro/js/stui_default.js "></script> <script type="text/javascript" src="/web/cms/mxpro/js/stui_default.js "></script>
<!--弹窗样式和自动弹窗方法--> <!--弹窗样式和自动弹窗方法-->
<link rel="stylesheet" href="/web/cms/mxpro/css/notice.css" type="text/css"> <link rel="stylesheet" href="/web/cms/mxpro/css/notice.css" type="text/css">
<script type="text/javascript" src="/web/cms/mxpro/js/mxhtml.js"></script> <script type="text/javascript" src="/web/cms/mxpro/js/mxhtml.js"></script>

View File

@ -0,0 +1,39 @@
var commonUtil = {
isVideo(playUrl){
let res_url = playUrl.split('?')[0];
if(playUrl.endsWith('.m3u8')||res_url.endsWith('.m3u8')){
return true
}else if(playUrl.endsWith('.mp4')||res_url.endsWith('.mp4')){
return true
}else if(/\.(m4a|mp3|flv|aac)$/.test(playUrl)||/\.(m4a|mp3|flv|aac)$/.test(res_url)){
return true
}
return false
},
getLocationFromRedirect(
originUrl,
method = "GET"
){
return new Promise<string>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, originUrl, true);
xhr.onload = function () {
resolve(xhr.responseURL);
};
xhr.onerror = reject;
xhr.send(null);
})
},
get302UrlResponse(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function () {
callback(xhr.responseURL);
}
xhr.send(null);
},
async getRealUrl(url,callback){
const res = await axios.get(`web/302redirect?url=${encodeURIComponent(url)}`);
return callback(res);
}
};