# Copyright (C) 2023 MikuInvidious Team # # MikuInvidious is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # MikuInvidious is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with MikuInvidious. If not, see . import requests from urllib.parse import urlparse import asyncio from flask import request, make_response, redirect, url_for, send_from_directory import subprocess from bs4 import BeautifulSoup from shared import * from views import * from res import * from filters import * from extra import video_get_src_for_qn, video_get_dash_for_qn, bv2av, av2bv import traceback from bilibili_api import exceptions ########################################## # APIs ########################################## @app.route('/toggle_theme') def toggle_theme_api(): resp = make_response() if dark_theme := request.cookies.get('dark-theme'): dark_theme = not int(dark_theme) else: dark_theme = False resp.set_cookie('dark-theme', '1' if dark_theme else '0') return resp ########################################## # Additional features ########################################## @app.route('/') async def b32tv_redirect(b32tvid): req = requests.get(f'https://b23.tv/{b32tvid}', allow_redirects=False) if req.status_code != 302: e = req.json() return await render_template_with_theme('error.html', status = e['message'], desc = e['message'], suggest='请检查您的请求并重试。'), -e['code'] url = urlparse(req.headers['Location']) if url.path.startswith('/read/mobile'): return redirect(url_for('read_view', cid = f'cv{url.path[13:]}')) elif url.path.startswith('/video/'): return redirect(url_for('video_view', vid = req.headers['Location'].split('/')[-1][:12])) @app.route('/download', methods=['POST']) def dl_redirect(): bvid = request.form.get('id') cvid = request.form.get('cvid') qual = request.form.get('qual') return redirect(f'/proxy/video/{bvid}_{cvid}_{qual}', code=302) ########################################## # Misc ########################################## @app.route('/preferences') async def pref_view(): return await render_template_with_theme('pref.html') @app.route('/test') async def test_view(): theme = detect_theme() resp = make_response(theme) resp.set_cookie('theme', 'default') return resp @app.route('/robots.txt') def robots_txt(): policy = appconf['site'].get('robots_policy') or 'strict' if policy == 'PLEASE_INDEX_EVERYTHING': return '', 404 return send_from_directory(f'static/rules', f'robots_{policy}.txt') ########################################## # Error handling ########################################## @app.errorhandler(exceptions.ArgsException) async def args_exception_view(e): return await render_template_with_theme('error.html', status = '请求错误', desc = e), 400 @app.errorhandler(exceptions.ResponseCodeException) async def resp_exception_view(e): suggest = None if e.code == -404: suggest = '这很可能说明您访问的视频/文章不存在,请检查您的请求。' \ '如果您认为这是站点的问题,请联系网站管理员。' return await render_template_with_theme('error.html', status = e.msg, desc = (e.raw if appconf['site']['site_show_unsafe_error_response'] else f'后端服务器发送了无效的回复。'), suggest=suggest), -e.code @app.errorhandler(Exception) async def general_exception_view(e): error_msg = f'{type(e).__name__}: {e}' print(error_msg) return await render_template_with_theme('error.html', status='服务器错误', desc=error_msg), 500