diff --git a/README.md b/README.md index 58bb3ab5..97d8e31f 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,13 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 docker pull jxxghp/moviepilot:latest ``` - - Windows - 下载 [MoviePilot.exe](https://github.com/jxxghp/MoviePilot/releases),双击运行后自动生成配置文件目录。 + 下载 [MoviePilot.exe](https://github.com/jxxghp/MoviePilot/releases),双击运行后自动生成配置文件目录,访问:http://localhost:3000 +- 群晖套件 + + 添加套件源:https://spk7.imnks.com/ - 本地运行 @@ -51,47 +53,73 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 2) 将工程 [MoviePilot-Resources](https://github.com/jxxghp/MoviePilot-Resources) resources目录下的所有文件复制到`app/helper`目录 3) 执行命令:`pip install -r requirements.txt` 安装依赖 4) 执行命令:`python app/main.py` 启动服务 + 5) 根据前端项目 [MoviePilot-Frontend](https://github.com/jxxghp/MoviePilot-Frontend) 说明,启动前端服务 ## 配置 -项目的所有配置均通过环境变量进行设置,支持两种配置方式: -- 在Docker环境变量部分或Windows系统环境变量中进行参数配置,如未自动显示配置项则需要手动增加对应环境变量。 -- 下载 [app.env](https://github.com/jxxghp/MoviePilot/raw/main/config/app.env) 配置文件,修改好配置后放置到配置文件映射路径根目录,配置项可根据说明自主增减。 - 配置文件映射路径:`/config`,配置项生效优先级:环境变量 > env文件 > 默认值,**部分参数如路径映射、站点认证、权限端口、时区等必须通过环境变量进行配置**。 > ❗号标识的为必填项,其它为可选项,可选项可删除配置变量从而使用默认值。 -### 1. **基础设置** +### 1. **环境变量** -- **❗NGINX_PORT:** WEB服务端口,默认`3000`,可自行修改,不能与API服务端口冲突(仅支持环境变量配置) -- **❗PORT:** API服务端口,默认`3001`,可自行修改,不能与WEB服务端口冲突(仅支持环境变量配置) -- **PUID**:运行程序用户的`uid`,默认`0`(仅支持环境变量配置) -- **PGID**:运行程序用户的`gid`,默认`0`(仅支持环境变量配置) -- **UMASK**:掩码权限,默认`000`,可以考虑设置为`022`(仅支持环境变量配置) -- **PROXY_HOST:** 网络代理,访问themoviedb或者重启更新需要使用代理访问,格式为`http(s)://ip:port`、`socks5://user:pass@host:port`(仅支持环境变量配置) -- **MOVIEPILOT_AUTO_UPDATE:** 重启时自动更新,`true`/`release`/`dev`/`false`,默认`release`,需要能正常连接Github **注意:如果出现网络问题可以配置`PROXY_HOST`**(仅支持环境变量配置) +- **❗NGINX_PORT:** WEB服务端口,默认`3000`,可自行修改,不能与API服务端口冲突 +- **❗PORT:** API服务端口,默认`3001`,可自行修改,不能与WEB服务端口冲突 +- **PUID**:运行程序用户的`uid`,默认`0` +- **PGID**:运行程序用户的`gid`,默认`0` +- **UMASK**:掩码权限,默认`000`,可以考虑设置为`022` +- **PROXY_HOST:** 网络代理,访问themoviedb或者重启更新需要使用代理访问,格式为`http(s)://ip:port`、`socks5://user:pass@host:port` +- **MOVIEPILOT_AUTO_UPDATE:** 重启时自动更新,`true`/`release`/`dev`/`false`,默认`release`,需要能正常连接Github **注意:如果出现网络问题可以配置`PROXY_HOST`** - **AUTO_UPDATE_RESOURCE**:启动时自动检测和更新资源包(站点索引及认证等),`true`/`false`,默认`true`,需要能正常连接Github,仅支持Docker ---- -- **❗SUPERUSER:** 超级管理员用户名,默认`admin`,安装后使用该用户登录后台管理界面 -- **❗SUPERUSER_PASSWORD:** 超级管理员初始密码,默认`password`,建议修改为复杂密码 +- **❗AUTH_SITE:** 认证站点(认证通过后才能使用站点相关功能),支持配置多个认证站点,使用`,`分隔,如:`iyuu,hhclub`,会依次执行认证操作,直到有一个站点认证成功。 + + 配置`AUTH_SITE`后,需要根据下表配置对应站点的认证参数,认证资源`v1.0.2`支持`iyuu`/`hhclub`/`audiences`/`hddolby`/`zmpt`/`freefarm`/`hdfans`/`wintersakura`/`leaves`/`1ptba`/`icc2022`/`ptlsp`/`xingtan`/`ptvicomo`/`agsvpt` + + | 站点 | 参数 | + |:------------:|:-----------------------------------------------------:| + | iyuu | `IYUU_SIGN`:IYUU登录令牌 | + | hhclub | `HHCLUB_USERNAME`:用户名
`HHCLUB_PASSKEY`:密钥 | + | audiences | `AUDIENCES_UID`:用户ID
`AUDIENCES_PASSKEY`:密钥 | + | hddolby | `HDDOLBY_ID`:用户ID
`HDDOLBY_PASSKEY`:密钥 | + | zmpt | `ZMPT_UID`:用户ID
`ZMPT_PASSKEY`:密钥 | + | freefarm | `FREEFARM_UID`:用户ID
`FREEFARM_PASSKEY`:密钥 | + | hdfans | `HDFANS_UID`:用户ID
`HDFANS_PASSKEY`:密钥 | + | wintersakura | `WINTERSAKURA_UID`:用户ID
`WINTERSAKURA_PASSKEY`:密钥 | + | leaves | `LEAVES_UID`:用户ID
`LEAVES_PASSKEY`:密钥 | + | 1ptba | `1PTBA_UID`:用户ID
`1PTBA_PASSKEY`:密钥 | + | icc2022 | `ICC2022_UID`:用户ID
`ICC2022_PASSKEY`:密钥 | + | ptlsp | `PTLSP_UID`:用户ID
`PTLSP_PASSKEY`:密钥 | + | xingtan | `XINGTAN_UID`:用户ID
`XINGTAN_PASSKEY`:密钥 | + | ptvicomo | `PTVICOMO_UID`:用户ID
`PTVICOMO_PASSKEY`:密钥 | + | agsvpt | `AGSVPT_UID`:用户ID
`AGSVPT_PASSKEY`:密钥 | + + +### 2. **app.env配置文件** + +下载 [app.env 模板](https://github.com/jxxghp/MoviePilot/raw/main/config/app.env),修改后放配置文件目录下,app.env 的所有配置项也可以通过环境变量进行配置。 + +- **❗SUPERUSER:** 超级管理员用户名,默认`admin`,安装后使用该用户登录后台管理界面,**注意:启动一次后再次修改该值不会生效,除非删除数据库文件!** +- **❗SUPERUSER_PASSWORD:** 超级管理员初始密码,默认`password`,建议修改为复杂密码,**注意:启动一次后再次修改该值不会生效,除非删除数据库文件!** - **❗API_TOKEN:** API密钥,默认`moviepilot`,在媒体服务器Webhook、微信回调等地址配置中需要加上`?token=`该值,建议修改为复杂字符串 -- **TMDB_API_DOMAIN:** TMDB API地址,默认`api.themoviedb.org`,也可配置为`api.tmdb.org`或其它中转代理服务地址,能连通即可 +- **BIG_MEMORY_MODE:** 大内存模式,默认为`false`,开启后会增加缓存数量,占用更多的内存,但响应速度会更快 +- **GITHUB_TOKEN:** Github token,提高自动更新、插件安装等请求Github Api的限流阈值,格式:ghp_**** +--- +- **TMDB_API_DOMAIN:** TMDB API地址,默认`api.themoviedb.org`,也可配置为`api.tmdb.org`、`tmdb.movie-pilot.org` 或其它中转代理服务地址,能连通即可 - **TMDB_IMAGE_DOMAIN:** TMDB图片地址,默认`image.tmdb.org`,可配置为其它中转代理以加速TMDB图片显示,如:`static-mdb.v.geilijiasu.com` - **WALLPAPER:** 登录首页电影海报,`tmdb`/`bing`,默认`tmdb` - **RECOGNIZE_SOURCE:** 媒体信息识别来源,`themoviedb`/`douban`,默认`themoviedb`,使用`douban`时不支持二级分类 -- **SCRAP_SOURCE:** 刮削元数据及图片使用的数据源,`themoviedb`/`douban`,默认`themoviedb` --- - **SCRAP_METADATA:** 刮削入库的媒体文件,`true`/`false`,默认`true` +- **SCRAP_SOURCE:** 刮削元数据及图片使用的数据源,`themoviedb`/`douban`,默认`themoviedb` - **SCRAP_FOLLOW_TMDB:** 新增已入库媒体是否跟随TMDB信息变化,`true`/`false`,默认`true`,为`false`时即使TMDB信息变化了也会仍然按历史记录中已入库的信息进行刮削 --- -- **❗TRANSFER_TYPE:** 整理转移方式,支持`link`/`copy`/`move`/`softlink`/`rclone_copy`/`rclone_move` **注意:在`link`和`softlink`转移方式下,转移后的文件会继承源文件的权限掩码,不受`UMASK`影响;rclone需要自行映射rclone配置目录到容器中或在容器内完成rclone配置,节点名称必须为:`MP`** -- **❗OVERWRITE_MODE:** 转移覆盖模式,默认为`size`,支持`nerver`/`size`/`always`/`latest`,分别表示`不覆盖同名文件`/`同名文件根据文件大小覆盖(大覆盖小)`/`总是覆盖同名文件`/`仅保留最新版本,删除旧版本文件(包括非同名文件)` - **❗LIBRARY_PATH:** 媒体库目录,多个目录使用`,`分隔 - **LIBRARY_MOVIE_NAME:** 电影媒体库目录名称(不是完整路径),默认`电影` - **LIBRARY_TV_NAME:** 电视剧媒体库目录称(不是完整路径),默认`电视剧` - **LIBRARY_ANIME_NAME:** 动漫媒体库目录称(不是完整路径),默认`电视剧/动漫` - **LIBRARY_CATEGORY:** 媒体库二级分类开关,`true`/`false`,默认`false`,开启后会根据配置 [category.yaml](https://github.com/jxxghp/MoviePilot/raw/main/config/category.yaml) 自动在媒体库目录下建立二级目录分类 +- **❗TRANSFER_TYPE:** 整理转移方式,支持`link`/`copy`/`move`/`softlink`/`rclone_copy`/`rclone_move` **注意:在`link`和`softlink`转移方式下,转移后的文件会继承源文件的权限掩码,不受`UMASK`影响;rclone需要自行映射rclone配置目录到容器中或在容器内完成rclone配置,节点名称必须为:`MP`** +- **OVERWRITE_MODE:** 转移覆盖模式,默认为`size`,支持`nerver`/`size`/`always`/`latest`,分别表示`不覆盖同名文件`/`同名文件根据文件大小覆盖(大覆盖小)`/`总是覆盖同名文件`/`仅保留最新版本,删除旧版本文件(包括非同名文件)` --- - **❗COOKIECLOUD_HOST:** CookieCloud服务器地址,格式:`http(s)://ip:port`,不配置默认使用内建服务器`https://movie-pilot.org/cookiecloud` - **❗COOKIECLOUD_KEY:** CookieCloud用户KEY @@ -105,8 +133,6 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - **AUTO_DOWNLOAD_USER:** 远程交互搜索时自动择优下载的用户ID(消息通知渠道的用户ID),多个用户使用,分割,未设置需要选择资源或者回复`0` --- - **OCR_HOST:** OCR识别服务器地址,格式:`http(s)://ip:port`,用于识别站点验证码实现自动登录获取Cookie等,不配置默认使用内建服务器`https://movie-pilot.org`,可使用 [这个镜像](https://hub.docker.com/r/jxxghp/moviepilot-ocr) 自行搭建。 -- **PLUGIN_MARKET:** 插件市场仓库地址,多个地址使用`,`分隔,保留最后的/,默认为官方插件仓库:`https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/main/`。 -- **GITHUB_TOKEN:** Github token,提高请求api限流阈值 ghp_****(仅支持环境变量配置) --- - **❗MESSAGER:** 消息通知渠道,支持 `telegram`/`wechat`/`slack`/`synologychat`,开启多个渠道时使用`,`分隔。同时还需要配置对应渠道的环境变量,非对应渠道的变量可删除,推荐使用`telegram` @@ -137,7 +163,6 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - **SYNOLOGYCHAT_WEBHOOK:** 在Synology Chat中创建机器人,获取机器人`传入URL` - **SYNOLOGYCHAT_TOKEN:** SynologyChat机器人`令牌` - --- - **❗DOWNLOAD_PATH:** 下载保存目录,**注意:需要将`moviepilot`及`下载器`的映射路径保持一致**,否则会导致下载文件无法转移 - **DOWNLOAD_MOVIE_PATH:** 电影下载保存目录路径,不设置则下载到`DOWNLOAD_PATH` @@ -145,8 +170,7 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - **DOWNLOAD_ANIME_PATH:** 动漫下载保存目录路径,不设置则下载到`DOWNLOAD_PATH` - **DOWNLOAD_CATEGORY:** 下载二级分类开关,`true`/`false`,默认`false`,开启后会根据配置 [category.yaml](https://github.com/jxxghp/MoviePilot/raw/main/config/category.yaml) 自动在下载目录下建立二级目录分类 - **DOWNLOAD_SUBTITLE:** 下载站点字幕,`true`/`false`,默认`true` -- **DOWNLOADER_MONITOR:** 下载器监控,`true`/`false`,默认为`true`,开启后下载完成时才会自动整理入库 -- **TORRENT_TAG:** 下载器种子标签,默认为`MOVIEPILOT`,设置后只有MoviePilot添加的下载才会处理,留空所有下载器中的任务均会处理 +--- - **❗DOWNLOADER:** 下载器,支持`qbittorrent`/`transmission`,QB版本号要求>= 4.3.9,TR版本号要求>= 3.0,同时还需要配置对应渠道的环境变量,非对应渠道的变量可删除,推荐使用`qbittorrent` - `qbittorrent`设置项: @@ -163,7 +187,8 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - **TR_HOST:** transmission地址,格式:`ip:port`,https需要添加`https://`前缀 - **TR_USER:** transmission用户名 - **TR_PASSWORD:** transmission密码 - +- **DOWNLOADER_MONITOR:** 下载器监控,`true`/`false`,默认为`true`,开启后下载完成时才会自动整理入库 +- **TORRENT_TAG:** 下载器种子标签,默认为`MOVIEPILOT`,设置后只有MoviePilot添加的下载才会处理,留空所有下载器中的任务均会处理 --- - **❗MEDIASERVER:** 媒体服务器,支持`emby`/`jellyfin`/`plex`,同时开启多个使用`,`分隔。还需要配置对应媒体服务器的环境变量,非对应媒体服务器的变量可删除,推荐使用`emby` @@ -181,88 +206,55 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - **PLEX_HOST:** Plex服务器地址,格式:`ip:port`,https需要添加`https://`前缀 - **PLEX_TOKEN:** Plex网页Url中的`X-Plex-Token`,通过浏览器F12->网络从请求URL中获取 - - **MEDIASERVER_SYNC_INTERVAL:** 媒体服务器同步间隔(小时),默认`6`,留空则不同步 - **MEDIASERVER_SYNC_BLACKLIST:** 媒体服务器同步黑名单,多个媒体库名称使用,分割 +--- +- **MOVIE_RENAME_FORMAT:** 电影重命名格式,基于jinjia2语法 + `MOVIE_RENAME_FORMAT`支持的配置项: -### 2. **用户认证** + > `title`: 标题 + > `original_name`: 原文件名 + > `original_title`: 原语种标题 + > `name`: 识别名称 + > `year`: 年份 + > `resourceType`:资源类型 + > `effect`:特效 + > `edition`: 版本(资源类型+特效) + > `videoFormat`: 分辨率 + > `releaseGroup`: 制作组/字幕组 + > `customization`: 自定义占位符 + > `videoCodec`: 视频编码 + > `audioCodec`: 音频编码 + > `tmdbid`: TMDBID + > `imdbid`: IMDBID + > `part`:段/节 + > `fileExt`:文件扩展名 + > `tmdbid`:TMDB ID + > `imdbid`:IMDB ID + > `customization`:自定义占位符 + + `MOVIE_RENAME_FORMAT`默认配置格式: + + ``` + {{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if part %}-{{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}} + ``` -`MoviePilot`需要认证后才能使用,配置`AUTH_SITE`后,需要根据下表配置对应站点的认证参数(**仅能通过环境变量配置**) - -`AUTH_SITE`支持配置多个认证站点,使用`,`分隔,如:`iyuu,hhclub`,会依次执行认证操作,直到有一个站点认证成功。 - -- **❗AUTH_SITE:** 认证站点,认证资源`v1.0.2`支持`iyuu`/`hhclub`/`audiences`/`hddolby`/`zmpt`/`freefarm`/`hdfans`/`wintersakura`/`leaves`/`1ptba`/`icc2022`/`ptlsp`/`xingtan`/`ptvicomo`/`agsvpt` - -| 站点 | 参数 | -|:------------:|:-----------------------------------------------------:| -| iyuu | `IYUU_SIGN`:IYUU登录令牌 | -| hhclub | `HHCLUB_USERNAME`:用户名
`HHCLUB_PASSKEY`:密钥 | -| audiences | `AUDIENCES_UID`:用户ID
`AUDIENCES_PASSKEY`:密钥 | -| hddolby | `HDDOLBY_ID`:用户ID
`HDDOLBY_PASSKEY`:密钥 | -| zmpt | `ZMPT_UID`:用户ID
`ZMPT_PASSKEY`:密钥 | -| freefarm | `FREEFARM_UID`:用户ID
`FREEFARM_PASSKEY`:密钥 | -| hdfans | `HDFANS_UID`:用户ID
`HDFANS_PASSKEY`:密钥 | -| wintersakura | `WINTERSAKURA_UID`:用户ID
`WINTERSAKURA_PASSKEY`:密钥 | -| leaves | `LEAVES_UID`:用户ID
`LEAVES_PASSKEY`:密钥 | -| 1ptba | `1PTBA_UID`:用户ID
`1PTBA_PASSKEY`:密钥 | -| icc2022 | `ICC2022_UID`:用户ID
`ICC2022_PASSKEY`:密钥 | -| ptlsp | `PTLSP_UID`:用户ID
`PTLSP_PASSKEY`:密钥 | -| xingtan | `XINGTAN_UID`:用户ID
`XINGTAN_PASSKEY`:密钥 | -| ptvicomo | `PTVICOMO_UID`:用户ID
`PTVICOMO_PASSKEY`:密钥 | -| agsvpt | `AGSVPT_UID`:用户ID
`AGSVPT_PASSKEY`:密钥 | - - -### 2. **进阶配置** - -- **BIG_MEMORY_MODE:** 大内存模式,默认为`false`,开启后会增加缓存数量,占用更多的内存,但响应速度会更快 - -- **MOVIE_RENAME_FORMAT:** 电影重命名格式 - -`MOVIE_RENAME_FORMAT`支持的配置项: - -> `title`: 标题 -> `original_name`: 原文件名 -> `original_title`: 原语种标题 -> `name`: 识别名称 -> `year`: 年份 -> `resourceType`:资源类型 -> `effect`:特效 -> `edition`: 版本(资源类型+特效) -> `videoFormat`: 分辨率 -> `releaseGroup`: 制作组/字幕组 -> `customization`: 自定义占位符 -> `videoCodec`: 视频编码 -> `audioCodec`: 音频编码 -> `tmdbid`: TMDBID -> `imdbid`: IMDBID -> `part`:段/节 -> `fileExt`:文件扩展名 -> `tmdbid`:TMDB ID -> `imdbid`:IMDB ID -> `customization`:自定义占位符 - -`MOVIE_RENAME_FORMAT`默认配置格式: - -``` -{{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if part %}-{{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}} -``` - -- **TV_RENAME_FORMAT:** 电视剧重命名格式 - -`TV_RENAME_FORMAT`额外支持的配置项: - -> `season`: 季号 -> `episode`: 集号 -> `season_episode`: 季集 SxxExx -> `episode_title`: 集标题 - -`TV_RENAME_FORMAT`默认配置格式: - -``` -{{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}} 集{% endif %}{{fileExt}} -``` +- **TV_RENAME_FORMAT:** 电视剧重命名格式,基于jinjia2语法 + `TV_RENAME_FORMAT`额外支持的配置项: + + > `season`: 季号 + > `episode`: 集号 + > `season_episode`: 季集 SxxExx + > `episode_title`: 集标题 + + `TV_RENAME_FORMAT`默认配置格式: + + ``` + {{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}} 集{% endif %}{{fileExt}} + ``` + ### 3. **优先级规则** @@ -270,6 +262,10 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - 符合任一层级规则的资源将被标识选中,匹配成功的层级做为该资源的优先级,排越前面优先级超高 - 不符合过滤规则所有层级规则的资源将不会被选中 +### 4. **插件扩展** + +- **PLUGIN_MARKET:** 插件市场仓库地址,仅支持Github仓库`main`分支,多个地址使用`,`分隔,默认为官方插件仓库:`https://github.com/jxxghp/MoviePilot-Plugins` ,通过查看[MoviePilot-Plugins](https://github.com/jxxghp/MoviePilot-Plugins)项目的fork,或者查看频道置顶了解更多第三方插件仓库。 + ## 使用 diff --git a/app/api/endpoints/download.py b/app/api/endpoints/download.py index 6c97c511..8ce6ace6 100644 --- a/app/api/endpoints/download.py +++ b/app/api/endpoints/download.py @@ -104,7 +104,7 @@ def stop_downloading( hashString: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ - 控制下载任务 + 暂停下载任务 """ ret = DownloadChain().set_downloading(hashString, "stop") return schemas.Response(success=True if ret else False) @@ -115,7 +115,7 @@ def remove_downloading( hashString: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ - 控制下载任务 + 删除下载任务 """ ret = DownloadChain().remove_downloading(hashString) return schemas.Response(success=True if ret else False) diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index 038aca68..6af14230 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -62,7 +62,7 @@ def install_plugin(plugin_id: str, state, msg = PluginHelper().install(pid=plugin_id, repo_url=repo_url) if not state: # 安装失败 - return schemas.Response(success=False, msg=msg) + return schemas.Response(success=False, message=msg) # 安装插件 if plugin_id not in install_plugins: install_plugins.append(plugin_id) @@ -89,11 +89,23 @@ def plugin_form(plugin_id: str, @router.get("/page/{plugin_id}", summary="获取插件数据页面") def plugin_page(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> List[dict]: """ - 根据插件ID获取插件配置信息 + 根据插件ID获取插件数据页面 """ return PluginManager().get_plugin_page(plugin_id) +@router.get("/reset/{plugin_id}", summary="重置插件配置", response_model=schemas.Response) +def reset_plugin(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> List[dict]: + """ + 根据插件ID重置插件配置 + """ + # 删除配置 + PluginManager().delete_plugin_config(plugin_id) + # 重新生效插件 + PluginManager().reload_plugin(plugin_id, {}) + return schemas.Response(success=True) + + @router.get("/{plugin_id}", summary="获取插件配置") def plugin_config(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> dict: """ @@ -106,7 +118,7 @@ def plugin_config(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token def set_plugin_config(plugin_id: str, conf: dict, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ - 根据插件ID获取插件配置信息 + 更新插件配置 """ # 保存配置 PluginManager().save_plugin_config(plugin_id, conf) diff --git a/app/api/endpoints/subscribe.py b/app/api/endpoints/subscribe.py index c4ddd20e..198e1c41 100644 --- a/app/api/endpoints/subscribe.py +++ b/app/api/endpoints/subscribe.py @@ -264,7 +264,7 @@ def delete_subscribe( async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks, authorization: str = Header(None)) -> Any: """ - Jellyseerr/Overseerr订阅 + Jellyseerr/Overseerr网络勾子通知订阅 """ if not authorization or authorization != settings.API_TOKEN: raise HTTPException( diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index c5dedacb..76c65db5 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -1,10 +1,10 @@ import json import time from datetime import datetime -from typing import Union +from typing import Union, Any import tailer -from fastapi import APIRouter, HTTPException, Depends +from fastapi import APIRouter, HTTPException, Depends, Response from fastapi.responses import StreamingResponse from app import schemas @@ -24,6 +24,19 @@ from version import APP_VERSION router = APIRouter() +@router.get("/img/{imgurl:path}", summary="图片代理") +def get_img(imgurl: str) -> Any: + """ + 通过图片代理(使用代理服务器) + """ + if not imgurl: + return None + response = RequestUtils(ua=settings.USER_AGENT, proxies=settings.PROXY).get_res(url=imgurl) + if response: + return Response(content=response.content, media_type="image/jpeg") + return None + + @router.get("/env", summary="查询系统环境变量", response_model=schemas.Response) def get_env_setting(_: schemas.TokenPayload = Depends(verify_token)): """ diff --git a/app/chain/download.py b/app/chain/download.py index a650a82a..ee0ecaee 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -283,6 +283,10 @@ class DownloadChain(ChainBase): if not file_meta.begin_episode \ or file_meta.begin_episode not in episodes: continue + # 只处理视频格式 + if not Path(file).suffix \ + or Path(file).suffix not in settings.RMT_MEDIAEXT: + continue files_to_add.append({ "download_hash": _hash, "downloader": settings.DOWNLOADER, diff --git a/app/chain/message.py b/app/chain/message.py index 0345abde..26315a81 100644 --- a/app/chain/message.py +++ b/app/chain/message.py @@ -1,14 +1,22 @@ -from typing import Any +import copy +import json +import re +from typing import Any, Optional, Dict -from app.chain.download import * +from app.chain import ChainBase +from app.chain.download import DownloadChain from app.chain.media import MediaChain from app.chain.search import SearchChain from app.chain.subscribe import SubscribeChain -from app.core.context import MediaInfo +from app.core.config import settings +from app.core.context import MediaInfo, Context from app.core.event import EventManager +from app.core.meta import MetaBase +from app.helper.torrent import TorrentHelper from app.log import logger from app.schemas import Notification -from app.schemas.types import EventType, MessageChannel +from app.schemas.types import EventType, MessageChannel, MediaType +from app.utils.string import StringUtils # 当前页面 _current_page: int = 0 @@ -33,7 +41,6 @@ class MessageChain(ChainBase): self.subscribechain = SubscribeChain() self.searchchain = SearchChain() self.medtachain = MediaChain() - self.torrent = TorrentHelper() self.eventmanager = EventManager() self.torrenthelper = TorrentHelper() @@ -353,6 +360,13 @@ class MessageChain(ChainBase): else: # 未完成下载 logger.info(f'{_current_media.title_year} 未下载未完整,添加订阅 ...') + if downloads and _current_media.type == MediaType.TV: + # 获取已下载剧集 + downloaded = [download.meta_info.begin_episode for download in downloads + if download.meta_info.begin_episode] + note = json.dumps(downloaded) + else: + note = None # 添加订阅,状态为R self.subscribechain.add(title=_current_media.title, year=_current_media.year, @@ -362,7 +376,8 @@ class MessageChain(ChainBase): channel=channel, userid=userid, username=username, - state="R") + state="R", + note=note) def __post_medias_message(self, channel: MessageChannel, title: str, items: list, userid: str, total: int): diff --git a/app/chain/torrents.py b/app/chain/torrents.py index 3af74851..cfa8df33 100644 --- a/app/chain/torrents.py +++ b/app/chain/torrents.py @@ -12,6 +12,7 @@ from app.db.site_oper import SiteOper from app.db.systemconfig_oper import SystemConfigOper from app.helper.rss import RssHelper from app.helper.sites import SitesHelper +from app.helper.torrent import TorrentHelper from app.log import logger from app.schemas import Notification from app.schemas.types import SystemConfigKey, MessageChannel, NotificationType @@ -34,6 +35,7 @@ class TorrentsChain(ChainBase, metaclass=Singleton): self.rsshelper = RssHelper() self.systemconfig = SystemConfigOper() self.mediachain = MediaChain() + self.torrenthelper = TorrentHelper() def remote_refresh(self, channel: MessageChannel, userid: Union[str, int] = None): """ @@ -143,6 +145,11 @@ class TorrentsChain(ChainBase, metaclass=Singleton): # 读取缓存 torrents_cache = self.get_torrents() + # 缓存过滤掉无效种子 + for _domain, _torrents in torrents_cache.items(): + torrents_cache[_domain] = [_torrent for _torrent in _torrents + if not self.torrenthelper.is_invalid(_torrent.torrent_info.enclosure)] + # 所有站点索引 indexers = self.siteshelper.get_indexers() # 遍历站点缓存资源 diff --git a/app/core/config.py b/app/core/config.py index 7dbbf97e..31519fd2 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,7 +1,7 @@ import secrets import sys from pathlib import Path -from typing import List +from typing import List, Optional from pydantic import BaseSettings @@ -156,7 +156,7 @@ class Settings(BaseSettings): # 媒体服务器 emby/jellyfin/plex,多个媒体服务器,分割 MEDIASERVER: str = "emby" # 媒体服务器同步间隔(小时) - MEDIASERVER_SYNC_INTERVAL: int = 6 + MEDIASERVER_SYNC_INTERVAL: Optional[int] = 6 # 媒体服务器同步黑名单,多个媒体库名称,分割 MEDIASERVER_SYNC_BLACKLIST: str = None # EMBY服务器地址,IP:PORT @@ -180,7 +180,7 @@ class Settings(BaseSettings): # CookieCloud端对端加密密码 COOKIECLOUD_PASSWORD: str = None # CookieCloud同步间隔(分钟) - COOKIECLOUD_INTERVAL: int = 60 * 24 + COOKIECLOUD_INTERVAL: Optional[int] = 60 * 24 # OCR服务器地址 OCR_HOST: str = "https://movie-pilot.org" # CookieCloud对应的浏览器UA @@ -211,7 +211,7 @@ class Settings(BaseSettings): # 大内存模式 BIG_MEMORY_MODE: bool = False # 插件市场仓库地址,多个地址使用,分隔,地址以/结尾 - PLUGIN_MARKET: str = "https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/main/" + PLUGIN_MARKET: str = "https://github.com/jxxghp/MoviePilot-Plugins" # Github token,提高请求api限流阈值 ghp_**** GITHUB_TOKEN: str = None # 自动检查和更新站点资源包(站点索引、认证等) diff --git a/app/core/plugin.py b/app/core/plugin.py index 872ecde3..18476122 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -147,6 +147,14 @@ class PluginManager(metaclass=Singleton): return False return self.systemconfig.set(self._config_key % pid, conf) + def delete_plugin_config(self, pid: str) -> bool: + """ + 删除插件配置 + """ + if not self._plugins.get(pid): + return False + return self.systemconfig.delete(self._config_key % pid) + def get_plugin_form(self, pid: str) -> Tuple[List[dict], Dict[str, Any]]: """ 获取插件表单 @@ -288,9 +296,6 @@ class PluginManager(metaclass=Singleton): # 图标 if plugin.get("icon"): conf.update({"plugin_icon": plugin.get("icon")}) - # 主题色 - if plugin.get("color"): - conf.update({"plugin_color": plugin.get("color")}) # 作者 if plugin.get("author"): conf.update({"plugin_author": plugin.get("author")}) @@ -358,9 +363,6 @@ class PluginManager(metaclass=Singleton): # 图标 if hasattr(plugin, "plugin_icon"): conf.update({"plugin_icon": plugin.plugin_icon}) - # 主题色 - if hasattr(plugin, "plugin_color"): - conf.update({"plugin_color": plugin.plugin_color}) # 作者 if hasattr(plugin, "plugin_author"): conf.update({"plugin_author": plugin.plugin_author}) diff --git a/app/db/downloadhistory_oper.py b/app/db/downloadhistory_oper.py index 3f8fc194..d23fbb27 100644 --- a/app/db/downloadhistory_oper.py +++ b/app/db/downloadhistory_oper.py @@ -57,7 +57,14 @@ class DownloadHistoryOper(DbOper): 按fullpath查询下载文件记录 :param fullpath: 数据key """ - return DownloadFiles.get_by_fullpath(self._db, fullpath) + return DownloadFiles.get_by_fullpath(self._db, fullpath=fullpath, all_files=False) + + def get_files_by_fullpath(self, fullpath: str) -> List[DownloadFiles]: + """ + 按fullpath查询下载文件记录 + :param fullpath: 数据key + """ + return DownloadFiles.get_by_fullpath(self._db, fullpath=fullpath, all_files=True) def get_files_by_savepath(self, fullpath: str) -> List[DownloadFiles]: """ @@ -78,7 +85,7 @@ class DownloadHistoryOper(DbOper): 按fullpath查询下载文件记录hash :param fullpath: 数据key """ - fileinfo: DownloadFiles = DownloadFiles.get_by_fullpath(self._db, fullpath) + fileinfo: DownloadFiles = DownloadFiles.get_by_fullpath(self._db, fullpath=fullpath, all_files=False) if fileinfo: return fileinfo.download_hash return "" @@ -115,3 +122,13 @@ class DownloadHistoryOper(DbOper): return DownloadHistory.list_by_user_date(db=self._db, date=date, username=username) + + def list_by_date(self, date: str, type: str, tmdbid: str, seasons: str = None) -> List[DownloadHistory]: + """ + 查询某时间之后的下载历史 + """ + return DownloadHistory.list_by_date(db=self._db, + date=date, + type=type, + tmdbid=tmdbid, + seasons=seasons) diff --git a/app/db/models/downloadhistory.py b/app/db/models/downloadhistory.py index c3d091d7..d37ab652 100644 --- a/app/db/models/downloadhistory.py +++ b/app/db/models/downloadhistory.py @@ -123,6 +123,24 @@ class DownloadHistory(Base): DownloadHistory.id.desc()).all() return list(result) + @staticmethod + @db_query + def list_by_date(db: Session, date: str, type: str, tmdbid: str, seasons: str = None): + """ + 查询某时间之后的下载历史 + """ + if seasons: + return db.query(DownloadHistory).filter(DownloadHistory.date > date, + DownloadHistory.type == type, + DownloadHistory.tmdbid == tmdbid, + DownloadHistory.seasons == seasons).order_by( + DownloadHistory.id.desc()).all() + else: + return db.query(DownloadHistory).filter(DownloadHistory.date > date, + DownloadHistory.type == type, + DownloadHistory.tmdbid == tmdbid).order_by( + DownloadHistory.id.desc()).all() + class DownloadFiles(Base): """ @@ -157,9 +175,13 @@ class DownloadFiles(Base): @staticmethod @db_query - def get_by_fullpath(db: Session, fullpath: str): - return db.query(DownloadFiles).filter(DownloadFiles.fullpath == fullpath).order_by( - DownloadFiles.id.desc()).first() + def get_by_fullpath(db: Session, fullpath: str, all_files: bool = False): + if not all_files: + return db.query(DownloadFiles).filter(DownloadFiles.fullpath == fullpath).order_by( + DownloadFiles.id.desc()).first() + else: + return db.query(DownloadFiles).filter(DownloadFiles.fullpath == fullpath).order_by( + DownloadFiles.id.desc()).all() @staticmethod @db_query diff --git a/app/db/systemconfig_oper.py b/app/db/systemconfig_oper.py index 40ae04ee..88d3e149 100644 --- a/app/db/systemconfig_oper.py +++ b/app/db/systemconfig_oper.py @@ -56,6 +56,20 @@ class SystemConfigOper(DbOper, metaclass=Singleton): return self.__SYSTEMCONF return self.__SYSTEMCONF.get(key) + def delete(self, key: Union[str, SystemConfigKey]): + """ + 删除系统设置 + """ + if isinstance(key, SystemConfigKey): + key = key.value + # 更新内存 + self.__SYSTEMCONF.pop(key, None) + # 写入数据库 + conf = SystemConfig.get_by_key(self._db, key) + if conf: + conf.delete(self._db, conf.id) + return True + def __del__(self): if self._db: self._db.close() diff --git a/app/helper/plugin.py b/app/helper/plugin.py index 9f867ba5..f22dd1f6 100644 --- a/app/helper/plugin.py +++ b/app/helper/plugin.py @@ -16,6 +16,8 @@ class PluginHelper(metaclass=Singleton): 插件市场管理,下载安装插件到本地 """ + _base_url = "https://raw.githubusercontent.com/%s/%s/main/" + @cached(cache=TTLCache(maxsize=10, ttl=1800)) def get_plugins(self, repo_url: str) -> Dict[str, dict]: """ @@ -24,27 +26,47 @@ class PluginHelper(metaclass=Singleton): """ if not repo_url: return {} + user, repo = self.get_repo_info(repo_url) + if not user or not repo: + return {} + raw_url = self._base_url % (user, repo) res = RequestUtils(proxies=settings.PROXY, headers=settings.GITHUB_HEADERS, - timeout=10).get_res(f"{repo_url}package.json") + timeout=10).get_res(f"{raw_url}package.json") if res: return json.loads(res.text) return {} @staticmethod - def install(pid: str, repo_url: str) -> Tuple[bool, str]: + def get_repo_info(repo_url: str) -> Tuple[Optional[str], Optional[str]]: """ - 安装插件 + 获取Github仓库信息 + :param repo_url: Github仓库地址 """ - # 从Github的repo_url获取用户和项目名 + if not repo_url: + return None, None + if not repo_url.endswith("/"): + repo_url += "/" + if repo_url.count("/") < 6: + repo_url = f"{repo_url}main/" try: user, repo = repo_url.split("/")[-4:-2] except Exception as e: - return False, f"不支持的插件仓库地址格式:{str(e)}" - if not user or not repo: - return False, "不支持的插件仓库地址格式" + print(str(e)) + return None, None + return user, repo + + def install(self, pid: str, repo_url: str) -> Tuple[bool, str]: + """ + 安装插件 + """ if SystemUtils.is_frozen(): return False, "可执行文件模式下,只能安装本地插件" + # 从Github的repo_url获取用户和项目名 + user, repo = self.get_repo_info(repo_url) + if not user or not repo: + return False, "不支持的插件仓库地址格式" + def __get_filelist(_p: str) -> Tuple[Optional[list], Optional[str]]: """ 获取插件的文件列表 diff --git a/app/helper/torrent.py b/app/helper/torrent.py index c7aa8cc6..5970f782 100644 --- a/app/helper/torrent.py +++ b/app/helper/torrent.py @@ -14,13 +14,17 @@ from app.db.systemconfig_oper import SystemConfigOper from app.log import logger from app.utils.http import RequestUtils from app.schemas.types import MediaType, SystemConfigKey +from app.utils.singleton import Singleton -class TorrentHelper: +class TorrentHelper(metaclass=Singleton): """ 种子帮助类 """ + # 失败的种子:站点链接 + _invalid_torrents = [] + def __init__(self): self.system_config = SystemConfigOper() @@ -123,6 +127,8 @@ class TorrentHelper: elif req.status_code == 429: return None, None, "", [], "触发站点流控,请稍后重试" else: + # 把错误的种子记下来,避免重复使用 + self.add_invalid(url) return None, None, "", [], f"下载种子出错,状态码:{req.status_code}" @staticmethod @@ -276,3 +282,16 @@ class TorrentHelper: continue episodes = list(set(episodes).union(set(meta.episode_list))) return episodes + + def is_invalid(self, url: str) -> bool: + """ + 判断种子是否是无效种子 + """ + return url in self._invalid_torrents + + def add_invalid(self, url: str): + """ + 添加无效种子 + """ + if url not in self._invalid_torrents: + self._invalid_torrents.append(url) diff --git a/app/main.py b/app/main.py index 133402cd..f33c5257 100644 --- a/app/main.py +++ b/app/main.py @@ -101,6 +101,9 @@ def start_tray(): if not SystemUtils.is_frozen(): return + if not SystemUtils.is_windows(): + return + def open_web(): """ 调用浏览器打开前端页面 diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index a867b16a..92f1562c 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -366,7 +366,7 @@ class Emby(metaclass=Singleton): season_episodes[season_index] = [] season_episodes[season_index].append(episode_index) # 返回 - return tv_item.get("Id"), season_episodes + return item_id, season_episodes except Exception as e: logger.error(f"连接Shows/Id/Episodes出错:" + str(e)) return None, None diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index 835c95fa..8d6b2b0f 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -332,7 +332,7 @@ class Jellyfin(metaclass=Singleton): if not season_episodes.get(season_index): season_episodes[season_index] = [] season_episodes[season_index].append(episode_index) - return tv_info.get('Id'), season_episodes + return item_id, season_episodes except Exception as e: logger.error(f"连接Shows/Id/Episodes出错:" + str(e)) return None, None diff --git a/app/schemas/plugin.py b/app/schemas/plugin.py index 88d3e047..abe71a03 100644 --- a/app/schemas/plugin.py +++ b/app/schemas/plugin.py @@ -14,8 +14,6 @@ class Plugin(BaseModel): plugin_desc: Optional[str] = None # 插件图标 plugin_icon: Optional[str] = None - # 主题色 - plugin_color: Optional[str] = None # 插件版本 plugin_version: Optional[str] = None # 插件作者 diff --git a/app/schemas/types.py b/app/schemas/types.py index 8acb5a5c..02d866f7 100644 --- a/app/schemas/types.py +++ b/app/schemas/types.py @@ -16,16 +16,12 @@ class TorrentStatus(Enum): class EventType(Enum): # 插件重载 PluginReload = "plugin.reload" + # 插件动作 + PluginAction = "plugin.action" # 执行命令 CommandExcute = "command.excute" - # 站点签到 - SiteSignin = "site.signin" - # 站点数据统计 - SiteStatistic = "site.statistic" # 站点删除 SiteDeleted = "site.deleted" - # 豆瓣想看 - DoubanSync = "douban.sync" # Webhook消息 WebhookMessage = "webhook.message" # 转移完成 @@ -44,14 +40,6 @@ class EventType(Enum): NameRecognize = "name.recognize" # 名称识别结果 NameRecognizeResult = "name.recognize.result" - # 目录监控同步 - DirectorySync = "directory.sync" - # Cloudflare IP优选 - CloudFlareSpeedTest = "cloudflare.speedtest" - # 站点自动登录更新Cookie、Ua - SiteLogin = "site.login" - # 网盘删除 - NetworkDiskDel = "networkdisk.del" # 系统配置Key字典 diff --git a/config/app.env b/config/app.env index 0aa6accf..153ae9e6 100644 --- a/config/app.env +++ b/config/app.env @@ -3,9 +3,9 @@ ####################################################################### #################################### -# 基础设置 # +# 系统设置 # #################################### -# 【*】API监听地址 +# 【*】API监听地址(注意不是前端访问地址) HOST=0.0.0.0 # 是否调试模式,打开后将输出更多日志 DEBUG=false @@ -15,6 +15,94 @@ DEV=false SUPERUSER=admin # 【*】超级管理员初始密码,设置后一但重启将固化到数据库中,修改将无效 SUPERUSER_PASSWORD=password +# 大内存模式,开启后会增加缓存数量,但会占用更多内存 +BIG_MEMORY_MODE=false +# 自动检查和更新站点资源包(索引、认证等) +AUTO_UPDATE_RESOURCE=true + +#################################### +# 消息通知渠道(按需配置) # +#################################### +# WeChat企业ID +WECHAT_CORPID= +# WeChat应用Secret +WECHAT_APP_SECRET= +# WeChat应用ID +WECHAT_APP_ID= +# WeChat代理服务器,无需代理需保留默认值 +WECHAT_PROXY=https://qyapi.weixin.qq.com +# WeChat Token +WECHAT_TOKEN= +# WeChat EncodingAESKey +WECHAT_ENCODING_AESKEY= +# WeChat 管理员 +WECHAT_ADMINS= + +# Telegram Bot Token +TELEGRAM_TOKEN= +# Telegram Chat ID +TELEGRAM_CHAT_ID= +# Telegram 用户ID,使用,分隔 +TELEGRAM_USERS= +# Telegram 管理员ID,使用,分隔 +TELEGRAM_ADMINS= + +# Slack Bot User OAuth Token +SLACK_OAUTH_TOKEN= +# Slack App-Level Token +SLACK_APP_TOKEN= +# Slack 频道名称 +SLACK_CHANNEL= + +# SynologyChat Webhook +SYNOLOGYCHAT_WEBHOOK= +# SynologyChat Token +SYNOLOGYCHAT_TOKEN= + +#################################### +# 下载器(按需配置) # +#################################### +# Qbittorrent地址,IP:PORT +QB_HOST= +# Qbittorrent用户名 +QB_USER= +# Qbittorrent密码 +QB_PASSWORD= +# Qbittorrent分类自动管理 +QB_CATEGORY=false +# Qbittorrent按顺序下载 +QB_SEQUENTIAL=true +# Qbittorrent忽略队列限制,强制继续 +QB_FORCE_RESUME=true + +# Transmission地址,IP:PORT +TR_HOST= +# Transmission用户名 +TR_USER= +# Transmission密码 +TR_PASSWORD= + +#################################### +# 媒体服务器(按需配置) # +#################################### +# EMBY服务器地址,IP:PORT +EMBY_HOST= +# EMBY Api Key +EMBY_API_KEY= + +# Jellyfin服务器地址,IP:PORT +JELLYFIN_HOST= +# Jellyfin Api Key +JELLYFIN_API_KEY= + +# Plex服务器地址,IP:PORT +PLEX_HOST= +# Plex Token +PLEX_TOKEN= + +#################################### +# 基础设置 # +#################################### # 【*】API密钥,建议更换复杂字符串,有Jellyseerr/Overseerr、媒体服务器Webhook等配置以及部分支持API_TOKEN的API中使用 API_TOKEN=moviepilot # 登录页面电影海报,tmdb/bing,tmdb要求能正常连接api.themoviedb.org @@ -25,10 +113,21 @@ TMDB_IMAGE_DOMAIN=image.tmdb.org TMDB_API_DOMAIN=api.themoviedb.org # 媒体识别来源 themoviedb/douban,使用themoviedb时需要确保能正常连接api.themoviedb.org,使用douban时不支持二级分类 RECOGNIZE_SOURCE=themoviedb -# 大内存模式,开启后会增加缓存数量,但会占用更多内存 -BIG_MEMORY_MODE=false -# 自动检查和更新站点资源包(索引、认证等) -AUTO_UPDATE_RESOURCE=true + +# 【*】消息通知渠道 telegram/wechat/slack,多个通知渠道用,分隔,需要在上面配置对应消息通知渠道的参数 +MESSAGER=telegram + +# 【*】下载器 qbittorrent/transmission,仅支持单个下载器,做为主下载器使用,需要在上面配置对应消下载器的参数 +DOWNLOADER=qbittorrent +# 下载器监控开关 +DOWNLOADER_MONITOR=true + +# 【*】媒体服务器 emby/jellyfin/plex,多个媒体服务器,分割 +MEDIASERVER=emby +# 媒体服务器同步间隔(小时) +MEDIASERVER_SYNC_INTERVAL=6 +# 媒体服务器同步黑名单,多个媒体库名称,分割 +MEDIASERVER_SYNC_BLACKLIST= #################################### # 媒体识别&刮削 # @@ -41,22 +140,24 @@ SCRAP_FOLLOW_TMDB=true SCRAP_SOURCE=themoviedb #################################### -# 媒体库 # +# 文件整理 & 媒体库 # #################################### # 【*】转移方式 link/copy/move/softlink/rclone_copy/rclone_move TRANSFER_TYPE=copy # 转移覆盖模式,`nerver`/`size`/`always`/`latest`,分别表示`不覆盖同名文件`/`同名文件根据文件大小覆盖(大覆盖小)`/`总是覆盖同名文件`/`仅保留最新版本,删除旧版本文件(包括非同名文件)` OVERWRITE_MODE=size + # 【*】媒体库目录,多个目录使用,分隔 -LIBRARY_PATH= +LIBRARY_PATH=/media # 电影媒体库目录名,默认电影 -LIBRARY_MOVIE_NAME= +LIBRARY_MOVIE_NAME=电影 # 电视剧媒体库目录名,默认电视剧 -LIBRARY_TV_NAME= +LIBRARY_TV_NAME=电视剧 # 动漫媒体库目录名,默认电视剧/动漫 -LIBRARY_ANIME_NAME= +LIBRARY_ANIME_NAME=电视剧/动漫 # 二级分类,开启后会根据配置 [category.yaml](https://github.com/jxxghp/MoviePilot/raw/main/config/category.yaml) 自动在媒体库目录下建立二级目录分类 LIBRARY_CATEGORY=true + # 电影重命名格式,Jinja2语法,参考:https://jinja.palletsprojects.com/en/3.0.x/templates/ MOVIE_RENAME_FORMAT={{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if part %}-{{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}} # 电视剧重命名格式,Jinja2语法,参考:https://jinja.palletsprojects.com/en/3.0.x/templates/ @@ -88,71 +189,9 @@ SUBSCRIBE_SEARCH=false # 交互搜索自动下载用户ID(消息通知渠道的用户ID),使用,分割,未设置需要用户手动选择资源或者回复`0` AUTO_DOWNLOAD_USER= -#################################### -# 消息通知 # -#################################### -# 【*】消息通知渠道 telegram/wechat/slack,多个通知渠道用,分隔 -MESSAGER=telegram -# WeChat企业ID -WECHAT_CORPID= -# WeChat应用Secret -WECHAT_APP_SECRET= -# WeChat应用ID -WECHAT_APP_ID= -# WeChat代理服务器,无需代理需保留默认值 -WECHAT_PROXY=https://qyapi.weixin.qq.com -# WeChat Token -WECHAT_TOKEN= -# WeChat EncodingAESKey -WECHAT_ENCODING_AESKEY= -# WeChat 管理员 -WECHAT_ADMINS= -# Telegram Bot Token -TELEGRAM_TOKEN= -# Telegram Chat ID -TELEGRAM_CHAT_ID= -# Telegram 用户ID,使用,分隔 -TELEGRAM_USERS= -# Telegram 管理员ID,使用,分隔 -TELEGRAM_ADMINS= -# Slack Bot User OAuth Token -SLACK_OAUTH_TOKEN= -# Slack App-Level Token -SLACK_APP_TOKEN= -# Slack 频道名称 -SLACK_CHANNEL= -# SynologyChat Webhook -SYNOLOGYCHAT_WEBHOOK= -# SynologyChat Token -SYNOLOGYCHAT_TOKEN= - #################################### # 下载 # #################################### -# 【*】下载器 qbittorrent/transmission,仅支持单个下载器,做为主下载器使用,非主下载器只要配置了参数仍可在插件等使用 -DOWNLOADER=qbittorrent -# 下载器监控开关 -DOWNLOADER_MONITOR=true -# Qbittorrent地址,IP:PORT -QB_HOST= -# Qbittorrent用户名 -QB_USER= -# Qbittorrent密码 -QB_PASSWORD= -# Qbittorrent分类自动管理 -QB_CATEGORY=false -# Qbittorrent按顺序下载 -QB_SEQUENTIAL=true -# Qbittorrent忽略队列限制,强制继续 -QB_FORCE_RESUME=false -# Transmission地址,IP:PORT -TR_HOST= -# Transmission用户名 -TR_USER= -# Transmission密码 -TR_PASSWORD= -# 种子标签 -TORRENT_TAG=MOVIEPILOT # 【*】下载保存目录,容器内映射路径需要一致,支持不同类型设置不同的下载目录(跨盘) DOWNLOAD_PATH=/downloads # 电影下载保存目录(路径),容器内映射路径需要一致 @@ -161,38 +200,19 @@ DOWNLOAD_MOVIE_PATH= DOWNLOAD_TV_PATH= # 动漫下载保存目录(路径),容器内映射路径需要一致 DOWNLOAD_ANIME_PATH= + # 下载目录二级分类,开启后会根据配置 [category.yaml](https://github.com/jxxghp/MoviePilot/raw/main/config/category.yaml) 自动在下载目录下建立二级目录分类 DOWNLOAD_CATEGORY=false -# 下载站点字幕 +# 种子标签 +TORRENT_TAG=MOVIEPILOT +# 自动下载站点字幕(如有) DOWNLOAD_SUBTITLE=true #################################### -# 媒体服务器 # -#################################### -# 【*】媒体服务器 emby/jellyfin/plex,多个媒体服务器,分割 -MEDIASERVER=emby -# 媒体服务器同步间隔(小时) -MEDIASERVER_SYNC_INTERVAL=6 -# 媒体服务器同步黑名单,多个媒体库名称,分割 -MEDIASERVER_SYNC_BLACKLIST= -# EMBY服务器地址,IP:PORT -EMBY_HOST= -# EMBY Api Key -EMBY_API_KEY= -# Jellyfin服务器地址,IP:PORT -JELLYFIN_HOST= -# Jellyfin Api Key -JELLYFIN_API_KEY= -# Plex服务器地址,IP:PORT -PLEX_HOST= -# Plex Token -PLEX_TOKEN= - -#################################### -# 第三方服务 # +# 扩展 # #################################### # OCR服务器地址 OCR_HOST=https://movie-pilot.org # 插件市场仓库地址,多个地址使用`,`分隔,保留最后的/ -PLUGIN_MARKET=https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/main/ +PLUGIN_MARKET=https://github.com/jxxghp/MoviePilot-Plugins diff --git a/version.py b/version.py index 7e9b2f74..26944e77 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -APP_VERSION = 'v1.4.4' +APP_VERSION = 'v1.4.7'