parent
bdcbb168a0
commit
49a82d7a48
33
README.md
33
README.md
@ -185,13 +185,25 @@ MoviePilot需要配套下载器和媒体服务器配合使用。
|
|||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
- 通过设置的超级管理员用户登录管理界面(默认用户:admin,默认端口:3000),**注意:初始密码为自动生成,需要在首次运行时的后台日志中查看!**
|
### 1. **WEB后台管理**
|
||||||
- 通过CookieCloud同步快速添加站点,不需要使用的站点可在WEB管理界面中禁用或删除,无法同步的站点可手动新增。
|
- 通过设置的超级管理员用户登录后台管理界面(`SUPERUSER`配置项,默认用户:admin,默认端口:3000)
|
||||||
- 通过打开下载器监控实现下载完成后自动整理入库并刮削媒体信息。
|
> ❗**注意:超级管理员用户初始密码为自动生成,需要在首次运行时的后台日志中查看!** 如首次运行日志丢失,则需要删除配置文件目录下的`user.db`文件,然后重启服务。
|
||||||
- 通过`微信`/`Telegram`/`Slack`/`SynologyChat`/`VoceChat`远程管理,其中 微信/Telegram 将会自动添加操作菜单(微信菜单条数有限制,部分菜单不显示);微信回调地址、SynologyChat传入地址地址相对路径均为:`/api/v1/message/`;VoceChat的Webhook地址相对路径为:`/api/v1/message/?token=moviepilot`,其中moviepilot为设置的`API_TOKEN`。
|
### 2. **站点维护**
|
||||||
- 设置媒体服务器Webhook,通过MoviePilot发送播放通知等。Webhook回调相对路径为`/api/v1/webhook?token=moviepilot`,其中`moviepilot`为设置的`API_TOKEN`。
|
- 通过CookieCloud同步快速添加站点,不需要使用的站点可在WEB管理界面中禁用或删除,无法同步的站点也可手动新增。
|
||||||
- 将MoviePilot做为Radarr或Sonarr服务器添加到Overseerr或Jellyseerr,可使用Overseerr/Jellyseerr浏览订阅。
|
- 需要通过环境变量设置用户认证信息且认证成功后才能使用站点相关功能,未认证通过时站点相关的插件也会无法显示。
|
||||||
- 映射宿主机docker.sock文件到容器`/var/run/docker.sock`,以支持内建重启操作。实例:`-v /var/run/docker.sock:/var/run/docker.sock:ro`。
|
### 3. **文件整理**
|
||||||
|
- 默认通过监控下载器实现下载完成后自动整理入库并刮削媒体信息,需要后台打开`下载器监控`开关,且仅会处理通过MoviePilot添加下载的任务。
|
||||||
|
- 使用`目录监控`等插件实现更灵活的自动整理。
|
||||||
|
### 4. **通知交互**
|
||||||
|
- 支持通过`微信`/`Telegram`/`Slack`/`SynologyChat`/`VoceChat`等渠道远程管理和订阅下载,其中 微信/Telegram 将会自动添加操作菜单(微信菜单条数有限制,部分菜单不显示)。
|
||||||
|
- `微信`回调地址、`SynologyChat`传入地址地址相对路径均为:`/api/v1/message/`;`VoceChat`的Webhook地址相对路径为:`/api/v1/message/?token=moviepilot`,其中moviepilot为设置的`API_TOKEN`。
|
||||||
|
### 5. **订阅与搜索**
|
||||||
|
- 通过MoviePilot管理后台搜索和订阅。
|
||||||
|
- 将MoviePilot做为`Radarr`或`Sonarr`服务器添加到`Overseerr`或`Jellyseerr`,可使用`Overseerr/Jellyseerr`浏览和添加订阅。
|
||||||
|
- 安装`豆瓣榜单订阅`、`猫眼订阅`等插件,实现自动订阅豆瓣榜单、猫眼榜单等。
|
||||||
|
### 6. **其他**
|
||||||
|
- 通过设置媒体服务器Webhook指向MoviePilot(相对路径为`/api/v1/webhook?token=moviepilot`,其中`moviepilot`为设置的`API_TOKEN`),可实现通过MoviePilot发送播放通知,以及配合各类插件实现播放限速等功能。
|
||||||
|
- 映射宿主机`docker.sock`文件到容器`/var/run/docker.sock`,可支持应用内建重启操作。实例:`-v /var/run/docker.sock:/var/run/docker.sock:ro`。
|
||||||
- 将WEB页面添加到手机桌面图标可获得与App一样的使用体验。
|
- 将WEB页面添加到手机桌面图标可获得与App一样的使用体验。
|
||||||
|
|
||||||
### **注意**
|
### **注意**
|
||||||
@ -219,6 +231,13 @@ location /cgi-bin/menu/create {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- 部分插件功能基于文件系统监控实现(如`目录监控`等),需在宿主机上(不是docker容器内)执行以下命令并重启:
|
||||||
|
```shell
|
||||||
|
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
|
||||||
|
echo fs.inotify.max_user_instances=524288 | sudo tee -a /etc/sysctl.conf
|
||||||
|
sudo sysctl -p
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
@ -212,7 +212,7 @@ class DownloadChain(ChainBase):
|
|||||||
if _media.genre_ids \
|
if _media.genre_ids \
|
||||||
and set(_media.genre_ids).intersection(set(settings.ANIME_GENREIDS)):
|
and set(_media.genre_ids).intersection(set(settings.ANIME_GENREIDS)):
|
||||||
# 动漫
|
# 动漫
|
||||||
download_dir = settings.SAVE_ANIME_PATH
|
download_dir = settings.SAVE_ANIME_PATH / _media.category
|
||||||
else:
|
else:
|
||||||
# 电视剧
|
# 电视剧
|
||||||
download_dir = settings.SAVE_TV_PATH / _media.category
|
download_dir = settings.SAVE_TV_PATH / _media.category
|
||||||
|
@ -69,6 +69,8 @@ class Settings(BaseSettings):
|
|||||||
'.tp']
|
'.tp']
|
||||||
# 支持的字幕文件后缀格式
|
# 支持的字幕文件后缀格式
|
||||||
RMT_SUBEXT: list = ['.srt', '.ass', '.ssa', '.sup']
|
RMT_SUBEXT: list = ['.srt', '.ass', '.ssa', '.sup']
|
||||||
|
# 下载器临时文件后缀
|
||||||
|
DOWNLOAD_TMPEXT: list = ['.!qB', '.part']
|
||||||
# 支持的音轨文件后缀格式
|
# 支持的音轨文件后缀格式
|
||||||
RMT_AUDIO_TRACK_EXT: list = ['.mka']
|
RMT_AUDIO_TRACK_EXT: list = ['.mka']
|
||||||
# 索引器
|
# 索引器
|
||||||
|
@ -324,24 +324,26 @@ class TorrentHelper(metaclass=Singleton):
|
|||||||
|
|
||||||
if not filter_rule:
|
if not filter_rule:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# 匹配内容
|
||||||
|
content = f"{torrent_info.title} {torrent_info.description} {' '.join(torrent_info.labels or [])}"
|
||||||
|
|
||||||
# 最少做种人数
|
# 最少做种人数
|
||||||
min_seeders = filter_rule.get("min_seeders")
|
min_seeders = filter_rule.get("min_seeders")
|
||||||
if min_seeders and torrent_info.seeders < int(min_seeders):
|
if min_seeders and torrent_info.seeders < int(min_seeders):
|
||||||
logger.info(f"{torrent_info.title} 做种人数不足 {min_seeders}")
|
logger.info(f"{torrent_info.title} 做种人数不足 {min_seeders}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 包含
|
# 包含
|
||||||
include = filter_rule.get("include")
|
include = filter_rule.get("include")
|
||||||
if include:
|
if include:
|
||||||
if not re.search(r"%s" % include,
|
if not re.search(r"%s" % include, content, re.I):
|
||||||
f"{torrent_info.title} {torrent_info.description}", re.I):
|
|
||||||
logger.info(f"{torrent_info.title} 不匹配包含规则 {include}")
|
logger.info(f"{torrent_info.title} 不匹配包含规则 {include}")
|
||||||
return False
|
return False
|
||||||
# 排除
|
# 排除
|
||||||
exclude = filter_rule.get("exclude")
|
exclude = filter_rule.get("exclude")
|
||||||
if exclude:
|
if exclude:
|
||||||
if re.search(r"%s" % exclude,
|
if re.search(r"%s" % exclude, content, re.I):
|
||||||
f"{torrent_info.title} {torrent_info.description}", re.I):
|
|
||||||
logger.info(f"{torrent_info.title} 匹配排除规则 {exclude}")
|
logger.info(f"{torrent_info.title} 匹配排除规则 {exclude}")
|
||||||
return False
|
return False
|
||||||
# 质量
|
# 质量
|
||||||
|
@ -39,12 +39,19 @@ class FilterModule(_ModuleBase):
|
|||||||
# 中字
|
# 中字
|
||||||
"CNSUB": {
|
"CNSUB": {
|
||||||
"include": [
|
"include": [
|
||||||
r'[中国國繁简](/|\s|\\|\|)?[繁简英粤]|[英简繁](/|\s|\\|\|)?[中繁简]|繁體|简体|[中国國][字配]|国语|國語|中文|中字|简日|繁日|简繁|繁体|([\s,.-\[])(CHT|CHS|cht|chs)(|[\s,.-\]])'],
|
r'[中国國繁简](/|\s|\\|\|)?[繁简英粤]|[英简繁](/|\s|\\|\|)?[中繁简]'
|
||||||
|
r'|繁體|简体|[中国國][字配]|国语|國語|中文|中字|简日|繁日|简繁|繁体'
|
||||||
|
r'|([\s,.-\[])(CHT|CHS|cht|chs)(|[\s,.-\]])'],
|
||||||
"exclude": [],
|
"exclude": [],
|
||||||
"tmdb": {
|
"tmdb": {
|
||||||
"original_language": "zh,cn"
|
"original_language": "zh,cn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
# 官种
|
||||||
|
"GZ": {
|
||||||
|
"include": [r'官方', r'官种'],
|
||||||
|
"match": ["labels"]
|
||||||
|
},
|
||||||
# 特效字幕
|
# 特效字幕
|
||||||
"SPECSUB": {
|
"SPECSUB": {
|
||||||
"include": [r'特效'],
|
"include": [r'特效'],
|
||||||
@ -256,14 +263,30 @@ class FilterModule(_ModuleBase):
|
|||||||
# 符合TMDB规则的直接返回True,即不过滤
|
# 符合TMDB规则的直接返回True,即不过滤
|
||||||
if tmdb and self.__match_tmdb(tmdb):
|
if tmdb and self.__match_tmdb(tmdb):
|
||||||
return True
|
return True
|
||||||
|
# 匹配项:标题、副标题、标签
|
||||||
|
content = f"{torrent.title} {torrent.description} {' '.join(torrent.labels or [])}"
|
||||||
|
# 只匹配指定关键字
|
||||||
|
match_content = []
|
||||||
|
matchs = self.rule_set[rule_name].get("match") or []
|
||||||
|
if matchs:
|
||||||
|
for match in matchs:
|
||||||
|
if not hasattr(torrent, match):
|
||||||
|
continue
|
||||||
|
match_value = getattr(torrent, match)
|
||||||
|
if not match_value:
|
||||||
|
continue
|
||||||
|
if isinstance(match_value, list):
|
||||||
|
match_content.extend(match_value)
|
||||||
|
else:
|
||||||
|
match_content.append(match_value)
|
||||||
|
if match_content:
|
||||||
|
content = " ".join(match_content)
|
||||||
# 包含规则项
|
# 包含规则项
|
||||||
includes = self.rule_set[rule_name].get("include") or []
|
includes = self.rule_set[rule_name].get("include") or []
|
||||||
# 排除规则项
|
# 排除规则项
|
||||||
excludes = self.rule_set[rule_name].get("exclude") or []
|
excludes = self.rule_set[rule_name].get("exclude") or []
|
||||||
# FREE规则
|
# FREE规则
|
||||||
downloadvolumefactor = self.rule_set[rule_name].get("downloadvolumefactor")
|
downloadvolumefactor = self.rule_set[rule_name].get("downloadvolumefactor")
|
||||||
# 匹配项
|
|
||||||
content = f"{torrent.title} {torrent.description} {' '.join(torrent.labels or [])}"
|
|
||||||
for include in includes:
|
for include in includes:
|
||||||
if not re.search(r"%s" % include, content, re.IGNORECASE):
|
if not re.search(r"%s" % include, content, re.IGNORECASE):
|
||||||
# 未发现包含项
|
# 未发现包含项
|
||||||
|
@ -15,6 +15,7 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
_categorys = {}
|
_categorys = {}
|
||||||
_movie_categorys = {}
|
_movie_categorys = {}
|
||||||
_tv_categorys = {}
|
_tv_categorys = {}
|
||||||
|
_anime_categorys = {}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._category_path: Path = settings.CONFIG_PATH / "category.yaml"
|
self._category_path: Path = settings.CONFIG_PATH / "category.yaml"
|
||||||
@ -43,6 +44,7 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
if self._categorys:
|
if self._categorys:
|
||||||
self._movie_categorys = self._categorys.get('movie')
|
self._movie_categorys = self._categorys.get('movie')
|
||||||
self._tv_categorys = self._categorys.get('tv')
|
self._tv_categorys = self._categorys.get('tv')
|
||||||
|
self._anime_categorys = self._categorys.get('anime')
|
||||||
logger.info(f"已加载二级分类策略 category.yaml")
|
logger.info(f"已加载二级分类策略 category.yaml")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -81,6 +83,15 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
return []
|
return []
|
||||||
return self._tv_categorys.keys()
|
return self._tv_categorys.keys()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def anime_categorys(self) -> list:
|
||||||
|
"""
|
||||||
|
获取动漫分类清单
|
||||||
|
"""
|
||||||
|
if not self._anime_categorys:
|
||||||
|
return []
|
||||||
|
return self._anime_categorys.keys()
|
||||||
|
|
||||||
def get_movie_category(self, tmdb_info) -> str:
|
def get_movie_category(self, tmdb_info) -> str:
|
||||||
"""
|
"""
|
||||||
判断电影的分类
|
判断电影的分类
|
||||||
@ -91,10 +102,14 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
|
|
||||||
def get_tv_category(self, tmdb_info) -> str:
|
def get_tv_category(self, tmdb_info) -> str:
|
||||||
"""
|
"""
|
||||||
判断电视剧的分类
|
判断电视剧的分类,包括动漫
|
||||||
:param tmdb_info: 识别的TMDB中的信息
|
:param tmdb_info: 识别的TMDB中的信息
|
||||||
:return: 二级分类的名称
|
:return: 二级分类的名称
|
||||||
"""
|
"""
|
||||||
|
genre_ids = tmdb_info.get("genre_ids") or []
|
||||||
|
if genre_ids \
|
||||||
|
and set(genre_ids).intersection(set(settings.ANIME_GENREIDS)):
|
||||||
|
return self.get_category(self._anime_categorys, tmdb_info)
|
||||||
return self.get_category(self._tv_categorys, tmdb_info)
|
return self.get_category(self._tv_categorys, tmdb_info)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -47,6 +47,15 @@ tv:
|
|||||||
# 未匹配以上分类,则命名为未分类
|
# 未匹配以上分类,则命名为未分类
|
||||||
未分类:
|
未分类:
|
||||||
|
|
||||||
|
# 配置动漫的分类策略
|
||||||
|
anime:
|
||||||
|
国漫:
|
||||||
|
# 匹配 origin_country 国家,CN是中国大陆,TW是中国台湾,HK是中国香港
|
||||||
|
origin_country: 'CN,TW,HK'
|
||||||
|
日番:
|
||||||
|
# 匹配 origin_country 国家,JP是日本
|
||||||
|
origin_country: 'JP'
|
||||||
|
未分类:
|
||||||
|
|
||||||
## genre_ids 内容类型 字典,注意部分中英文是不一样的
|
## genre_ids 内容类型 字典,注意部分中英文是不一样的
|
||||||
# 28 Action
|
# 28 Action
|
||||||
|
Loading…
x
Reference in New Issue
Block a user