feat: 便携版EXE

This commit is contained in:
helloplhm-qwq 2023-11-11 18:16:03 +08:00
parent 6409caf638
commit 49cb760d4d
No known key found for this signature in database
GPG Key ID: 6BE1B64B905567C7
11 changed files with 96 additions and 29 deletions

5
.gitignore vendored
View File

@ -10,6 +10,11 @@ __pycache__/
*.egg-info *.egg-info
*.egg *.egg
# pyinstaller
*.spec
dist
build
# project # project
cache.db cache.db
data.db data.db

39
apis/.github/workflows/build_binary.yml vendored Normal file
View File

@ -0,0 +1,39 @@
# this workflow using github actions to build a binary exe file for windows users
name: build Windows exe
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-exe:
runs-on: windows-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: setup python 3.11
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: install pyinstaller
run: python -m pip install pyinstaller
- name: install dependencies
run: python -m pip install -r ./requirements.txt
- name: build exe
run: pyinstaller -F main.py
- name: rename
run: Rename-Item ./dist/main.exe ./dist/lx-music-api-server_$(git rev-parse --short HEAD).exe
- name: upload
uses: actions/upload-artifact@v2
with:
name: lx-music-api-server
path: ./dist

View File

@ -8,8 +8,8 @@
# This file is part of the "lx-music-api-server" project. # This file is part of the "lx-music-api-server" project.
# Do not edit except you konw what you are doing. # Do not edit except you konw what you are doing.
from common.utils import require
from common.exceptions import FailedException from common.exceptions import FailedException
from common.utils import require
from common import log from common import log
from common import config from common import config
from . import kw from . import kw
@ -59,8 +59,15 @@ async def SongURL(source, songId, quality):
'data': c['url'], 'data': c['url'],
} }
except: except:
traceback.print_exc() logger.error(traceback.format_exc())
try:
func = require('apis.' + source).url func = require('apis.' + source).url
except:
return {
'code': 1,
'msg': '未知的源: ' + source,
'data': None,
}
try: try:
url = await func(songId, quality) url = await func(songId, quality)
logger.debug(f'获取{source}_{songId}_{quality}成功URL{url}') logger.debug(f'获取{source}_{songId}_{quality}成功URL{url}')

View File

@ -14,32 +14,34 @@ import os
import traceback import traceback
import sys import sys
import sqlite3 import sqlite3
from .utils import unique_list, readfile from .utils import readfile
from . import variable from . import variable
from .log import log from .log import log
# from dbutils.pooled_db import PooledDB
import threading import threading
logger = log('config_manager')
# 创建线程本地存储对象 # 创建线程本地存储对象
local_data = threading.local() local_data = threading.local()
def get_data_connection(): def get_data_connection():
# 检查线程本地存储对象是否存在连接对象,如果不存在则创建一个新的连接对象 # 检查线程本地存储对象是否存在连接对象,如果不存在则创建一个新的连接对象
if not hasattr(local_data, 'connection'): if not hasattr(local_data, 'connection'):
local_data.connection = sqlite3.connect('data.db') local_data.connection = sqlite3.connect('data.db')
return local_data.connection return local_data.connection
# 创建线程本地存储对象 # 创建线程本地存储对象
local_cache = threading.local() local_cache = threading.local()
def get_cache_connection(): def get_cache_connection():
# 检查线程本地存储对象是否存在连接对象,如果不存在则创建一个新的连接对象 # 检查线程本地存储对象是否存在连接对象,如果不存在则创建一个新的连接对象
if not hasattr(local_cache, 'connection'): if not hasattr(local_cache, 'connection'):
local_cache.connection = sqlite3.connect('cache.db') local_cache.connection = sqlite3.connect('cache.db')
return local_cache.connection return local_cache.connection
logger = log('config_manager')
class ConfigReadException(Exception): class ConfigReadException(Exception):
pass pass
@ -395,8 +397,11 @@ def initConfig():
cursor = conn.cursor() cursor = conn.cursor()
# 创建一个表来存储缓存数据 # 创建一个表来存储缓存数据
cursor.execute(readfile(variable.workdir + cursor.execute('''CREATE TABLE IF NOT EXISTS cache
'/common/sql/create_cache_db.sql')) (id INTEGER PRIMARY KEY AUTOINCREMENT,
module TEXT NOT NULL,
key TEXT NOT NULL,
data TEXT NOT NULL)''')
conn.close() conn.close()
@ -405,8 +410,9 @@ def initConfig():
# 创建一个游标对象 # 创建一个游标对象
cursor2 = conn2.cursor() cursor2 = conn2.cursor()
cursor2.execute(readfile(variable.workdir + cursor2.execute('''CREATE TABLE IF NOT EXISTS data
'/common/sql/create_data_db.sql')) (key TEXT PRIMARY KEY,
value TEXT)''')
conn2.close() conn2.close()
@ -416,7 +422,7 @@ def initConfig():
if (load_data() == {}): if (load_data() == {}):
write_data('banList', []) write_data('banList', [])
write_data('requestTime', {}) write_data('requestTime', {})
logger.debug('数据库内容为空,已写入默认值') logger.info('数据库内容为空,已写入默认值')
def ban_ip(ip_addr, ban_time=-1): def ban_ip(ip_addr, ban_time=-1):

View File

@ -11,6 +11,10 @@
import logging import logging
import colorlog import colorlog
import os import os
import traceback
from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import TerminalFormatter
from .utils import sanitize_filename from .utils import sanitize_filename
from .variable import debug_mode, log_length_limit from .variable import debug_mode, log_length_limit
@ -19,6 +23,13 @@ try:
except: except:
pass pass
def highlight_error(error):
# 对堆栈跟踪进行语法高亮
highlighted_traceback = highlight(error, PythonLexer(), TerminalFormatter())
# 返回语法高亮后的堆栈跟踪字符串
return str(highlighted_traceback)
class flaskLogHelper(logging.Handler): class flaskLogHelper(logging.Handler):
# werkzeug日志转接器 # werkzeug日志转接器
def __init__(self, custom_logger): def __init__(self, custom_logger):
@ -45,7 +56,7 @@ class log:
datefmt='%Y-%m-%d %H:%M:%S', datefmt='%Y-%m-%d %H:%M:%S',
log_colors={ log_colors={
'DEBUG': 'cyan', 'DEBUG': 'cyan',
'INFO': 'green', 'INFO': 'white',
'WARNING': 'yellow', 'WARNING': 'yellow',
'ERROR': 'red', 'ERROR': 'red',
'CRITICAL': 'red,bg_white', 'CRITICAL': 'red,bg_white',
@ -120,6 +131,9 @@ class log:
self._logger.warning(message) self._logger.warning(message)
def error(self, message): def error(self, message):
if (message.startswith('Traceback')):
self._logger.error('\n' + highlight_error(message))
else:
self._logger.error(message) self._logger.error(message)
def critical(self, message): def critical(self, message):

View File

@ -37,7 +37,3 @@ def checklxmheader(lxm, url):
return True return True
except: except:
return False return False
exports = {
}

View File

@ -1,5 +0,0 @@
CREATE TABLE IF NOT EXISTS cache
(id INTEGER PRIMARY KEY AUTOINCREMENT,
module TEXT NOT NULL,
key TEXT NOT NULL,
data TEXT NOT NULL)

View File

@ -1,3 +0,0 @@
CREATE TABLE IF NOT EXISTS data
(key TEXT PRIMARY KEY,
value TEXT)

View File

@ -10,6 +10,7 @@
import platform import platform
import binascii import binascii
import builtins
import base64 import base64
import zlib import zlib
import re import re
@ -51,6 +52,9 @@ def require(module):
index += 1 index += 1
return _module return _module
def add_to_global_namespace(key, data):
setattr(builtins, key, data)
def sanitize_filename(filename): def sanitize_filename(filename):
if platform.system() == 'Windows' or platform.system() == 'Cygwin': if platform.system() == 'Windows' or platform.system() == 'Cygwin':
# Windows不合法文件名字符 # Windows不合法文件名字符
@ -129,3 +133,4 @@ class jsobject(dict):
def __getattr__(self, UNUSED): def __getattr__(self, UNUSED):
return None return None
add_to_global_namespace('require', require)

View File

@ -29,8 +29,6 @@ from common import utils
from common import lxsecurity from common import lxsecurity
from apis import SongURL from apis import SongURL
require = utils.require
@app.route('/') @app.route('/')
def index(): def index():
return utils.format_dict_json({"code": 0, "msg": "success", "data": None}), 200 return utils.format_dict_json({"code": 0, "msg": "success", "data": None}), 200
@ -64,16 +62,20 @@ def _404(_):
@app.before_request @app.before_request
def check(): def check():
# nginx proxy header
if (request.headers.get("X-Real-IP")): if (request.headers.get("X-Real-IP")):
request.remote_addr = request.headers.get("X-Real-IP") request.remote_addr = request.headers.get("X-Real-IP")
# check ip
if (config.check_ip_banned(request.remote_addr)): if (config.check_ip_banned(request.remote_addr)):
return utils.format_dict_json({"code": 1, "msg": "您的IP已被封禁", "data": None}), 403 return utils.format_dict_json({"code": 1, "msg": "您的IP已被封禁", "data": None}), 403
# update request time
config.updateRequestTime(request.remote_addr) config.updateRequestTime(request.remote_addr)
# check host
if (config.read_config("security.allowed_host.enable")): if (config.read_config("security.allowed_host.enable")):
if request.remote_host.split(":")[0] not in config.read_config("security.allowed_host.list"): if request.remote_host.split(":")[0] not in config.read_config("security.allowed_host.list"):
if config.read_config("security.allowed_host.blacklist.enable"): if config.read_config("security.allowed_host.blacklist.enable"):
config.ban_ip(request.remote_addr, int(config.read_config("security.allowed_host.blacklist.length"))) config.ban_ip(request.remote_addr, int(config.read_config("security.allowed_host.blacklist.length")))
return utils.format_dict_json({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}), 404 return utils.format_dict_json({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}), 404
# run
app.run(host=config.read_config('common.host'), port=config.read_config('common.port')) app.run(host=config.read_config('common.host'), port=config.read_config('common.port'))

View File

@ -3,3 +3,4 @@ pycryptodome
ujson ujson
requests requests
colorlog colorlog
pygments