1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2026-03-23 09:29:44 +08:00

feat: 增加musicfree插件集成功能

This commit is contained in:
Boluofan
2025-12-09 16:21:53 +08:00
committed by 涵曦
parent 881d0bca5c
commit 67d93feec5
25 changed files with 5189 additions and 185 deletions

View File

@@ -95,7 +95,7 @@ security = HTTPBasic()
def verification(
credentials: Annotated[HTTPBasicCredentials, Depends(security)],
credentials: Annotated[HTTPBasicCredentials, Depends(security)],
):
current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = config.httpauth_username.encode("utf8")
@@ -253,6 +253,239 @@ def searchmusic(name: str = "", Verifcation=Depends(verification)):
return xiaomusic.searchmusic(name)
@app.get("/api/search/online")
async def search_online_music(
keyword: str = Query(..., description="搜索关键词"),
plugin: str = Query("all", description="指定插件名称all表示搜索所有插件"),
page: int = Query(1, description="页码"),
limit: int = Query(20, description="每页数量"),
Verifcation=Depends(verification)
):
"""在线音乐搜索API"""
try:
if not keyword:
return {"success": False, "error": "Keyword required"}
return await xiaomusic.get_music_list_online(keyword=keyword, plugin=plugin, page=page, limit=limit)
except Exception as e:
return {"success": False, "error": str(e)}
@app.get("/api/proxy/real-music-url")
async def get_real_music_url(url: str = Query(..., description="音乐下载URL"),
Verifcation=Depends(verification)):
"""通过服务端代理获取真实的音乐播放URL避免CORS问题"""
try:
# 获取真实的音乐播放URL
return await xiaomusic.get_real_url_of_openapi(url)
except Exception as e:
log.error(f"获取真实音乐URL失败: {e}")
# 如果代理获取失败仍然返回原始URL
return {
"success": False,
"realUrl": url,
"error": str(e)
}
@app.post("/api/play/getMediaSource")
async def get_media_source(
request: Request, Verifcation=Depends(verification)
):
"""获取音乐真实播放URL"""
try:
# 获取请求数据
data = await request.json()
# 调用公共函数处理
return await xiaomusic.get_media_source_url(data)
except Exception as e:
return {"success": False, "error": str(e)}
@app.post("/api/play/getLyric")
async def get_media_lyric(
request: Request, Verifcation=Depends(verification)
):
"""获取音乐真实播放URL"""
try:
# 获取请求数据
data = await request.json()
# 调用公共函数处理
return await xiaomusic.get_media_lyric(data)
except Exception as e:
return {"success": False, "error": str(e)}
@app.post("/api/play/online")
async def play_online_music(
request: Request, Verifcation=Depends(verification)
):
"""设备端在线播放插件音乐"""
try:
# 获取请求数据
data = await request.json()
did = data.get('did')
openapi_info = xiaomusic.js_plugin_manager.get_openapi_info()
if openapi_info.get("enabled", False):
media_source = await xiaomusic.get_real_url_of_openapi(data.get('url'))
else:
# 调用公共函数处理,获取音乐真实播放URL
media_source = await xiaomusic.get_media_source_url(data)
if not media_source or not media_source.get('url'):
return {"success": False, "error": "Failed to get media source URL"}
url = media_source.get('url')
decoded_url = urllib.parse.unquote(url)
return await xiaomusic.play_url(did=did, arg1=decoded_url)
except Exception as e:
return {"success": False, "error": str(e)}
# =====================================插件入口函数===============
@app.get("/api/js-plugins")
def get_js_plugins(
enabled_only: bool = Query(False, description="是否只返回启用的插件"),
Verifcation=Depends(verification)
):
"""获取插件列表"""
try:
if not hasattr(xiaomusic, 'js_plugin_manager') or not xiaomusic.js_plugin_manager:
return {"success": False, "error": "JS Plugin Manager not available"}
# 重新加载插件
# xiaomusic.js_plugin_manager.reload_plugins()
if enabled_only:
plugins = xiaomusic.js_plugin_manager.get_enabled_plugins()
else:
plugins = xiaomusic.js_plugin_manager.get_plugin_list()
return {"success": True, "data": plugins}
except Exception as e:
return {"success": False, "error": str(e)}
@app.put("/api/js-plugins/{plugin_name}/enable")
def enable_js_plugin(plugin_name: str, Verifcation=Depends(verification)):
"""启用插件"""
try:
if not hasattr(xiaomusic, 'js_plugin_manager') or not xiaomusic.js_plugin_manager:
return {"success": False, "error": "JS Plugin Manager not available"}
success = xiaomusic.js_plugin_manager.enable_plugin(plugin_name)
return {"success": success}
except Exception as e:
return {"success": False, "error": str(e)}
@app.put("/api/js-plugins/{plugin_name}/disable")
def disable_js_plugin(plugin_name: str, Verifcation=Depends(verification)):
"""禁用插件"""
try:
if not hasattr(xiaomusic, 'js_plugin_manager') or not xiaomusic.js_plugin_manager:
return {"success": False, "error": "JS Plugin Manager not available"}
success = xiaomusic.js_plugin_manager.disable_plugin(plugin_name)
return {"success": success}
except Exception as e:
return {"success": False, "error": str(e)}
@app.delete("/api/js-plugins/{plugin_name}/uninstall")
def uninstall_js_plugin(plugin_name: str, Verifcation=Depends(verification)):
"""卸载插件"""
try:
if not hasattr(xiaomusic, 'js_plugin_manager') or not xiaomusic.js_plugin_manager:
return {"success": False, "error": "JS Plugin Manager not available"}
success = xiaomusic.js_plugin_manager.uninstall_plugin(plugin_name)
return {"success": success}
except Exception as e:
return {"success": False, "error": str(e)}
@app.post("/api/js-plugins/upload")
async def upload_js_plugin(
file: UploadFile = File(...),
verification=Depends(verification)
):
"""上传 JS 插件"""
try:
# 验证文件扩展名
if not file.filename.endswith('.js'):
raise HTTPException(status_code=400, detail="只允许上传 .js 文件")
# 使用 JSPluginManager 中定义的插件目录
if not hasattr(xiaomusic, 'js_plugin_manager') or not xiaomusic.js_plugin_manager:
raise HTTPException(status_code=500, detail="JS Plugin Manager not available")
plugin_dir = xiaomusic.js_plugin_manager.plugins_dir
os.makedirs(plugin_dir, exist_ok=True)
file_path = os.path.join(plugin_dir, file.filename)
# 校验是否已存在同名js插件 存在则提示,停止上传
if os.path.exists(file_path):
raise HTTPException(status_code=409, detail=f"插件 {file.filename} 已存在,请重命名后再上传")
file_path = os.path.join(plugin_dir, file.filename)
# 写入文件内容
async with aiofiles.open(file_path, 'wb') as f:
content = await file.read()
await f.write(content)
# 更新插件配置文件
plugin_name = os.path.splitext(file.filename)[0]
xiaomusic.js_plugin_manager.update_plugin_config(plugin_name, file.filename)
# 重新加载插件
xiaomusic.js_plugin_manager.reload_plugins()
return {"success": True, "message": "插件上传成功"}
except Exception as e:
return {"success": False, "error": str(e)}
# =====================================开放接口配置函数===============
@app.get("/api/openapi/load")
def get_openapi_info(
Verifcation=Depends(verification)
):
"""获取开放接口配置信息"""
try:
openapi_info = xiaomusic.js_plugin_manager.get_openapi_info()
return {"success": True, "data": openapi_info}
except Exception as e:
return {"success": False, "error": str(e)}
@app.post("/api/openapi/toggle")
def toggle_openapi(Verifcation=Depends(verification)):
"""开放接口状态切换"""
try:
return xiaomusic.js_plugin_manager.toggle_openapi()
except Exception as e:
return {"success": False, "error": str(e)}
@app.post("/api/openapi/updateUrl")
async def update_openapi_url(request: Request, Verifcation=Depends(verification)):
"""更新开放接口地址"""
try:
request_json = await request.json()
search_url = request_json.get('search_url')
if not request_json or 'search_url' not in request_json:
return {"success": False, "error": "Missing 'search_url' in request body"}
return xiaomusic.js_plugin_manager.update_openapi_url(search_url)
except Exception as e:
return {"success": False, "error": str(e)}
# =====================================开放接口函数END===============
@app.get("/playingmusic")
def playingmusic(did: str = "", Verifcation=Depends(verification)):
if not xiaomusic.did_exist(did):
@@ -344,7 +577,7 @@ async def musiclist(Verifcation=Depends(verification)):
@app.get("/musicinfo")
async def musicinfo(
name: str, musictag: bool = False, Verifcation=Depends(verification)
name: str, musictag: bool = False, Verifcation=Depends(verification)
):
url, _ = await xiaomusic.get_music_url(name)
info = {
@@ -359,9 +592,9 @@ async def musicinfo(
@app.get("/musicinfos")
async def musicinfos(
name: list[str] = Query(None),
musictag: bool = False,
Verifcation=Depends(verification),
name: list[str] = Query(None),
musictag: bool = False,
Verifcation=Depends(verification),
):
ret = []
for music_name in name:
@@ -724,7 +957,7 @@ class PlayListUpdateObj(BaseModel):
# 修改歌单名字
@app.post("/playlistupdatename")
async def playlistupdatename(
data: PlayListUpdateObj, Verifcation=Depends(verification)
data: PlayListUpdateObj, Verifcation=Depends(verification)
):
ret = xiaomusic.play_list_update_name(data.oldname, data.newname)
if ret:
@@ -769,7 +1002,7 @@ async def playlistdelmusic(data: PlayListMusicObj, Verifcation=Depends(verificat
# 歌单更新歌曲
@app.post("/playlistupdatemusic")
async def playlistupdatemusic(
data: PlayListMusicObj, Verifcation=Depends(verification)
data: PlayListMusicObj, Verifcation=Depends(verification)
):
ret = xiaomusic.play_list_update_music(data.name, data.music_list)
if ret:
@@ -790,7 +1023,7 @@ async def getplaylist(name: str, Verifcation=Depends(verification)):
# 更新版本
@app.post("/updateversion")
async def updateversion(
version: str = "", lite: bool = True, Verifcation=Depends(verification)
version: str = "", lite: bool = True, Verifcation=Depends(verification)
):
ret = await update_version(version, lite)
if ret != "OK":
@@ -821,7 +1054,7 @@ def access_key_verification(file_path, key, code):
if key is not None:
current_key_bytes = key.encode("utf8")
correct_key_bytes = (
config.httpauth_username + config.httpauth_password
config.httpauth_username + config.httpauth_password
).encode("utf8")
is_correct_key = secrets.compare_digest(correct_key_bytes, current_key_bytes)
if is_correct_key:
@@ -832,7 +1065,7 @@ def access_key_verification(file_path, key, code):
correct_code_bytes = (
hashlib.sha256(
(
file_path + config.httpauth_username + config.httpauth_password
file_path + config.httpauth_username + config.httpauth_password
).encode("utf-8")
)
.hexdigest()
@@ -1017,8 +1250,8 @@ JWT_EXPIRE_SECONDS = 60 * 5 # 5 分钟有效期(足够前端连接和重连
@app.get("/generate_ws_token")
def generate_ws_token(
did: str,
_: bool = Depends(verification), # 复用 HTTP Basic 验证
did: str,
_: bool = Depends(verification), # 复用 HTTP Basic 验证
):
if not xiaomusic.did_exist(did):
raise HTTPException(status_code=400, detail="Invalid did")