add sonarr/radarr api
This commit is contained in:
@ -22,7 +22,7 @@ async def read_sites(db: Session = Depends(get_db),
|
||||
return Site.list(db)
|
||||
|
||||
|
||||
@router.post("/update", response_model=schemas.Site)
|
||||
@router.put("/", response_model=schemas.Site)
|
||||
async def update_site(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
|
@ -47,7 +47,7 @@ async def create_subscribe(
|
||||
return {"success": result}
|
||||
|
||||
|
||||
@router.post("/update", response_model=schemas.Subscribe)
|
||||
@router.put("/", response_model=schemas.Subscribe)
|
||||
async def update_subscribe(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
@ -67,7 +67,7 @@ async def update_subscribe(
|
||||
return subscribe
|
||||
|
||||
|
||||
@router.post("/delete", response_model=schemas.Response)
|
||||
@router.delete("/", response_model=schemas.Response)
|
||||
async def delete_subscribe(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
|
387
app/api/servarr.py
Normal file
387
app/api/servarr.py
Normal file
@ -0,0 +1,387 @@
|
||||
from typing import Any, List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import settings
|
||||
from version import APP_VERSION
|
||||
|
||||
arr_router = APIRouter()
|
||||
|
||||
|
||||
@arr_router.get("/system/status")
|
||||
async def arr_system_status(apiKey: str) -> Any:
|
||||
"""
|
||||
模拟Radarr、Sonarr系统状态
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
return {
|
||||
"appName": "MoviePilot",
|
||||
"instanceName": "moviepilot",
|
||||
"version": APP_VERSION,
|
||||
"urlBase": "/api/v3"
|
||||
}
|
||||
|
||||
|
||||
@arr_router.get("/qualityProfile")
|
||||
async def arr_qualityProfile(apiKey: str) -> Any:
|
||||
"""
|
||||
模拟Radarr、Sonarr质量配置
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
return [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "默认"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@arr_router.get("/rootfolder")
|
||||
async def arr_rootfolder(apiKey: str) -> Any:
|
||||
"""
|
||||
模拟Radarr、Sonarr根目录
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
return [
|
||||
{
|
||||
"id": 1,
|
||||
"path": settings.LIBRARY_PATH,
|
||||
"accessible": True,
|
||||
"freeSpace": 0,
|
||||
"unmappedFolders": []
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@arr_router.get("/tag")
|
||||
async def arr_tag(apiKey: str) -> Any:
|
||||
"""
|
||||
模拟Radarr、Sonarr标签
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
return [
|
||||
{
|
||||
"id": 1,
|
||||
"label": "默认"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@arr_router.get("/languageprofile")
|
||||
async def arr_languageprofile(apiKey: str) -> Any:
|
||||
"""
|
||||
模拟Radarr、Sonarr语言
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
return {
|
||||
"id": 1,
|
||||
"name": "中文"
|
||||
}
|
||||
|
||||
|
||||
@arr_router.get("/movie", response_model=List[schemas.RadarrMovie])
|
||||
async def arr_movies(apiKey: str) -> Any:
|
||||
"""
|
||||
查询Rardar电影
|
||||
"""
|
||||
"""
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"title": "string",
|
||||
"originalTitle": "string",
|
||||
"originalLanguage": {
|
||||
"id": 0,
|
||||
"name": "string"
|
||||
},
|
||||
"secondaryYear": 0,
|
||||
"secondaryYearSourceId": 0,
|
||||
"sortTitle": "string",
|
||||
"sizeOnDisk": 0,
|
||||
"status": "tba",
|
||||
"overview": "string",
|
||||
"inCinemas": "2023-06-13T09:23:41.494Z",
|
||||
"physicalRelease": "2023-06-13T09:23:41.494Z",
|
||||
"digitalRelease": "2023-06-13T09:23:41.494Z",
|
||||
"physicalReleaseNote": "string",
|
||||
"images": [
|
||||
{
|
||||
"coverType": "unknown",
|
||||
"url": "string",
|
||||
"remoteUrl": "string"
|
||||
}
|
||||
],
|
||||
"website": "string",
|
||||
"remotePoster": "string",
|
||||
"year": 0,
|
||||
"hasFile": true,
|
||||
"youTubeTrailerId": "string",
|
||||
"studio": "string",
|
||||
"path": "string",
|
||||
"qualityProfileId": 0,
|
||||
"monitored": true,
|
||||
"minimumAvailability": "tba",
|
||||
"isAvailable": true,
|
||||
"folderName": "string",
|
||||
"runtime": 0,
|
||||
"cleanTitle": "string",
|
||||
"imdbId": "string",
|
||||
"tmdbId": 0,
|
||||
"titleSlug": "string",
|
||||
"rootFolderPath": "string",
|
||||
"folder": "string",
|
||||
"certification": "string",
|
||||
"genres": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
0
|
||||
],
|
||||
"added": "2023-06-13T09:23:41.494Z",
|
||||
"addOptions": {
|
||||
"ignoreEpisodesWithFiles": true,
|
||||
"ignoreEpisodesWithoutFiles": true,
|
||||
"monitor": "movieOnly",
|
||||
"searchForMovie": true,
|
||||
"addMethod": "manual"
|
||||
},
|
||||
"popularity": 0
|
||||
}
|
||||
]
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.get("/movie/{mid}", response_model=schemas.RadarrMovie)
|
||||
async def arr_movie(apiKey: str) -> Any:
|
||||
"""
|
||||
查询Rardar电影
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.get("/movie/lookup", response_model=List[schemas.RadarrMovie])
|
||||
async def arr_movie_lookup(apiKey: str, term: str) -> Any:
|
||||
"""
|
||||
查询Rardar电影 term: `tmdb:${id}`
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.put("/movie", response_model=schemas.Response)
|
||||
async def arr_add_movie(apiKey: str, title: str, tmdbId: int, year: int) -> Any:
|
||||
"""
|
||||
新增Rardar电影订阅
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.delete("/movie/{mid}", response_model=schemas.Response)
|
||||
async def arr_remove_movie(apiKey: str, mid: int) -> Any:
|
||||
"""
|
||||
删除Rardar电影订阅
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.get("/series", response_model=List[schemas.SonarrSeries])
|
||||
async def arr_series(apiKey: str) -> Any:
|
||||
"""
|
||||
查询Sonarr剧集
|
||||
"""
|
||||
"""
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"title": "string",
|
||||
"sortTitle": "string",
|
||||
"status": "continuing",
|
||||
"ended": true,
|
||||
"profileName": "string",
|
||||
"overview": "string",
|
||||
"nextAiring": "2023-06-13T09:08:17.624Z",
|
||||
"previousAiring": "2023-06-13T09:08:17.624Z",
|
||||
"network": "string",
|
||||
"airTime": "string",
|
||||
"images": [
|
||||
{
|
||||
"coverType": "unknown",
|
||||
"url": "string",
|
||||
"remoteUrl": "string"
|
||||
}
|
||||
],
|
||||
"originalLanguage": {
|
||||
"id": 0,
|
||||
"name": "string"
|
||||
},
|
||||
"remotePoster": "string",
|
||||
"seasons": [
|
||||
{
|
||||
"seasonNumber": 0,
|
||||
"monitored": true,
|
||||
"statistics": {
|
||||
"nextAiring": "2023-06-13T09:08:17.624Z",
|
||||
"previousAiring": "2023-06-13T09:08:17.624Z",
|
||||
"episodeFileCount": 0,
|
||||
"episodeCount": 0,
|
||||
"totalEpisodeCount": 0,
|
||||
"sizeOnDisk": 0,
|
||||
"releaseGroups": [
|
||||
"string"
|
||||
],
|
||||
"percentOfEpisodes": 0
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"coverType": "unknown",
|
||||
"url": "string",
|
||||
"remoteUrl": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"year": 0,
|
||||
"path": "string",
|
||||
"qualityProfileId": 0,
|
||||
"seasonFolder": true,
|
||||
"monitored": true,
|
||||
"useSceneNumbering": true,
|
||||
"runtime": 0,
|
||||
"tvdbId": 0,
|
||||
"tvRageId": 0,
|
||||
"tvMazeId": 0,
|
||||
"firstAired": "2023-06-13T09:08:17.624Z",
|
||||
"seriesType": "standard",
|
||||
"cleanTitle": "string",
|
||||
"imdbId": "string",
|
||||
"titleSlug": "string",
|
||||
"rootFolderPath": "string",
|
||||
"folder": "string",
|
||||
"certification": "string",
|
||||
"genres": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
0
|
||||
],
|
||||
"added": "2023-06-13T09:08:17.624Z",
|
||||
"addOptions": {
|
||||
"ignoreEpisodesWithFiles": true,
|
||||
"ignoreEpisodesWithoutFiles": true,
|
||||
"monitor": "unknown",
|
||||
"searchForMissingEpisodes": true,
|
||||
"searchForCutoffUnmetEpisodes": true
|
||||
},
|
||||
"ratings": {
|
||||
"votes": 0,
|
||||
"value": 0
|
||||
},
|
||||
"statistics": {
|
||||
"seasonCount": 0,
|
||||
"episodeFileCount": 0,
|
||||
"episodeCount": 0,
|
||||
"totalEpisodeCount": 0,
|
||||
"sizeOnDisk": 0,
|
||||
"releaseGroups": [
|
||||
"string"
|
||||
],
|
||||
"percentOfEpisodes": 0
|
||||
},
|
||||
"episodesChanged": true
|
||||
}
|
||||
]
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.get("/series/{tid}")
|
||||
async def arr_serie(apiKey: str) -> Any:
|
||||
"""
|
||||
查询Sonarr剧集
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.get("/series/lookup")
|
||||
async def arr_series_lookup(apiKey: str, term: str) -> Any:
|
||||
"""
|
||||
查询Sonarr剧集 term: `tvdb:${id}` title
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.put("/series")
|
||||
async def arr_add_series(apiKey: str, title: str, seasons: list, year: int) -> Any:
|
||||
"""
|
||||
新增Sonarr剧集订阅
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
||||
|
||||
|
||||
@arr_router.delete("/series/{tid}")
|
||||
async def arr_remove_series(apiKey: str, tid: int) -> Any:
|
||||
"""
|
||||
删除Sonarr剧集订阅
|
||||
"""
|
||||
if not apiKey or apiKey != settings.API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="认证失败!",
|
||||
)
|
@ -3,6 +3,7 @@ from fastapi import FastAPI
|
||||
from uvicorn import Config
|
||||
|
||||
from app.api.apiv1 import api_router
|
||||
from app.api.servarr import arr_router
|
||||
from app.command import Command
|
||||
from app.core.config import settings
|
||||
from app.core.module import ModuleManager
|
||||
@ -19,6 +20,9 @@ App = FastAPI(title=settings.PROJECT_NAME,
|
||||
# API路由
|
||||
App.include_router(api_router, prefix=settings.API_V1_STR)
|
||||
|
||||
# Radarr、Sonarr路由
|
||||
App.include_router(arr_router, prefix="/api/v3")
|
||||
|
||||
# uvicorn服务
|
||||
Server = uvicorn.Server(Config(App, host=settings.HOST, port=settings.PORT, reload=settings.RELOAD))
|
||||
|
||||
|
@ -4,3 +4,4 @@ from .response import Response
|
||||
from .site import Site
|
||||
from .subscribe import Subscribe
|
||||
from .context import Context
|
||||
from .servarr import RadarrMovie, SonarrSeries
|
||||
|
54
app/schemas/servarr.py
Normal file
54
app/schemas/servarr.py
Normal file
@ -0,0 +1,54 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class RadarrMovie(BaseModel):
|
||||
id: int
|
||||
title: str
|
||||
isAvailable: bool
|
||||
monitored: bool
|
||||
tmdbId: Optional[int]
|
||||
imdbId: Optional[str]
|
||||
titleSlug: Optional[str]
|
||||
folderName: Optional[str]
|
||||
path: Optional[str]
|
||||
profileId: Optional[int]
|
||||
qualityProfileId: Optional[int]
|
||||
added: Optional[str]
|
||||
hasFile: bool
|
||||
|
||||
|
||||
class SonarrSeries:
|
||||
title: str
|
||||
sortTitle: Optional[str]
|
||||
seasonCount: Optional[int]
|
||||
status: Optional[str]
|
||||
overview: Optional[str]
|
||||
network: Optional[str]
|
||||
airTime: Optional[str]
|
||||
images: Optional[list]
|
||||
remotePoster: Optional[str]
|
||||
seasons: Optional[list]
|
||||
year: int
|
||||
path: Optional[str]
|
||||
profileId: Optional[int]
|
||||
languageProfileId: Optional[int]
|
||||
seasonFolder: Optional[bool]
|
||||
monitored: Optional[bool]
|
||||
useSceneNumbering: Optional[bool]
|
||||
runtime: Optional[int]
|
||||
tvdbId: Optional[int]
|
||||
tvRageId: Optional[int]
|
||||
tvMazeId: Optional[int]
|
||||
firstAired: Optional[str]
|
||||
seriesType: Optional[str]
|
||||
cleanTitle: Optional[str]
|
||||
imdbId: Optional[str]
|
||||
titleSlug: Optional[str]
|
||||
certification: Optional[str]
|
||||
genres: Optional[list]
|
||||
tags: Optional[list]
|
||||
added: Optional[str]
|
||||
ratings: Optional[dict]
|
||||
qualityProfileId: Optional[int]
|
||||
statistics: Optional[dict]
|
Reference in New Issue
Block a user