mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
fix(v3): complete music recognition and scraping
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from app import schemas
|
||||
from app.chain.media import MediaChain
|
||||
from app.chain.music import MusicChain
|
||||
from app.chain.tmdb import TmdbChain
|
||||
from app.core.config import settings
|
||||
from app.core.context import Context
|
||||
from app.core.music import MusicInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.core.meta import MetaBase
|
||||
from app.core.metainfo import MetaInfo, MetaInfoPath
|
||||
@@ -23,6 +26,17 @@ router = APIRouter()
|
||||
MediaSource = str
|
||||
|
||||
|
||||
def _is_valid_source_media_id(source: Optional[str], media_id: str) -> bool:
|
||||
"""按媒体数据源校验原生 ID,MusicBrainz 使用 UUID,其它现有来源使用数字 ID。"""
|
||||
if source == "musicbrainz":
|
||||
try:
|
||||
UUID(media_id)
|
||||
return True
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
return media_id.isdigit()
|
||||
|
||||
|
||||
def _build_recognize_metainfo(
|
||||
title: str,
|
||||
subtitle: Optional[str] = None,
|
||||
@@ -148,6 +162,12 @@ async def recognize_file(
|
||||
"""
|
||||
根据文件路径识别媒体信息
|
||||
"""
|
||||
if MusicChain.is_audio_path(path) or source == "musicbrainz":
|
||||
meta_info, media_info = await MusicChain().async_recognize_by_path(
|
||||
path=path,
|
||||
source=source or "musicbrainz",
|
||||
)
|
||||
return Context(meta_info=meta_info, media_info=media_info).to_dict()
|
||||
# 识别媒体信息
|
||||
context = await MediaChain().async_recognize_by_path(path, source=source)
|
||||
if context:
|
||||
@@ -255,9 +275,32 @@ def scrape(
|
||||
return schemas.Response(
|
||||
success=False, message="指定媒体ID时必须同时指定媒体数据源"
|
||||
)
|
||||
if normalized_media_id and not normalized_media_id.isdigit():
|
||||
if normalized_media_id and not _is_valid_source_media_id(media_source, normalized_media_id):
|
||||
return schemas.Response(success=False, message="媒体ID格式无效")
|
||||
|
||||
is_music = (
|
||||
type_name == MediaType.MUSIC
|
||||
or media_source == "musicbrainz"
|
||||
or MusicChain.is_audio_path(fileitem.path)
|
||||
)
|
||||
if is_music:
|
||||
if type_name not in (None, MediaType.MUSIC):
|
||||
return schemas.Response(success=False, message="MusicBrainz 只能用于音乐刮削")
|
||||
music_info: Optional[MusicInfo] = None
|
||||
if normalized_media_id:
|
||||
music_info = MusicChain().recognize(
|
||||
source=media_source or "musicbrainz",
|
||||
media_id=normalized_media_id,
|
||||
)
|
||||
if not music_info:
|
||||
return schemas.Response(success=False, message="刮削失败,无法识别音乐信息")
|
||||
success, message = MusicChain().scrape_metadata(
|
||||
fileitem=fileitem,
|
||||
mediainfo=music_info,
|
||||
overwrite=True,
|
||||
)
|
||||
return schemas.Response(success=success, message=message)
|
||||
|
||||
chain = MediaChain()
|
||||
if normalized_media_id:
|
||||
meta_info = MetaInfoPath(Path(fileitem.path))
|
||||
|
||||
@@ -10,6 +10,14 @@ from app.core.security import verify_token
|
||||
router = APIRouter()
|
||||
|
||||
CountParam = Annotated[int, Query(ge=1, le=100)]
|
||||
MusicRangeParam = Annotated[
|
||||
str,
|
||||
Query(pattern="^(this_week|this_month|this_year|all_time)$"),
|
||||
]
|
||||
MusicSortParam = Annotated[
|
||||
str,
|
||||
Query(pattern="^listen_count\\.(desc|asc)$"),
|
||||
]
|
||||
|
||||
|
||||
def _serialize_music(info: MusicInfo) -> schemas.MusicInfo:
|
||||
@@ -59,12 +67,19 @@ async def recognize_music(
|
||||
async def explore_music(
|
||||
page: Annotated[int, Query(ge=1)] = 1,
|
||||
count: CountParam = 30,
|
||||
range_name: MusicRangeParam = "this_month",
|
||||
sort_by: MusicSortParam = "listen_count.desc",
|
||||
min_listen_count: Annotated[int, Query(ge=0)] = 0,
|
||||
with_cover: bool = False,
|
||||
_: schemas.TokenPayload = Depends(verify_token),
|
||||
) -> list[schemas.MusicInfo]:
|
||||
"""按月度全站收听榜单分页返回可搜索和订阅的音乐候选。"""
|
||||
"""按周期、热度和封面条件返回可搜索和订阅的音乐候选。"""
|
||||
results = await MusicChain().async_chart(
|
||||
range_name="this_month",
|
||||
range_name=range_name,
|
||||
page=page,
|
||||
count=count,
|
||||
sort_by=sort_by,
|
||||
min_listen_count=min_listen_count,
|
||||
with_cover=with_cover,
|
||||
)
|
||||
return [_serialize_music(info) for info in results]
|
||||
|
||||
Reference in New Issue
Block a user