mirror of
https://github.com/MeoProject/lx-music-api-server.git
synced 2025-05-23 19:17:41 +08:00
feat: 支持客户端播放服务端音乐
This commit is contained in:
parent
a8e4d8ac69
commit
339e5edf3d
@ -108,6 +108,11 @@ default = {
|
|||||||
"mg": ["128k"],
|
"mg": ["128k"],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"local_music": {
|
||||||
|
"desc": "服务器侧本地音乐相关配置,请确保你的带宽足够",
|
||||||
|
"audio_path": "./audio",
|
||||||
|
"temp_path": "./temp",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"rate_limit": {
|
"rate_limit": {
|
||||||
@ -153,7 +158,7 @@ default = {
|
|||||||
"enable": True,
|
"enable": True,
|
||||||
"length": 86400 * 7, # 七天
|
"length": 86400 * 7, # 七天
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"module": {
|
"module": {
|
||||||
"kg": {
|
"kg": {
|
||||||
|
362
common/localMusic.py
Normal file
362
common/localMusic.py
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
# ----------------------------------------
|
||||||
|
# - mode: python -
|
||||||
|
# - author: helloplhm-qwq -
|
||||||
|
# - name: localMusic.py -
|
||||||
|
# - project: lx-music-api-server -
|
||||||
|
# - license: MIT -
|
||||||
|
# ----------------------------------------
|
||||||
|
# This file is part of the "lx-music-api-server" project.
|
||||||
|
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from PIL import Image
|
||||||
|
import aiohttp
|
||||||
|
from common.utils import createMD5, timeLengthFormat
|
||||||
|
from . import log, config
|
||||||
|
from pydub.utils import mediainfo
|
||||||
|
import ujson as json
|
||||||
|
import traceback
|
||||||
|
import mutagen
|
||||||
|
import os
|
||||||
|
|
||||||
|
logger = log.log('local_music_handler')
|
||||||
|
|
||||||
|
audios = []
|
||||||
|
map = {}
|
||||||
|
AUDIO_PATH = config.read_config("common.local_music.audio_path")
|
||||||
|
TEMP_PATH = config.read_config("common.local_music.temp_path")
|
||||||
|
FFMPEG_PATH = None
|
||||||
|
|
||||||
|
def convertCover(input_bytes):
|
||||||
|
if (input_bytes.startswith(b'\xff\xd8\xff\xe0')): # jpg object do not need convert
|
||||||
|
return input_bytes
|
||||||
|
temp = TEMP_PATH + '/' + createMD5(input_bytes) + '.img'
|
||||||
|
with open(temp, 'wb') as f:
|
||||||
|
f.write(input_bytes)
|
||||||
|
f.close()
|
||||||
|
img = Image.open(temp)
|
||||||
|
img = img.convert('RGB')
|
||||||
|
with open(temp + 'crt', 'wb') as f:
|
||||||
|
img.save(f, format='JPEG')
|
||||||
|
f.close()
|
||||||
|
data = None
|
||||||
|
with open(temp + 'crt', 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
try:
|
||||||
|
os.remove(temp)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
os.remove(temp + 'crt')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return data
|
||||||
|
|
||||||
|
def check_ffmpeg():
|
||||||
|
logger.info('正在检查ffmpeg')
|
||||||
|
devnull = open(os.devnull, 'w')
|
||||||
|
linux_bin_path = '/usr/bin/ffmpeg'
|
||||||
|
environ_ffpmeg_path = os.environ.get('FFMPEG_PATH')
|
||||||
|
if (platform.system() == 'Windows' or platform.system() == 'Cygwin'):
|
||||||
|
if (environ_ffpmeg_path and (not environ_ffpmeg_path.endswith('.exe'))):
|
||||||
|
environ_ffpmeg_path += '/ffmpeg.exe'
|
||||||
|
else:
|
||||||
|
if (environ_ffpmeg_path and os.path.isdir(environ_ffpmeg_path)):
|
||||||
|
environ_ffpmeg_path += '/ffmpeg'
|
||||||
|
|
||||||
|
if (environ_ffpmeg_path):
|
||||||
|
try:
|
||||||
|
subprocess.Popen([environ_ffpmeg_path, '-version'], stdout=devnull, stderr=devnull)
|
||||||
|
devnull.close()
|
||||||
|
return environ_ffpmeg_path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if (os.path.isfile(linux_bin_path)):
|
||||||
|
try:
|
||||||
|
subprocess.Popen([linux_bin_path, '-version'], stdout=devnull, stderr=devnull)
|
||||||
|
devnull.close()
|
||||||
|
return linux_bin_path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.Popen(['ffmpeg', '-version'], stdout=devnull, stderr=devnull)
|
||||||
|
return 'ffmpeg'
|
||||||
|
except:
|
||||||
|
logger.warning('无法找到ffmpeg,对于本地音乐的一些扩展功能无法使用,如果您不需要,请忽略本条提示')
|
||||||
|
logger.warning('如果您已经安装,请将 FFMPEG_PATH 环境变量设置为您的ffmpeg安装路径或者将其添加到PATH中')
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getAudioCoverFromFFMpeg(path):
|
||||||
|
if (not FFMPEG_PATH):
|
||||||
|
return None
|
||||||
|
cmd = [FFMPEG_PATH, '-i', path, TEMP_PATH + '/_tmp.jpg']
|
||||||
|
popen = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stdout)
|
||||||
|
popen.wait()
|
||||||
|
if (os.path.exists(TEMP_PATH + '/_tmp.jpg')):
|
||||||
|
with open(TEMP_PATH + '/_tmp.jpg', 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
try:
|
||||||
|
os.remove(TEMP_PATH + '/_tmp.jpg')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return data
|
||||||
|
|
||||||
|
def readFileCheckCover(path):
|
||||||
|
with open(path, 'rb') as f: # read the first 1MB audio
|
||||||
|
data = f.read(1024 * 1024)
|
||||||
|
return b'image/' in data
|
||||||
|
|
||||||
|
def checkLyricValid(lyric_content):
|
||||||
|
if (lyric_content is None):
|
||||||
|
return False
|
||||||
|
if (lyric_content == ''):
|
||||||
|
return False
|
||||||
|
lines = lyric_content.split('\n')
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
if (line == ''):
|
||||||
|
continue
|
||||||
|
if (line.startswith('[')):
|
||||||
|
continue
|
||||||
|
if (not line.startswith('[')):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def filterLyricLine(lyric_content: str) -> str:
|
||||||
|
lines = lyric_content.split('\n')
|
||||||
|
completed = []
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
if (line.startswith('[')):
|
||||||
|
completed.append(line)
|
||||||
|
continue
|
||||||
|
return '\n'.join(completed)
|
||||||
|
|
||||||
|
def getAudioMeta(filepath):
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
audio = mutagen.File(filepath)
|
||||||
|
if not audio:
|
||||||
|
return None
|
||||||
|
logger.info(audio.items())
|
||||||
|
if (filepath.lower().endswith('.mp3')):
|
||||||
|
cover = audio.get('APIC:')
|
||||||
|
if (cover):
|
||||||
|
cover = convertCover(cover.data)
|
||||||
|
|
||||||
|
title = audio.get('TIT2')
|
||||||
|
artist = audio.get('TPE1')
|
||||||
|
album = audio.get('TALB')
|
||||||
|
lyric = audio.get('TLRC')
|
||||||
|
if (title):
|
||||||
|
title = title.text
|
||||||
|
if (artist):
|
||||||
|
artist = artist.text
|
||||||
|
if (album):
|
||||||
|
album = album.text
|
||||||
|
if (lyric):
|
||||||
|
lyric = lyric.text
|
||||||
|
else:
|
||||||
|
lyric = [None]
|
||||||
|
else:
|
||||||
|
cover = audio.get('cover')
|
||||||
|
if (cover):
|
||||||
|
cover = convertCover(cover[0])
|
||||||
|
else:
|
||||||
|
if (readFileCheckCover(filepath)):
|
||||||
|
cover = getAudioCoverFromFFMpeg(filepath)
|
||||||
|
else:
|
||||||
|
cover = None
|
||||||
|
title = audio.get('title')
|
||||||
|
artist = audio.get('artist')
|
||||||
|
album = audio.get('album')
|
||||||
|
lyric = audio.get('lyrics')
|
||||||
|
if (not lyric):
|
||||||
|
if (os.path.isfile(os.path.splitext(filepath)[0] + '.lrc')):
|
||||||
|
with open(os.path.splitext(filepath)[0] + '.lrc', 'r', encoding='utf-8') as f:
|
||||||
|
lyric = filterLyricLine(f.read())
|
||||||
|
if (not checkLyricValid(lyric)):
|
||||||
|
lyric = [None]
|
||||||
|
f.close()
|
||||||
|
else:
|
||||||
|
lyric = [None]
|
||||||
|
return {
|
||||||
|
"filepath": filepath,
|
||||||
|
"title": title[0] if title else '',
|
||||||
|
"artist": '、'.join(artist) if artist else '',
|
||||||
|
"album": album[0] if album else '',
|
||||||
|
"cover_path": extractCover({
|
||||||
|
"filepath": filepath,
|
||||||
|
"cover": cover,
|
||||||
|
}, TEMP_PATH),
|
||||||
|
"lyrics": lyric[0],
|
||||||
|
'length': audio.info.length,
|
||||||
|
'format_length': timeLengthFormat(audio.info.length),
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
logger.error(f"get audio meta error: {filepath}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def checkAudioValid(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
audio = mutagen.File(path)
|
||||||
|
if not audio:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
logger.error(f"check audio valid error: {path}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
|
def extractCover(audio_info, temp_path):
|
||||||
|
if (not audio_info['cover']):
|
||||||
|
return None
|
||||||
|
path = os.path.join(temp_path + '/' + createMD5(audio_info['filepath']) + '_cover.jpg')
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(audio_info['cover'])
|
||||||
|
return path
|
||||||
|
|
||||||
|
def findAudios():
|
||||||
|
|
||||||
|
available_exts = [
|
||||||
|
'mp3',
|
||||||
|
'wav',
|
||||||
|
'flac',
|
||||||
|
'ogg',
|
||||||
|
'm4a',
|
||||||
|
]
|
||||||
|
|
||||||
|
files = os.listdir(AUDIO_PATH)
|
||||||
|
if (files == []):
|
||||||
|
return []
|
||||||
|
|
||||||
|
audios = []
|
||||||
|
for file in files:
|
||||||
|
if (not file.endswith(tuple(available_exts))):
|
||||||
|
continue
|
||||||
|
path = os.path.join(AUDIO_PATH, file)
|
||||||
|
if (not checkAudioValid(path)):
|
||||||
|
continue
|
||||||
|
logger.info(f"found audio: {path}")
|
||||||
|
meta = getAudioMeta(path)
|
||||||
|
audios = audios + [meta]
|
||||||
|
|
||||||
|
return audios
|
||||||
|
|
||||||
|
def getAudioCover(filepath):
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
audio = mutagen.File(filepath)
|
||||||
|
if not audio:
|
||||||
|
return None
|
||||||
|
return convertCover(audio.get('APIC:').data)
|
||||||
|
except:
|
||||||
|
logger.error(f"get audio cover error: {filepath}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def writeAudioCover(filepath):
|
||||||
|
s = getAudioCover(filepath)
|
||||||
|
path = os.path.join(TEMP_PATH + '/' + createMD5(filepath) + '_cover.jpg')
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(s)
|
||||||
|
f.close()
|
||||||
|
return path
|
||||||
|
|
||||||
|
def writeLocalCache(audios):
|
||||||
|
with open(TEMP_PATH + '/meta.json', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps({
|
||||||
|
"file_list": os.listdir(AUDIO_PATH),
|
||||||
|
"audios": audios
|
||||||
|
}, ensure_ascii = False, indent = 2))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def dumpLocalCache():
|
||||||
|
try:
|
||||||
|
TEMP_PATH = config.read_config("common.local_music.temp_path")
|
||||||
|
with open(TEMP_PATH + '/meta.json', 'r', encoding='utf-8') as f:
|
||||||
|
d = json.loads(f.read())
|
||||||
|
return d
|
||||||
|
except:
|
||||||
|
return {
|
||||||
|
"file_list": [],
|
||||||
|
"audios": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def initMain():
|
||||||
|
global FFMPEG_PATH
|
||||||
|
FFMPEG_PATH = check_ffmpeg()
|
||||||
|
logger.debug('找到的ffmpeg命令: ' + str(FFMPEG_PATH))
|
||||||
|
if (not os.path.exists(AUDIO_PATH)):
|
||||||
|
os.mkdir(AUDIO_PATH)
|
||||||
|
logger.info(f"创建本地音乐文件夹 {AUDIO_PATH}")
|
||||||
|
if (not os.path.exists(TEMP_PATH)):
|
||||||
|
os.mkdir(TEMP_PATH)
|
||||||
|
logger.info(f"创建本地音乐临时文件夹 {TEMP_PATH}")
|
||||||
|
global audios
|
||||||
|
cache = dumpLocalCache()
|
||||||
|
if (cache['file_list'] == os.listdir(AUDIO_PATH)):
|
||||||
|
audios = cache['audios']
|
||||||
|
else:
|
||||||
|
audios = findAudios()
|
||||||
|
writeLocalCache(audios)
|
||||||
|
for a in audios:
|
||||||
|
map[a['filepath']] = a
|
||||||
|
logger.info("初始化本地音乐成功")
|
||||||
|
logger.debug(f'本地音乐列表: {audios}')
|
||||||
|
logger.debug(f'本地音乐map: {map}')
|
||||||
|
|
||||||
|
async def generateAudioFileResonse(path):
|
||||||
|
try:
|
||||||
|
w = map[path]
|
||||||
|
return aiohttp.web.FileResponse(w['filepath'])
|
||||||
|
except:
|
||||||
|
return {
|
||||||
|
'code': 2,
|
||||||
|
'msg': '未找到文件',
|
||||||
|
'data': None
|
||||||
|
}, 404
|
||||||
|
|
||||||
|
async def generateAudioCoverResonse(path):
|
||||||
|
try:
|
||||||
|
w = map[path]
|
||||||
|
if (not os.path.exists(w['cover_path'])):
|
||||||
|
p = writeAudioCover(w['filepath'])
|
||||||
|
logger.debug(f"生成音乐封面文件 {w['cover_path']} 成功")
|
||||||
|
return aiohttp.web.FileResponse(p)
|
||||||
|
return aiohttp.web.FileResponse(w['cover_path'])
|
||||||
|
except:
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
return {
|
||||||
|
'code': 2,
|
||||||
|
'msg': '未找到封面',
|
||||||
|
'data': None
|
||||||
|
}, 404
|
||||||
|
|
||||||
|
async def generateAudioLyricResponse(path):
|
||||||
|
try:
|
||||||
|
w = map[path]
|
||||||
|
return w['lyrics']
|
||||||
|
except:
|
||||||
|
return {
|
||||||
|
'code': 2,
|
||||||
|
'msg': '未找到歌词',
|
||||||
|
'data': None
|
||||||
|
}, 404
|
||||||
|
|
||||||
|
def checkLocalMusic(path):
|
||||||
|
return {
|
||||||
|
'file': os.path.exists(path),
|
||||||
|
'cover': os.path.exists(map[path]['cover_path']),
|
||||||
|
'lyric': bool(map[path]['lyrics'])
|
||||||
|
}
|
@ -64,8 +64,10 @@ def filterFileName(filename):
|
|||||||
# 将不合法字符替换为下划线
|
# 将不合法字符替换为下划线
|
||||||
return re.sub(illegal_chars, '_', filename)
|
return re.sub(illegal_chars, '_', filename)
|
||||||
|
|
||||||
def createMD5(s: str):
|
def createMD5(s: (str, bytes)):
|
||||||
return handleCreateMD5(s.encode("utf-8")).hexdigest()
|
if (isinstance(s, str)):
|
||||||
|
s = s.encode("utf-8")
|
||||||
|
return handleCreateMD5(s).hexdigest()
|
||||||
|
|
||||||
def readFile(path, mode = "text"):
|
def readFile(path, mode = "text"):
|
||||||
try:
|
try:
|
||||||
|
58
main.py
58
main.py
@ -11,18 +11,20 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from common.utils import createBase64Decode
|
||||||
|
|
||||||
if ((sys.version_info.major == 3 and sys.version_info.minor < 6) or sys.version_info.major == 2):
|
if ((sys.version_info.major == 3 and sys.version_info.minor < 6) or sys.version_info.major == 2):
|
||||||
print('Python版本过低,请使用Python 3.6+ ')
|
print('Python版本过低,请使用Python 3.6+ ')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
from common import config
|
from common import config, localMusic
|
||||||
from common import lxsecurity
|
from common import lxsecurity
|
||||||
from common import log
|
from common import log
|
||||||
from common import Httpx
|
from common import Httpx
|
||||||
from common import variable
|
from common import variable
|
||||||
from common import scheduler
|
from common import scheduler
|
||||||
from common import lx_script
|
from common import lx_script
|
||||||
from aiohttp.web import Response
|
from aiohttp.web import Response, FileResponse, StreamResponse
|
||||||
import ujson as json
|
import ujson as json
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
@ -33,6 +35,12 @@ import time
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
def handleResult(dic, status = 200) -> Response:
|
def handleResult(dic, status = 200) -> Response:
|
||||||
|
if (not isinstance(dic, dict)):
|
||||||
|
dic = {
|
||||||
|
'code': 0,
|
||||||
|
'msg': 'success',
|
||||||
|
'data': dic
|
||||||
|
}
|
||||||
return Response(body = json.dumps(dic, indent=2, ensure_ascii=False), content_type='application/json', status = status)
|
return Response(body = json.dumps(dic, indent=2, ensure_ascii=False), content_type='application/json', status = status)
|
||||||
|
|
||||||
logger = log.log("main")
|
logger = log.log("main")
|
||||||
@ -99,7 +107,7 @@ async def handle_before_request(app, handler):
|
|||||||
resp = handleResult(body, status)
|
resp = handleResult(body, status)
|
||||||
else:
|
else:
|
||||||
resp = Response(body = str(body), content_type='text/plain', status = status)
|
resp = Response(body = str(body), content_type='text/plain', status = status)
|
||||||
elif (not isinstance(resp, Response)):
|
elif (not isinstance(resp, (Response, FileResponse, StreamResponse))):
|
||||||
resp = Response(body = str(resp), content_type='text/plain', status = 200)
|
resp = Response(body = str(resp), content_type='text/plain', status = 200)
|
||||||
aiologger.info(f'{request.remote_addr + ("" if (request.remote == request.remote_addr) else f"|proxy@{request.remote}")} - {request.method} "{request.path}", {resp.status}')
|
aiologger.info(f'{request.remote_addr + ("" if (request.remote == request.remote_addr) else f"|proxy@{request.remote}")} - {request.method} "{request.path}", {resp.status}')
|
||||||
return resp
|
return resp
|
||||||
@ -142,6 +150,48 @@ async def handle(request):
|
|||||||
async def handle_404(request):
|
async def handle_404(request):
|
||||||
return handleResult({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}, 404)
|
return handleResult({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}, 404)
|
||||||
|
|
||||||
|
async def handle_local(request):
|
||||||
|
try:
|
||||||
|
query = dict(request.query)
|
||||||
|
data = query.get('q')
|
||||||
|
data = createBase64Decode(data.replace('-', '+').replace('_', '/'))
|
||||||
|
data = json.loads(data)
|
||||||
|
t = request.match_info.get('type')
|
||||||
|
data['t'] = t
|
||||||
|
except:
|
||||||
|
return handleResult({'code': 6, 'msg': '请求参数有错', 'data': None}, 404)
|
||||||
|
if (data['t'] == 'u'):
|
||||||
|
if (data['p'] in list(localMusic.map.keys())):
|
||||||
|
return await localMusic.generateAudioFileResonse(data['p'])
|
||||||
|
else:
|
||||||
|
return handleResult({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}, 404)
|
||||||
|
if (data['t'] == 'l'):
|
||||||
|
if (data['p'] in list(localMusic.map.keys())):
|
||||||
|
return await localMusic.generateAudioLyricResponse(data['p'])
|
||||||
|
else:
|
||||||
|
return handleResult({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}, 404)
|
||||||
|
if (data['t'] == 'p'):
|
||||||
|
if (data['p'] in list(localMusic.map.keys())):
|
||||||
|
return await localMusic.generateAudioCoverResonse(data['p'])
|
||||||
|
else:
|
||||||
|
return handleResult({'code': 6, 'msg': '未找到您所请求的资源', 'data': None}, 404)
|
||||||
|
if (data['t'] == 'c'):
|
||||||
|
if (not data['p'] in list(localMusic.map.keys())):
|
||||||
|
return {
|
||||||
|
'code': 0,
|
||||||
|
'msg': 'success',
|
||||||
|
'data': {
|
||||||
|
'file': False,
|
||||||
|
'cover': False,
|
||||||
|
'lyric': False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'code': 0,
|
||||||
|
'msg': 'success',
|
||||||
|
'data': localMusic.checkLocalMusic(data['p'])
|
||||||
|
}
|
||||||
|
|
||||||
app = aiohttp.web.Application(middlewares=[handle_before_request])
|
app = aiohttp.web.Application(middlewares=[handle_before_request])
|
||||||
# mainpage
|
# mainpage
|
||||||
app.router.add_get('/', main)
|
app.router.add_get('/', main)
|
||||||
@ -149,6 +199,7 @@ app.router.add_get('/', main)
|
|||||||
# api
|
# api
|
||||||
app.router.add_get('/{method}/{source}/{songId}/{quality}', handle)
|
app.router.add_get('/{method}/{source}/{songId}/{quality}', handle)
|
||||||
app.router.add_get('/{method}/{source}/{songId}', handle)
|
app.router.add_get('/{method}/{source}/{songId}', handle)
|
||||||
|
app.router.add_get('/local/{type}', handle_local)
|
||||||
|
|
||||||
if (config.read_config('common.allow_download_script')):
|
if (config.read_config('common.allow_download_script')):
|
||||||
app.router.add_get('/script', lx_script.generate_script_response)
|
app.router.add_get('/script', lx_script.generate_script_response)
|
||||||
@ -225,6 +276,7 @@ async def run_app():
|
|||||||
async def initMain():
|
async def initMain():
|
||||||
await scheduler.run()
|
await scheduler.run()
|
||||||
variable.aioSession = aiohttp.ClientSession(trust_env=True)
|
variable.aioSession = aiohttp.ClientSession(trust_env=True)
|
||||||
|
localMusic.initMain()
|
||||||
try:
|
try:
|
||||||
await run_app()
|
await run_app()
|
||||||
logger.info("服务器启动成功,请按下Ctrl + C停止")
|
logger.info("服务器启动成功,请按下Ctrl + C停止")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user