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'