mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
feat(v3): add music automation workflow
This commit is contained in:
@@ -8,6 +8,7 @@ from .file import *
|
||||
from .history import *
|
||||
from .mediaserver import *
|
||||
from .message import *
|
||||
from .music import *
|
||||
from .monitoring import *
|
||||
from .plugin import *
|
||||
from .response import *
|
||||
|
||||
@@ -2,6 +2,8 @@ from typing import Optional, Dict, List, Union, Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.music import MusicInfo, MusicMeta
|
||||
|
||||
|
||||
class MetaInfo(BaseModel):
|
||||
"""
|
||||
@@ -15,7 +17,7 @@ class MetaInfo(BaseModel):
|
||||
title: Optional[str] = None
|
||||
# 副标题
|
||||
subtitle: Optional[str] = None
|
||||
# 类型 电影、电视剧
|
||||
# 类型 电影、电视剧、音乐
|
||||
type: Optional[str] = None
|
||||
# 名称
|
||||
name: Optional[str] = None
|
||||
@@ -252,7 +254,7 @@ class TorrentInfo(BaseModel):
|
||||
labels: Optional[list] = Field(default_factory=list)
|
||||
# 种子优先级
|
||||
pri_order: Optional[int] = 0
|
||||
# 种子分类 电影/电视剧
|
||||
# 种子分类 电影/电视剧/音乐
|
||||
category: Optional[str] = None
|
||||
# 促销
|
||||
volume_factor: Optional[str] = None
|
||||
@@ -319,9 +321,9 @@ class Context(BaseModel):
|
||||
上下文
|
||||
"""
|
||||
# 元数据
|
||||
meta_info: Optional[Union[MetaInfo, Any]] = None
|
||||
meta_info: Optional[Union[MusicMeta, MetaInfo, Any]] = None
|
||||
# 媒体信息
|
||||
media_info: Optional[Union[MediaInfo, Any]] = None
|
||||
media_info: Optional[Union[MusicInfo, MediaInfo, Any]] = None
|
||||
# 种子信息
|
||||
torrent_info: Optional[TorrentInfo] = None
|
||||
# 候选资源来源:rss、spider、search、unknown
|
||||
|
||||
@@ -12,7 +12,7 @@ class DownloadHistory(BaseModel):
|
||||
id: int
|
||||
# 保存路程
|
||||
path: Optional[str] = None
|
||||
# 类型:电影、电视剧
|
||||
# 类型:电影、电视剧、音乐
|
||||
type: Optional[str] = None
|
||||
# 标题
|
||||
title: Optional[str] = None
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class MusicMeta(BaseModel):
|
||||
"""音乐名称及音频文件解析结果。"""
|
||||
|
||||
type: Literal["音乐"] = "音乐"
|
||||
org_string: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
artists: list[str] = Field(default_factory=list)
|
||||
artist: Optional[str] = None
|
||||
album: Optional[str] = None
|
||||
album_artist: Optional[str] = None
|
||||
year: Optional[int] = None
|
||||
disc_number: Optional[int] = None
|
||||
track_number: Optional[int] = None
|
||||
total_discs: Optional[int] = None
|
||||
total_tracks: Optional[int] = None
|
||||
version: Optional[str] = None
|
||||
audio_format: Optional[str] = None
|
||||
bit_depth: Optional[int] = None
|
||||
sample_rate: Optional[int] = None
|
||||
bitrate: Optional[int] = None
|
||||
duration: Optional[int] = None
|
||||
isrc: Optional[str] = None
|
||||
media_source: Optional[str] = None
|
||||
media_id: Optional[str] = None
|
||||
|
||||
|
||||
class MusicInfo(BaseModel):
|
||||
"""标准化音乐元数据信息。"""
|
||||
|
||||
type: Literal["音乐"] = "音乐"
|
||||
source: Optional[str] = None
|
||||
media_id: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
artists: list[str] = Field(default_factory=list)
|
||||
artist: Optional[str] = None
|
||||
album: Optional[str] = None
|
||||
album_artist: Optional[str] = None
|
||||
year: Optional[int] = None
|
||||
release_date: Optional[str] = None
|
||||
disc_number: Optional[int] = None
|
||||
track_number: Optional[int] = None
|
||||
total_tracks: Optional[int] = None
|
||||
duration: Optional[int] = None
|
||||
isrc: Optional[str] = None
|
||||
cover_url: Optional[str] = None
|
||||
lyrics: Optional[str] = None
|
||||
version: Optional[str] = None
|
||||
category: Optional[str] = ""
|
||||
names: list[str] = Field(default_factory=list)
|
||||
detail_link: Optional[str] = None
|
||||
listen_count: Optional[int] = None
|
||||
raw_data: dict[str, Any] = Field(default_factory=dict)
|
||||
title_year: Optional[str] = None
|
||||
poster_path: Optional[str] = None
|
||||
backdrop_path: Optional[str] = None
|
||||
mediaid_prefix: Optional[str] = None
|
||||
overview: Optional[str] = None
|
||||
vote_average: float = 0.0
|
||||
|
||||
|
||||
class MusicRecognizeRequest(BaseModel):
|
||||
"""音乐元数据详情识别请求。"""
|
||||
|
||||
source: str
|
||||
media_id: str
|
||||
@@ -1,9 +1,10 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, List, Optional
|
||||
from typing import Any, Callable, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.context import MetaInfo, MediaInfo
|
||||
from app.schemas.music import MusicInfo, MusicMeta
|
||||
from app.schemas.file import FileItem
|
||||
from app.schemas.history import DownloadHistory
|
||||
from app.schemas.system import TransferDirectoryConf
|
||||
@@ -96,7 +97,7 @@ class TransferJobTask(BaseModel):
|
||||
文件整理作业任务
|
||||
"""
|
||||
fileitem: Optional[FileItem] = None
|
||||
meta: Optional[MetaInfo] = None
|
||||
meta: Optional[Union[MusicMeta, MetaInfo]] = None
|
||||
state: Optional[str] = None
|
||||
downloader: Optional[str] = None
|
||||
download_hash: Optional[str] = None
|
||||
@@ -106,7 +107,7 @@ class TransferJob(BaseModel):
|
||||
"""
|
||||
文件整理作业
|
||||
"""
|
||||
media: Optional[MediaInfo] = None
|
||||
media: Optional[Union[MusicInfo, MediaInfo]] = None
|
||||
season: Optional[int] = None
|
||||
tasks: Optional[List[TransferJobTask]] = Field(default_factory=list)
|
||||
|
||||
|
||||
+18
-5
@@ -6,22 +6,31 @@ from typing import Optional
|
||||
class MediaType(Enum):
|
||||
MOVIE = '电影'
|
||||
TV = '电视剧'
|
||||
MUSIC = '音乐'
|
||||
COLLECTION = '系列'
|
||||
UNKNOWN = '未知'
|
||||
|
||||
@staticmethod
|
||||
def from_agent(key: str) -> Optional["MediaType"]:
|
||||
"""'movie' -> MediaType.MOVIE, 'tv' -> MediaType.TV, 否则 None"""
|
||||
_map = {"movie": MediaType.MOVIE, "tv": MediaType.TV}
|
||||
"""将 Agent 媒体类型转换为 MediaType。"""
|
||||
_map = {
|
||||
"movie": MediaType.MOVIE,
|
||||
"tv": MediaType.TV,
|
||||
"music": MediaType.MUSIC,
|
||||
}
|
||||
return _map.get(key.strip().lower() if key else "")
|
||||
|
||||
def to_agent(self) -> str:
|
||||
"""MediaType.MOVIE -> 'movie', MediaType.TV -> 'tv', 其他返回 .value"""
|
||||
return {MediaType.MOVIE: "movie", MediaType.TV: "tv"}.get(self, self.value)
|
||||
"""将 MediaType 转换为 Agent 使用的媒体类型。"""
|
||||
return {
|
||||
MediaType.MOVIE: "movie",
|
||||
MediaType.TV: "tv",
|
||||
MediaType.MUSIC: "music",
|
||||
}.get(self, self.value)
|
||||
|
||||
|
||||
def media_type_to_agent(value) -> Optional[str]:
|
||||
"""将 MediaType 枚举或中文字符串统一转为 'movie'/'tv'"""
|
||||
"""将 MediaType 枚举或字符串统一转换为 Agent 媒体类型。"""
|
||||
if isinstance(value, MediaType):
|
||||
return value.to_agent()
|
||||
if isinstance(value, str):
|
||||
@@ -397,6 +406,8 @@ class MediaRecognizeType(Enum):
|
||||
Bangumi = "Bangumi"
|
||||
# AniList
|
||||
AniList = "AniList"
|
||||
# MusicBrainz
|
||||
MusicBrainz = "MusicBrainz"
|
||||
|
||||
|
||||
# 用户配置Key字典
|
||||
@@ -448,6 +459,8 @@ class OtherModulesType(Enum):
|
||||
PostgreSQL = "PostgreSQL"
|
||||
# Redis
|
||||
Redis = "Redis"
|
||||
# ListenBrainz
|
||||
ListenBrainz = "ListenBrainz"
|
||||
|
||||
|
||||
class NameValueEnum(Enum):
|
||||
|
||||
Reference in New Issue
Block a user