mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-14 07:26:50 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2db628a2ba | ||
|
|
b6c40436c9 | ||
|
|
a8a70cac08 | ||
|
|
3eefbf97b1 | ||
|
|
3c423e0838 | ||
|
|
99cde43954 | ||
|
|
fa3a787bf7 | ||
|
|
c776dc8036 | ||
|
|
1ef068351d | ||
|
|
6abe0a1862 | ||
|
|
ff13045f52 | ||
|
|
59c09681cb | ||
|
|
f664cf6fa5 | ||
|
|
01a847a9c2 | ||
|
|
6da655f67f | ||
|
|
21df7dced1 | ||
|
|
7fc257ea79 | ||
|
|
24f170ff72 | ||
|
|
39999c9ee4 | ||
|
|
27a5188e4e | ||
|
|
a5af0786aa | ||
|
|
e9c9cfaa72 | ||
|
|
8ca4ea0f3f | ||
|
|
86e1f9a9d6 | ||
|
|
b36ceda585 | ||
|
|
27a3e6c6db | ||
|
|
a731327c00 | ||
|
|
737c00978e | ||
|
|
18bcb3a067 | ||
|
|
f49f55576f | ||
|
|
1bef4f9a4d | ||
|
|
ab1df59f7a |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
file: docker/Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
linux/arm64/v8
|
||||
|
||||
55
.github/workflows/bulit-lite.yml
vendored
55
.github/workflows/bulit-lite.yml
vendored
@@ -1,55 +0,0 @@
|
||||
name: MoviePilot Builder v2 Lite
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- v2
|
||||
paths:
|
||||
- 'version.py'
|
||||
|
||||
jobs:
|
||||
Docker-build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build Docker Image
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Release version
|
||||
id: release_version
|
||||
run: |
|
||||
app_version=$(cat version.py |sed -ne "s/APP_VERSION\s=\s'v\(.*\)'/\1/gp")
|
||||
echo "app_version=$app_version" >> $GITHUB_ENV
|
||||
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_USERNAME }}/moviepilot-v2
|
||||
tags: |
|
||||
type=raw,value=lite-latest
|
||||
|
||||
- name: Set Up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set Up Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.lite
|
||||
platforms: |
|
||||
linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha, scope=${{ github.workflow }}-docker
|
||||
cache-to: type=gha, scope=${{ github.workflow }}-docker
|
||||
@@ -1,93 +0,0 @@
|
||||
FROM python:3.12.8-slim-bookworm
|
||||
ENV LANG="C.UTF-8" \
|
||||
TZ="Asia/Shanghai" \
|
||||
HOME="/moviepilot" \
|
||||
CONFIG_DIR="/config" \
|
||||
TERM="xterm" \
|
||||
DISPLAY=:987 \
|
||||
PUID=0 \
|
||||
PGID=0 \
|
||||
UMASK=000 \
|
||||
PORT=3001 \
|
||||
NGINX_PORT=3000 \
|
||||
MOVIEPILOT_AUTO_UPDATE=release
|
||||
WORKDIR "/app"
|
||||
RUN apt-get update -y \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get -y install \
|
||||
musl-dev \
|
||||
nginx \
|
||||
gettext-base \
|
||||
locales \
|
||||
procps \
|
||||
gosu \
|
||||
bash \
|
||||
wget \
|
||||
curl \
|
||||
busybox \
|
||||
dumb-init \
|
||||
jq \
|
||||
fuse3 \
|
||||
rsync \
|
||||
ffmpeg \
|
||||
nano \
|
||||
&& \
|
||||
if [ "$(uname -m)" = "x86_64" ]; \
|
||||
then ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1; \
|
||||
elif [ "$(uname -m)" = "aarch64" ]; \
|
||||
then ln -s /usr/lib/aarch64-linux-musl/libc.so /lib/libc.musl-aarch64.so.1; \
|
||||
fi \
|
||||
&& curl https://rclone.org/install.sh | bash \
|
||||
&& curl --insecure -fsSL https://raw.githubusercontent.com/DDS-Derek/Aria2-Pro-Core/master/aria2-install.sh | bash \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf \
|
||||
/tmp/* \
|
||||
/moviepilot/.cache \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/tmp/*
|
||||
COPY requirements.in requirements.in
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y build-essential \
|
||||
&& pip install --upgrade pip \
|
||||
&& pip install Cython pip-tools \
|
||||
&& pip-compile requirements.in \
|
||||
&& pip install -r requirements.txt \
|
||||
&& playwright install-deps chromium \
|
||||
&& apt-get remove -y build-essential \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf \
|
||||
/tmp/* \
|
||||
/moviepilot/.cache \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/tmp/*
|
||||
COPY . .
|
||||
RUN cp -f /app/nginx.conf /etc/nginx/nginx.template.conf \
|
||||
&& cp -f /app/update /usr/local/bin/mp_update \
|
||||
&& cp -f /app/entrypoint /entrypoint \
|
||||
&& cp -f /app/docker_http_proxy.conf /etc/nginx/docker_http_proxy.conf \
|
||||
&& chmod +x /entrypoint /usr/local/bin/mp_update \
|
||||
&& mkdir -p ${HOME} \
|
||||
&& groupadd -r moviepilot -g 918 \
|
||||
&& useradd -r moviepilot -g moviepilot -d ${HOME} -s /bin/bash -u 918 \
|
||||
&& python_ver=$(python3 -V | awk '{print $2}') \
|
||||
&& echo "/app/" > /usr/local/lib/python${python_ver%.*}/site-packages/app.pth \
|
||||
&& echo 'fs.inotify.max_user_watches=5242880' >> /etc/sysctl.conf \
|
||||
&& echo 'fs.inotify.max_user_instances=5242880' >> /etc/sysctl.conf \
|
||||
&& locale-gen zh_CN.UTF-8 \
|
||||
&& python3 /app/setup.py \
|
||||
&& find /app/app -type f -name "*.py" ! -path "/app/app/main.py" -exec rm -f {} \; \
|
||||
&& FRONTEND_VERSION=$(sed -n "s/^FRONTEND_VERSION\s*=\s*'\([^']*\)'/\1/p" /app/version.py) \
|
||||
&& curl -sL "https://github.com/jxxghp/MoviePilot-Frontend/releases/download/${FRONTEND_VERSION}/dist.zip" | busybox unzip -d / - \
|
||||
&& mv /dist /public \
|
||||
&& curl -sL "https://github.com/jxxghp/MoviePilot-Plugins/archive/refs/heads/main.zip" | busybox unzip -d /tmp - \
|
||||
&& mv -f /tmp/MoviePilot-Plugins-main/plugins.v2/* /app/app/plugins/ \
|
||||
&& cat /tmp/MoviePilot-Plugins-main/package.json | jq -r 'to_entries[] | select(.value.v2 == true) | .key' | awk '{print tolower($0)}' | \
|
||||
while read -r i; do if [ ! -d "/app/app/plugins/$i" ]; then mv "/tmp/MoviePilot-Plugins-main/plugins/$i" "/app/app/plugins/"; else echo "跳过 $i"; fi; done \
|
||||
&& curl -sL "https://github.com/jxxghp/MoviePilot-Resources/archive/refs/heads/main.zip" | busybox unzip -d /tmp - \
|
||||
&& mv -f /tmp/MoviePilot-Resources-main/resources/* /app/app/helper/ \
|
||||
&& rm -rf /tmp/* /app/build
|
||||
EXPOSE 3000
|
||||
VOLUME [ "/config" ]
|
||||
ENTRYPOINT [ "/entrypoint" ]
|
||||
@@ -62,7 +62,7 @@ class FetchTorrentsAction(BaseAction):
|
||||
params = FetchTorrentsParams(**params)
|
||||
if params.search_type == "keyword":
|
||||
# 按关键字搜索
|
||||
torrents = self.searchchain.search_by_title(title=params.name, sites=params.sites, cache_local=False)
|
||||
torrents = self.searchchain.search_by_title(title=params.name, sites=params.sites)
|
||||
for torrent in torrents:
|
||||
if global_vars.is_workflow_stopped(workflow_id):
|
||||
break
|
||||
|
||||
@@ -58,12 +58,12 @@ def search_by_id(mediaid: str,
|
||||
if doubaninfo:
|
||||
torrents = SearchChain().search_by_id(doubanid=doubaninfo.get("id"),
|
||||
mtype=media_type, area=area, season=media_season,
|
||||
sites=site_list)
|
||||
sites=site_list, cache_local=True)
|
||||
else:
|
||||
return schemas.Response(success=False, message="未识别到豆瓣媒体信息")
|
||||
else:
|
||||
torrents = SearchChain().search_by_id(tmdbid=tmdbid, mtype=media_type, area=area, season=media_season,
|
||||
sites=site_list)
|
||||
sites=site_list, cache_local=True)
|
||||
elif mediaid.startswith("douban:"):
|
||||
doubanid = mediaid.replace("douban:", "")
|
||||
if settings.RECOGNIZE_SOURCE == "themoviedb":
|
||||
@@ -74,12 +74,12 @@ def search_by_id(mediaid: str,
|
||||
media_season = tmdbinfo.get('season')
|
||||
torrents = SearchChain().search_by_id(tmdbid=tmdbinfo.get("id"),
|
||||
mtype=media_type, area=area, season=media_season,
|
||||
sites=site_list)
|
||||
sites=site_list, cache_local=True)
|
||||
else:
|
||||
return schemas.Response(success=False, message="未识别到TMDB媒体信息")
|
||||
else:
|
||||
torrents = SearchChain().search_by_id(doubanid=doubanid, mtype=media_type, area=area, season=media_season,
|
||||
sites=site_list)
|
||||
sites=site_list, cache_local=True)
|
||||
elif mediaid.startswith("bangumi:"):
|
||||
bangumiid = int(mediaid.replace("bangumi:", ""))
|
||||
if settings.RECOGNIZE_SOURCE == "themoviedb":
|
||||
@@ -88,7 +88,7 @@ def search_by_id(mediaid: str,
|
||||
if tmdbinfo:
|
||||
torrents = SearchChain().search_by_id(tmdbid=tmdbinfo.get("id"),
|
||||
mtype=media_type, area=area, season=media_season,
|
||||
sites=site_list)
|
||||
sites=site_list, cache_local=True)
|
||||
else:
|
||||
return schemas.Response(success=False, message="未识别到TMDB媒体信息")
|
||||
else:
|
||||
@@ -97,7 +97,7 @@ def search_by_id(mediaid: str,
|
||||
if doubaninfo:
|
||||
torrents = SearchChain().search_by_id(doubanid=doubaninfo.get("id"),
|
||||
mtype=media_type, area=area, season=media_season,
|
||||
sites=site_list)
|
||||
sites=site_list, cache_local=True)
|
||||
else:
|
||||
return schemas.Response(success=False, message="未识别到豆瓣媒体信息")
|
||||
else:
|
||||
@@ -113,11 +113,11 @@ def search_by_id(mediaid: str,
|
||||
if event_data.media_dict:
|
||||
search_id = event_data.media_dict.get("id")
|
||||
if event_data.convert_type == "themoviedb":
|
||||
torrents = SearchChain().search_by_id(tmdbid=search_id,
|
||||
mtype=media_type, area=area, season=media_season)
|
||||
torrents = SearchChain().search_by_id(tmdbid=search_id, mtype=media_type, area=area,
|
||||
season=media_season, cache_local=True)
|
||||
elif event_data.convert_type == "douban":
|
||||
torrents = SearchChain().search_by_id(doubanid=search_id,
|
||||
mtype=media_type, area=area, season=media_season)
|
||||
torrents = SearchChain().search_by_id(doubanid=search_id, mtype=media_type, area=area,
|
||||
season=media_season, cache_local=True)
|
||||
else:
|
||||
if not title:
|
||||
return schemas.Response(success=False, message="未知的媒体ID")
|
||||
@@ -133,11 +133,11 @@ def search_by_id(mediaid: str,
|
||||
mediainfo = MediaChain().recognize_media(meta=meta)
|
||||
if mediainfo:
|
||||
if settings.RECOGNIZE_SOURCE == "themoviedb":
|
||||
torrents = SearchChain().search_by_id(tmdbid=mediainfo.tmdb_id,
|
||||
mtype=media_type, area=area, season=media_season)
|
||||
torrents = SearchChain().search_by_id(tmdbid=mediainfo.tmdb_id, mtype=media_type, area=area,
|
||||
season=media_season, cache_local=True)
|
||||
else:
|
||||
torrents = SearchChain().search_by_id(doubanid=mediainfo.douban_id,
|
||||
mtype=media_type, area=area, season=media_season)
|
||||
torrents = SearchChain().search_by_id(doubanid=mediainfo.douban_id, mtype=media_type, area=area,
|
||||
season=media_season, cache_local=True)
|
||||
# 返回搜索结果
|
||||
if not torrents:
|
||||
return schemas.Response(success=False, message="未搜索到任何资源")
|
||||
@@ -154,7 +154,8 @@ def search_by_title(keyword: Optional[str] = None,
|
||||
根据名称模糊搜索站点资源,支持分页,关键词为空是返回首页资源
|
||||
"""
|
||||
torrents = SearchChain().search_by_title(title=keyword, page=page,
|
||||
sites=[int(site) for site in sites.split(",") if site] if sites else None)
|
||||
sites=[int(site) for site in sites.split(",") if site] if sites else None,
|
||||
cache_local=True)
|
||||
if not torrents:
|
||||
return schemas.Response(success=False, message="未搜索到任何资源")
|
||||
return schemas.Response(success=True, data=[torrent.to_dict() for torrent in torrents])
|
||||
|
||||
@@ -283,6 +283,9 @@ def set_setting(key: str, value: Union[list, dict, bool, int, str] = None,
|
||||
success, message = settings.update_setting(key=key, value=value)
|
||||
return schemas.Response(success=success, message=message)
|
||||
elif key in {item.value for item in SystemConfigKey}:
|
||||
if isinstance(value, list):
|
||||
value = list(filter(None, value))
|
||||
value = value if value else None
|
||||
SystemConfigOper().set(key, value)
|
||||
return schemas.Response(success=True)
|
||||
else:
|
||||
|
||||
@@ -36,7 +36,7 @@ class SearchChain(ChainBase):
|
||||
|
||||
def search_by_id(self, tmdbid: Optional[int] = None, doubanid: Optional[str] = None,
|
||||
mtype: MediaType = None, area: Optional[str] = "title", season: Optional[int] = None,
|
||||
sites: List[int] = None) -> List[Context]:
|
||||
sites: List[int] = None, cache_local: bool = False) -> List[Context]:
|
||||
"""
|
||||
根据TMDBID/豆瓣ID搜索资源,精确匹配,不过滤本地存在的资源
|
||||
:param tmdbid: TMDB ID
|
||||
@@ -45,6 +45,7 @@ class SearchChain(ChainBase):
|
||||
:param area: 搜索范围,title or imdbid
|
||||
:param season: 季数
|
||||
:param sites: 站点ID列表
|
||||
:param cache_local: 是否缓存到本地
|
||||
"""
|
||||
mediainfo = self.recognize_media(tmdbid=tmdbid, doubanid=doubanid, mtype=mtype)
|
||||
if not mediainfo:
|
||||
@@ -59,12 +60,12 @@ class SearchChain(ChainBase):
|
||||
}
|
||||
results = self.process(mediainfo=mediainfo, sites=sites, area=area, no_exists=no_exists)
|
||||
# 保存到本地文件
|
||||
bytes_results = pickle.dumps(results)
|
||||
self.save_cache(bytes_results, self.__result_temp_file)
|
||||
if cache_local:
|
||||
self.save_cache(pickle.dumps(results), self.__result_temp_file)
|
||||
return results
|
||||
|
||||
def search_by_title(self, title: str, page: Optional[int] = 0,
|
||||
sites: List[int] = None, cache_local: Optional[bool] = True) -> List[Context]:
|
||||
sites: List[int] = None, cache_local: Optional[bool] = False) -> List[Context]:
|
||||
"""
|
||||
根据标题搜索资源,不识别不过滤,直接返回站点内容
|
||||
:param title: 标题,为空时返回所有站点首页内容
|
||||
@@ -86,8 +87,7 @@ class SearchChain(ChainBase):
|
||||
torrent_info=torrent) for torrent in torrents]
|
||||
# 保存到本地文件
|
||||
if cache_local:
|
||||
bytes_results = pickle.dumps(contexts)
|
||||
self.save_cache(bytes_results, self.__result_temp_file)
|
||||
self.save_cache(pickle.dumps(contexts), self.__result_temp_file)
|
||||
return contexts
|
||||
|
||||
def last_search_results(self) -> List[Context]:
|
||||
|
||||
@@ -297,7 +297,7 @@ class SiteChain(ChainBase):
|
||||
"""
|
||||
if StringUtils.get_url_domain(inx.get("domain")) == sub_domain:
|
||||
return inx.get("domain")
|
||||
for ext_d in inx.get("ext_domains"):
|
||||
for ext_d in inx.get("ext_domains", []):
|
||||
if StringUtils.get_url_domain(ext_d) == sub_domain:
|
||||
return ext_d
|
||||
return sub_domain
|
||||
|
||||
@@ -15,32 +15,32 @@ class ReleaseGroupsMatcher(metaclass=Singleton):
|
||||
"0ff": ['FF(?:(?:A|WE)B|CD|E(?:DU|B)|TV)'],
|
||||
"1pt": [],
|
||||
"52pt": [],
|
||||
"audiences": ['Audies', 'AD(?:Audio|E(?:|book)|Music|Web)'],
|
||||
"audiences": ['Audies', 'AD(?:Audio|E(?:book|)|Music|Web)'],
|
||||
"azusa": [],
|
||||
"beitai": ['BeiTai'],
|
||||
"btschool": ['Bts(?:CHOOL|HD|PAD|TV)', 'Zone'],
|
||||
"carpt": ['CarPT'],
|
||||
"chdbits": ['CHD(?:|Bits|PAD|(?:|HK)TV|WEB)', 'StBOX', 'OneHD', 'Lee', 'xiaopie'],
|
||||
"chdbits": ['CHD(?:Bits|PAD|(?:|HK)TV|WEB|)', 'StBOX', 'OneHD', 'Lee', 'xiaopie'],
|
||||
"discfan": [],
|
||||
"dragonhd": [],
|
||||
"eastgame": ['(?:(?:iNT|(?:HALFC|Mini(?:S|H|FH)D))-|)TLF'],
|
||||
"filelist": [],
|
||||
"gainbound": ['(?:DG|GBWE)B'],
|
||||
"hares": ['Hares(?:|(?:M|T)V|Web)'],
|
||||
"hares": ['Hares(?:(?:M|T)V|Web|)'],
|
||||
"hd4fans": [],
|
||||
"hdarea": ['HDA(?:pad|rea|TV)', 'EPiC'],
|
||||
"hdatmos": [],
|
||||
"hdbd": [],
|
||||
"hdchina": ['HDC(?:|hina|TV)', 'k9611', 'tudou', 'iHD'],
|
||||
"hdchina": ['HDC(?:hina|TV|)', 'k9611', 'tudou', 'iHD'],
|
||||
"hddolby": ['D(?:ream|BTV)', '(?:HD|QHstudI)o'],
|
||||
"hdfans": ['beAst(?:|TV)'],
|
||||
"hdhome": ['HDH(?:|ome|Pad|TV|WEB)'],
|
||||
"hdpt": ['HDPT(?:|Web)'],
|
||||
"hdsky": ['HDS(?:|ky|TV|Pad|WEB)', 'AQLJ'],
|
||||
"hdfans": ['beAst(?:TV|)'],
|
||||
"hdhome": ['HDH(?:ome|Pad|TV|WEB|)'],
|
||||
"hdpt": ['HDPT(?:Web|)'],
|
||||
"hdsky": ['HDS(?:ky|TV|Pad|WEB|)', 'AQLJ'],
|
||||
"hdtime": [],
|
||||
"HDU": [],
|
||||
"hdvideo": [],
|
||||
"hdzone": ['HDZ(?:|one)'],
|
||||
"hdzone": ['HDZ(?:one|)'],
|
||||
"hhanclub": ['HHWEB'],
|
||||
"hitpt": [],
|
||||
"htpt": ['HTPT'],
|
||||
@@ -48,34 +48,36 @@ class ReleaseGroupsMatcher(metaclass=Singleton):
|
||||
"joyhd": [],
|
||||
"keepfrds": ['FRDS', 'Yumi', 'cXcY'],
|
||||
"lemonhd": ['L(?:eague(?:(?:C|H)D|(?:M|T)V|NF|WEB)|HD)', 'i18n', 'CiNT'],
|
||||
"mteam": ['MTeam(?:|TV)', 'MPAD'],
|
||||
"mteam": ['MTeam(?:TV|)', 'MPAD'],
|
||||
"nanyangpt": [],
|
||||
"nicept": [],
|
||||
"oshen": [],
|
||||
"ourbits": ['Our(?:Bits|TV)', 'FLTTH', 'Ao', 'PbK', 'MGs', 'iLove(?:HD|TV)'],
|
||||
"piggo": ['PiGo(?:NF|(?:H|WE)B)'],
|
||||
"ptchina": [],
|
||||
"pterclub": ['PTer(?:|DIY|Game|(?:M|T)V|WEB)'],
|
||||
"pthome": ['PTH(?:|Audio|eBook|music|ome|tv|WEB)'],
|
||||
"pterclub": ['PTer(?:DIY|Game|(?:M|T)V|WEB|)'],
|
||||
"pthome": ['PTH(?:Audio|eBook|music|ome|tv|WEB|)'],
|
||||
"ptmsg": [],
|
||||
"ptsbao": ['PTsbao', 'OPS', 'F(?:Fans(?:AIeNcE|BD|D(?:VD|IY)|TV|WEB)|HDMv)', 'SGXT'],
|
||||
"pttime": [],
|
||||
"putao": ['PuTao'],
|
||||
"soulvoice": [],
|
||||
"springsunday": ['CMCT(?:|V)'],
|
||||
"sharkpt": ['Shark(?:|WEB|DIY|TV|MV)'],
|
||||
"springsunday": ['CMCT(?:V|)'],
|
||||
"sharkpt": ['Shark(?:WEB|DIY|TV|MV|)'],
|
||||
"tccf": [],
|
||||
"tjupt": ['TJUPT'],
|
||||
"totheglory": ['TTG', 'WiKi', 'NGB', 'DoA', '(?:ARi|ExRE)N'],
|
||||
"U2": [],
|
||||
"ultrahd": [],
|
||||
"others": ['B(?:MDru|eyondHD|TN)', 'C(?:fandora|trlhd|MRG)', 'DON', 'EVO', 'FLUX', 'HONE(?:|yG)',
|
||||
'N(?:oGroup|T(?:b|G))', 'PandaMoon', 'SMURF', 'T(?:EPES|aengoo|rollHD )', 'UBWEB'],
|
||||
"others": ['B(?:MDru|eyondHD|TN)', 'C(?:fandora|trlhd|MRG)', 'DON', 'EVO', 'FLUX', 'HONE(?:yG|)',
|
||||
'N(?:oGroup|T(?:b|G))', 'PandaMoon', 'SMURF', 'T(?:EPES|aengoo|rollHD )',],
|
||||
"anime": ['ANi', 'HYSUB', 'KTXP', 'LoliHouse', 'MCE', 'Nekomoe kissaten', 'SweetSub', 'MingY',
|
||||
'(?:Lilith|NC)-Raws', '织梦字幕组', '枫叶字幕组', '猎户手抄部', '喵萌奶茶屋', '漫猫字幕社',
|
||||
'霜庭云花Sub', '北宇治字幕组', '氢气烤肉架', '云歌字幕组', '萌樱字幕组', '极影字幕社',
|
||||
'悠哈璃羽字幕社',
|
||||
'❀拨雪寻春❀', '沸羊羊(?:制作|字幕组)', '(?:桜|樱)都字幕组']
|
||||
'❀拨雪寻春❀', '沸羊羊(?:制作|字幕组)', '(?:桜|樱)都字幕组'],
|
||||
"forge": ['FROG(?:E|Web|)'],
|
||||
"ubits": ['UB(?:its|WEB|TV)'],
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@@ -97,6 +99,8 @@ class ReleaseGroupsMatcher(metaclass=Singleton):
|
||||
if not groups:
|
||||
# 自定义组
|
||||
custom_release_groups = self.systemconfig.get(SystemConfigKey.CustomReleaseGroups)
|
||||
if isinstance(custom_release_groups, list):
|
||||
custom_release_groups = list(filter(None, custom_release_groups))
|
||||
if custom_release_groups:
|
||||
custom_release_groups_str = '|'.join(custom_release_groups)
|
||||
groups = f"{self.__release_groups}|{custom_release_groups_str}"
|
||||
|
||||
@@ -113,6 +113,7 @@ class DownloadHistoryOper(DbOper):
|
||||
season: Optional[str] = None, episode: Optional[str] = None, tmdbid=None) -> List[DownloadHistory]:
|
||||
"""
|
||||
按类型、标题、年份、季集查询下载记录
|
||||
tmdbid + mtype 或 title + year
|
||||
"""
|
||||
return DownloadHistory.get_last_by(db=self._db,
|
||||
mtype=mtype,
|
||||
|
||||
@@ -85,45 +85,54 @@ class DownloadHistory(Base):
|
||||
year: Optional[str] = None, season: Optional[str] = None,
|
||||
episode: Optional[str] = None, tmdbid: Optional[int] = None):
|
||||
"""
|
||||
据tmdbid、season、season_episode查询转移记录
|
||||
据tmdbid、season、season_episode查询下载记录
|
||||
tmdbid + mtype 或 title + year
|
||||
"""
|
||||
result = None
|
||||
if tmdbid and not season and not episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid).order_by(
|
||||
# TMDBID + 类型
|
||||
if tmdbid and mtype:
|
||||
# 电视剧某季某集
|
||||
if season and episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
||||
DownloadHistory.type == mtype,
|
||||
DownloadHistory.seasons == season,
|
||||
DownloadHistory.episodes == episode).order_by(
|
||||
DownloadHistory.id.desc()).all()
|
||||
if tmdbid and season and not episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
||||
DownloadHistory.seasons == season).order_by(
|
||||
# 电视剧某季
|
||||
elif season:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
||||
DownloadHistory.type == mtype,
|
||||
DownloadHistory.seasons == season).order_by(
|
||||
DownloadHistory.id.desc()).all()
|
||||
if tmdbid and season and episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
||||
DownloadHistory.seasons == season,
|
||||
DownloadHistory.episodes == episode).order_by(
|
||||
else:
|
||||
# 电视剧所有季集/电影
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
||||
DownloadHistory.type == mtype).order_by(
|
||||
DownloadHistory.id.desc()).all()
|
||||
# 电视剧所有季集|电影
|
||||
if not season and not episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.type == mtype,
|
||||
DownloadHistory.title == title,
|
||||
DownloadHistory.year == year).order_by(
|
||||
# 标题 + 年份
|
||||
elif title and year:
|
||||
# 电视剧某季某集
|
||||
if season and episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.title == title,
|
||||
DownloadHistory.year == year,
|
||||
DownloadHistory.seasons == season,
|
||||
DownloadHistory.episodes == episode).order_by(
|
||||
DownloadHistory.id.desc()).all()
|
||||
# 电视剧某季
|
||||
if season and not episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.type == mtype,
|
||||
DownloadHistory.title == title,
|
||||
DownloadHistory.year == year,
|
||||
DownloadHistory.seasons == season).order_by(
|
||||
# 电视剧某季
|
||||
elif season:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.title == title,
|
||||
DownloadHistory.year == year,
|
||||
DownloadHistory.seasons == season).order_by(
|
||||
DownloadHistory.id.desc()).all()
|
||||
# 电视剧某季某集
|
||||
if season and episode:
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.type == mtype,
|
||||
DownloadHistory.title == title,
|
||||
DownloadHistory.year == year,
|
||||
DownloadHistory.seasons == season,
|
||||
DownloadHistory.episodes == episode).order_by(
|
||||
else:
|
||||
# 电视剧所有季集/电影
|
||||
result = db.query(DownloadHistory).filter(DownloadHistory.title == title,
|
||||
DownloadHistory.year == year).order_by(
|
||||
DownloadHistory.id.desc()).all()
|
||||
|
||||
if result:
|
||||
return list(result)
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
@db_query
|
||||
|
||||
@@ -1031,6 +1031,8 @@ class Emby:
|
||||
eventItem.image_url = self.get_remote_image_by_id(item_id=eventItem.item_id,
|
||||
image_type="Backdrop")
|
||||
|
||||
eventItem.json_object = message
|
||||
|
||||
return eventItem
|
||||
|
||||
def get_data(self, url: str) -> Optional[Response]:
|
||||
|
||||
@@ -323,6 +323,8 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
||||
cid = '0'
|
||||
else:
|
||||
cid = fileitem.fileid
|
||||
if not cid:
|
||||
cid = self._path_to_id(fileitem.path)
|
||||
|
||||
items = []
|
||||
offset = 0
|
||||
|
||||
@@ -696,6 +696,8 @@ class Jellyfin:
|
||||
# jellyfin 的 webhook 不含 item_path,需要单独获取
|
||||
eventItem.item_path = self.get_item_path_by_id(eventItem.item_id)
|
||||
|
||||
eventItem.json_object = message
|
||||
|
||||
return eventItem
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -703,6 +703,8 @@ class Plex:
|
||||
eventItem.image_url = self.get_remote_image_by_id(item_id=eventItem.item_id,
|
||||
image_type="Backdrop")
|
||||
|
||||
eventItem.json_object = message
|
||||
|
||||
return eventItem
|
||||
|
||||
def get_plex(self):
|
||||
|
||||
@@ -160,6 +160,7 @@ class WebhookEventInfo(BaseModel):
|
||||
save_reason: Optional[str] = None
|
||||
item_isvirtual: Optional[bool] = None
|
||||
media_type: Optional[str] = None
|
||||
json_object: Optional[dict] = {}
|
||||
|
||||
|
||||
class MediaServerPlayItem(BaseModel):
|
||||
|
||||
@@ -642,13 +642,14 @@ class StringUtils:
|
||||
if len(parts) > 3:
|
||||
# 处理不希望包含多个冒号的情况(除了协议后的冒号)
|
||||
return None, None
|
||||
# 不含端口地址
|
||||
domain = ":".join(parts[:-1]).rstrip('/')
|
||||
# 端口号
|
||||
try:
|
||||
elif len(parts) == 3:
|
||||
port = int(parts[-1])
|
||||
except ValueError:
|
||||
# 端口号不是整数,返回 None 表示无效
|
||||
# 不含端口地址
|
||||
domain = ":".join(parts[:-1]).rstrip('/')
|
||||
elif len(parts) == 2:
|
||||
port = 443 if address.startswith("https") else 80
|
||||
domain = address
|
||||
else:
|
||||
return None, None
|
||||
return domain, port
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ RUN apt-get update -y \
|
||||
then ln -s /usr/lib/aarch64-linux-musl/libc.so /lib/libc.musl-aarch64.so.1; \
|
||||
fi \
|
||||
&& curl https://rclone.org/install.sh | bash \
|
||||
&& curl --insecure -fsSL https://raw.githubusercontent.com/DDS-Derek/Aria2-Pro-Core/master/aria2-install.sh | bash \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf \
|
||||
@@ -46,7 +45,7 @@ RUN apt-get update -y \
|
||||
/moviepilot/.cache \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/tmp/*
|
||||
COPY requirements.in requirements.in
|
||||
COPY ../requirements.in requirements.in
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y build-essential \
|
||||
&& pip install --upgrade pip \
|
||||
@@ -62,12 +61,13 @@ RUN apt-get update -y \
|
||||
/moviepilot/.cache \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/tmp/*
|
||||
COPY . .
|
||||
RUN cp -f /app/nginx.conf /etc/nginx/nginx.template.conf \
|
||||
&& cp -f /app/update /usr/local/bin/mp_update \
|
||||
&& cp -f /app/entrypoint /entrypoint \
|
||||
&& cp -f /app/docker_http_proxy.conf /etc/nginx/docker_http_proxy.conf \
|
||||
&& chmod +x /entrypoint /usr/local/bin/mp_update \
|
||||
COPY .. .
|
||||
RUN cp -f /app/docker/nginx.common.conf /etc/nginx/common.conf \
|
||||
&& cp -f /app/docker/nginx.template.conf /etc/nginx/nginx.template.conf \
|
||||
&& cp -f /app/docker/update.sh /usr/local/bin/mp_update.sh \
|
||||
&& cp -f /app/docker/entrypoint.sh /entrypoint.sh \
|
||||
&& cp -f /app/docker/docker_http_proxy.conf /etc/nginx/docker_http_proxy.conf \
|
||||
&& chmod +x /entrypoint.sh /usr/local/bin/mp_update.sh \
|
||||
&& mkdir -p ${HOME} \
|
||||
&& groupadd -r moviepilot -g 918 \
|
||||
&& useradd -r moviepilot -g moviepilot -d ${HOME} -s /bin/bash -u 918 \
|
||||
@@ -88,4 +88,4 @@ RUN cp -f /app/nginx.conf /etc/nginx/nginx.template.conf \
|
||||
&& rm -rf /tmp/*
|
||||
EXPOSE 3000
|
||||
VOLUME [ "/config" ]
|
||||
ENTRYPOINT [ "/entrypoint" ]
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
101
docker/cert.sh
Normal file
101
docker/cert.sh
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
Green="\033[32m"
|
||||
Red="\033[31m"
|
||||
Yellow='\033[33m'
|
||||
Font="\033[0m"
|
||||
INFO="[${Green}INFO${Font}]"
|
||||
ERROR="[${Red}ERROR${Font}]"
|
||||
WARN="[${Yellow}WARN${Font}]"
|
||||
function INFO() {
|
||||
echo -e "${INFO} ${1}"
|
||||
}
|
||||
function ERROR() {
|
||||
echo -e "${ERROR} ${1}"
|
||||
}
|
||||
function WARN() {
|
||||
echo -e "${WARN} ${1}"
|
||||
}
|
||||
|
||||
# 核心条件验证
|
||||
if [ "${ENABLE_SSL}" = "true" ] && \
|
||||
[ "${AUTO_ISSUE_CERT}" = "true" ] && \
|
||||
[ -n "${SSL_DOMAIN}" ]; then
|
||||
|
||||
# 创建证书目录
|
||||
mkdir -p /config/certs/"${SSL_DOMAIN}"
|
||||
chown moviepilot:moviepilot /config/certs -R
|
||||
|
||||
# 安装acme.sh(使用官方安装脚本)
|
||||
if [ ! -d "/config/acme.sh" ]; then
|
||||
INFO "→ 安装acme.sh..."
|
||||
|
||||
# 生成安装参数
|
||||
INSTALL_ARGS=(
|
||||
"--install-online"
|
||||
"--home" "/config/acme.sh"
|
||||
"--config-home" "/config/acme.sh/data"
|
||||
"--cert-home" "/config/certs"
|
||||
)
|
||||
|
||||
# 添加邮箱参数(如果设置)
|
||||
if [ -n "${SSL_EMAIL}" ]; then
|
||||
INSTALL_ARGS+=("--accountemail" "${SSL_EMAIL}")
|
||||
else
|
||||
WARN "未设置SSL_EMAIL,建议配置邮箱用于证书过期提醒"
|
||||
fi
|
||||
|
||||
# 执行官方安装命令
|
||||
curl -sSL https://get.acme.sh | sh -s -- "${INSTALL_ARGS[@]}"
|
||||
fi
|
||||
|
||||
# 签发证书(仅当证书不存在时)
|
||||
if [ ! -f "/config/certs/${SSL_DOMAIN}/fullchain.pem" ]; then
|
||||
# 必要参数检查
|
||||
REQUIRED_VARS=("DNS_PROVIDER")
|
||||
for var in "${REQUIRED_VARS[@]}"; do
|
||||
eval "value=\${${var}}"
|
||||
[ -z "$value" ] && { ERROR "必须设置环境变量: ${var}"; exit 1; }
|
||||
done
|
||||
|
||||
INFO "→ 签发证书: ${SSL_DOMAIN} (DNS验证方式: ${DNS_PROVIDER})"
|
||||
|
||||
# 加载ACME环境变量(带安全过滤)
|
||||
INFO "正在加载ACME环境变量..."
|
||||
env | grep '^ACME_ENV_' | while read -r line; do
|
||||
key="${line#ACME_ENV_}"
|
||||
key="${key%%=*}"
|
||||
value="${line#ACME_ENV_${key}=}"
|
||||
|
||||
# 过滤非法变量名
|
||||
if [[ "$key" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
|
||||
export "$key"="$value"
|
||||
INFO "已加载环境变量: ${key}=******"
|
||||
else
|
||||
WARN "跳过无效变量名: ${key}"
|
||||
fi
|
||||
done
|
||||
|
||||
# 签发证书
|
||||
/config/acme.sh/acme.sh --issue \
|
||||
--dns "${DNS_PROVIDER}" \
|
||||
--domain "${SSL_DOMAIN}" \
|
||||
--key-file /config/certs/"${SSL_DOMAIN}"/privkey.pem \
|
||||
--fullchain-file /config/certs/"${SSL_DOMAIN}"/fullchain.pem \
|
||||
--reloadcmd "nginx -s reload" \
|
||||
--force
|
||||
|
||||
# 创建稳定符号链接
|
||||
ln -sf /config/certs/"${SSL_DOMAIN}" /config/certs/latest
|
||||
fi
|
||||
|
||||
# 配置自动更新任务
|
||||
INFO "→ 配置cron自动更新..."
|
||||
echo "0 3 * * * /config/acme.sh/acme.sh --cron --home /config/acme.sh && nginx -s reload" > /etc/cron.d/acme
|
||||
chmod 644 /etc/cron.d/acme
|
||||
service cron start
|
||||
|
||||
elif [ "${ENABLE_SSL}" = "true" ] && [ "${AUTO_ISSUE_CERT}" = "true" ] && [ -z "${SSL_DOMAIN}" ]; then
|
||||
WARN "已启用自动签发证书但未设置SSL_DOMAIN,跳过证书管理"
|
||||
fi
|
||||
97
docker/entrypoint.sh
Normal file
97
docker/entrypoint.sh
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
# shellcheck shell=bash
|
||||
# shellcheck disable=SC2016
|
||||
# shellcheck disable=SC2155
|
||||
|
||||
Green="\033[32m"
|
||||
Red="\033[31m"
|
||||
Yellow='\033[33m'
|
||||
Font="\033[0m"
|
||||
INFO="[${Green}INFO${Font}]"
|
||||
ERROR="[${Red}ERROR${Font}]"
|
||||
WARN="[${Yellow}WARN${Font}]"
|
||||
function INFO() {
|
||||
echo -e "${INFO} ${1}"
|
||||
}
|
||||
function ERROR() {
|
||||
echo -e "${ERROR} ${1}"
|
||||
}
|
||||
function WARN() {
|
||||
echo -e "${WARN} ${1}"
|
||||
}
|
||||
|
||||
# 生成HTTPS配置块
|
||||
if [ "${ENABLE_SSL}" = "true" ]; then
|
||||
export HTTPS_SERVER_CONF=$(cat <<EOF
|
||||
server {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name ${SSL_DOMAIN:-moviepilot};
|
||||
|
||||
# SSL证书路径
|
||||
ssl_certificate /config/certs/latest/fullchain.pem;
|
||||
ssl_certificate_key /config/certs/latest/privkey.pem;
|
||||
|
||||
# SSL安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 公共配置
|
||||
include common.conf;
|
||||
}
|
||||
EOF
|
||||
)
|
||||
else
|
||||
export HTTPS_SERVER_CONF="# HTTPS未启用"
|
||||
fi
|
||||
|
||||
# 使用 `envsubst` 将模板文件中的 ${NGINX_PORT} 替换为实际的环境变量值
|
||||
export NGINX_CLIENT_MAX_BODY_SIZE=${NGINX_CLIENT_MAX_BODY_SIZE:-10m}
|
||||
envsubst '${NGINX_PORT}${PORT}${NGINX_CLIENT_MAX_BODY_SIZE}${ENABLE_SSL}${HTTPS_SERVER_CONF}' < /etc/nginx/nginx.template.conf > /etc/nginx/nginx.conf
|
||||
# 自动更新
|
||||
cd /
|
||||
source /usr/local/bin/mp_update.sh
|
||||
cd /app || exit
|
||||
# 更改 moviepilot userid 和 groupid
|
||||
groupmod -o -g "${PGID}" moviepilot
|
||||
usermod -o -u "${PUID}" moviepilot
|
||||
# 更改文件权限
|
||||
chown -R moviepilot:moviepilot \
|
||||
"${HOME}" \
|
||||
/app \
|
||||
/public \
|
||||
/config \
|
||||
/var/lib/nginx \
|
||||
/var/log/nginx
|
||||
chown moviepilot:moviepilot /etc/hosts /tmp
|
||||
# 下载浏览器内核
|
||||
if [[ "$HTTPS_PROXY" =~ ^https?:// ]] || [[ "$HTTPS_PROXY" =~ ^https?:// ]] || [[ "$PROXY_HOST" =~ ^https?:// ]]; then
|
||||
HTTPS_PROXY="${HTTPS_PROXY:-${https_proxy:-$PROXY_HOST}}" gosu moviepilot:moviepilot playwright install chromium
|
||||
else
|
||||
gosu moviepilot:moviepilot playwright install chromium
|
||||
fi
|
||||
# 证书管理
|
||||
source /app/docker/cert.sh
|
||||
# 启动前端nginx服务
|
||||
INFO "→ 启动前端nginx服务..."
|
||||
nginx
|
||||
# 启动docker http proxy nginx
|
||||
if [ -S "/var/run/docker.sock" ]; then
|
||||
INFO "→ 启动 Docker Proxy..."
|
||||
nginx -c /etc/nginx/docker_http_proxy.conf
|
||||
# 上面nginx是通过root启动的,会将目录权限改成root,所以需要重新再设置一遍权限
|
||||
chown -R moviepilot:moviepilot \
|
||||
/var/lib/nginx \
|
||||
/var/log/nginx
|
||||
fi
|
||||
# 设置后端服务权限掩码
|
||||
umask "${UMASK}"
|
||||
# 启动后端服务
|
||||
INFO "→ 启动后端服务..."
|
||||
exec dumb-init gosu moviepilot:moviepilot python3 app/main.py
|
||||
100
docker/nginx.common.conf
Normal file
100
docker/nginx.common.conf
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
# 公共根目录
|
||||
root /public;
|
||||
|
||||
# 主应用路由
|
||||
location / {
|
||||
expires off;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 图片类静态资源
|
||||
location ~* \.(png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# assets目录
|
||||
location /assets {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 站点图标
|
||||
location /api/v1/site/icon/ {
|
||||
# 站点图标缓存
|
||||
proxy_cache my_cache;
|
||||
# 缓存响应码为200和302的请求1小时
|
||||
proxy_cache_valid 200 302 1h;
|
||||
# 缓存其他响应码的请求5分钟
|
||||
proxy_cache_valid any 5m;
|
||||
# 缓存键的生成规则
|
||||
proxy_cache_key "$scheme$request_method$host$request_uri";
|
||||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
||||
|
||||
# 向后端API转发请求
|
||||
proxy_pass http://backend_api;
|
||||
}
|
||||
|
||||
|
||||
# 本地CookieCloud
|
||||
location /cookiecloud {
|
||||
proxy_pass http://backend_api;
|
||||
rewrite ^.+mock-server/?(.*)$ /$1 break;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Nginx-Proxy true;
|
||||
|
||||
# 超时设置
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# SSE特殊配置
|
||||
location ~ ^/api/v1/system/(message|progress/) {
|
||||
# SSE MIME类型设置
|
||||
default_type text/event-stream;
|
||||
|
||||
# 禁用缓存
|
||||
add_header Cache-Control no-cache;
|
||||
add_header X-Accel-Buffering no;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# 代理设置
|
||||
proxy_pass http://backend_api;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# 超时设置
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
|
||||
# API代理配置
|
||||
location /api {
|
||||
proxy_pass http://backend_api;
|
||||
rewrite ^.+mock-server/?(.*)$ /$1 break;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Nginx-Proxy true;
|
||||
|
||||
# 超时设置
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
55
docker/nginx.template.conf
Normal file
55
docker/nginx.template.conf
Normal file
@@ -0,0 +1,55 @@
|
||||
user moviepilot;
|
||||
worker_processes auto;
|
||||
worker_cpu_affinity auto;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
# 设置缓存路径和缓存区大小
|
||||
proxy_cache_path /tmp levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=60m use_temp_path=off;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 3600;
|
||||
|
||||
client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE};
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
gzip_proxied any;
|
||||
gzip_min_length 256;
|
||||
gzip_vary on;
|
||||
gzip_comp_level 6;
|
||||
|
||||
# HTTP
|
||||
server {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
listen ${NGINX_PORT};
|
||||
listen [::]:${NGINX_PORT};
|
||||
server_name moviepilot;
|
||||
|
||||
# HTTPS重定向
|
||||
if (${ENABLE_SSL} = 'true') {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# 公共配置
|
||||
include common.conf;
|
||||
}
|
||||
|
||||
# HTTPS
|
||||
${HTTPS_SERVER_CONF}
|
||||
|
||||
upstream backend_api {
|
||||
# 后端API的地址和端口
|
||||
server 127.0.0.1:${PORT};
|
||||
# 可以添加更多后端服务器作为负载均衡
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ function download_and_unzip() {
|
||||
local max_retries=3
|
||||
local url="$1"
|
||||
local target_dir="$2"
|
||||
INFO "正在下载 ${url}..."
|
||||
INFO "→ 正在下载 ${url}..."
|
||||
while [ $retries -lt $max_retries ]; do
|
||||
if curl ${CURL_OPTIONS} "${url}" ${CURL_HEADERS} | busybox unzip -d ${TMP_PATH} - > /dev/null; then
|
||||
if [ -e ${TMP_PATH}/MoviePilot-* ]; then
|
||||
@@ -54,19 +54,19 @@ function install_backend_and_download_resources() {
|
||||
return 1
|
||||
fi
|
||||
INFO "后端程序下载成功"
|
||||
INFO "依赖安装中..."
|
||||
INFO "→ 正在安装依赖..."
|
||||
if ! pip install ${PIP_OPTIONS} --upgrade --root-user-action=ignore pip > /dev/null; then
|
||||
ERROR "pip 更新失败,请重新拉取镜像"
|
||||
return 1
|
||||
fi
|
||||
if ! pip install ${PIP_OPTIONS} --root-user-action=ignore -r ${TMP_PATH}/App/requirements.txt > /dev/null; then
|
||||
ERROR "安装依赖失败,请重新拉取镜像"
|
||||
ERROR "依赖安装失败,请重新拉取镜像"
|
||||
return 1
|
||||
fi
|
||||
INFO "安装依赖成功"
|
||||
INFO "依赖安装成功"
|
||||
# 如果是"heads/v2.zip",则查找v2开头的最新版本号
|
||||
if [[ "${1}" == "heads/v2.zip" ]]; then
|
||||
INFO "正在获取前端最新版本号..."
|
||||
INFO "→ 正在获取前端最新版本号..."
|
||||
# 获取所有发布的版本列表,并筛选出以v2开头的版本号
|
||||
releases=$(curl ${CURL_OPTIONS} "https://api.github.com/repos/jxxghp/MoviePilot-Frontend/releases" ${CURL_HEADERS} | jq -r '.[].tag_name' | grep "^v2\.")
|
||||
if [ -z "$releases" ]; then
|
||||
@@ -78,7 +78,7 @@ function install_backend_and_download_resources() {
|
||||
fi
|
||||
INFO "前端最新版本号:${frontend_version}"
|
||||
else
|
||||
INFO "正在获取前端版本号..."
|
||||
INFO "→ 正在获取前端版本号..."
|
||||
# 从后端文件中读取前端版本号
|
||||
frontend_version=$(sed -n "s/^FRONTEND_VERSION\s*=\s*'\([^']*\)'/\1/p" ${TMP_PATH}/App/version.py)
|
||||
if [[ "${frontend_version}" != *v* ]]; then
|
||||
@@ -94,13 +94,13 @@ function install_backend_and_download_resources() {
|
||||
fi
|
||||
INFO "前端程序下载成功"
|
||||
# 备份插件目录
|
||||
INFO "备份插件目录中..."
|
||||
INFO "→ 正在备份插件目录..."
|
||||
rm -rf /plugins
|
||||
mkdir -p /plugins
|
||||
cp -a /app/app/plugins/* /plugins/
|
||||
rm -f /plugins/__init__.py
|
||||
# 备份站点资源
|
||||
INFO "备份站点资源目录中..."
|
||||
INFO "→ 正在备份站点资源目录..."
|
||||
rm -rf /resources_bakcup
|
||||
mkdir /resources_bakcup
|
||||
cp -a /app/app/helper/user.sites.bin /resources_bakcup
|
||||
@@ -118,14 +118,13 @@ function install_backend_and_download_resources() {
|
||||
# 恢复插件目录
|
||||
cp -a /plugins/* /app/app/plugins/
|
||||
# 更新站点资源
|
||||
INFO "开始更新站点资源..."
|
||||
INFO "→ 开始更新站点资源..."
|
||||
if ! download_and_unzip "${GITHUB_PROXY}https://github.com/jxxghp/MoviePilot-Resources/archive/refs/heads/main.zip" "Resources"; then
|
||||
cp -a /resources_bakcup/* /app/app/helper/
|
||||
rm -rf /resources_bakcup
|
||||
WARN "站点资源下载失败,继续使用旧的资源来启动..."
|
||||
return 1
|
||||
fi
|
||||
INFO "站点资源下载成功"
|
||||
# 复制新站点资源
|
||||
cp -a ${TMP_PATH}/Resources/resources/* /app/app/helper/
|
||||
INFO "站点资源更新成功"
|
||||
43
entrypoint
43
entrypoint
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
# shellcheck shell=bash
|
||||
# shellcheck disable=SC2016
|
||||
|
||||
# 使用 `envsubst` 将模板文件中的 ${NGINX_PORT} 替换为实际的环境变量值
|
||||
export NGINX_CLIENT_MAX_BODY_SIZE=${NGINX_CLIENT_MAX_BODY_SIZE:-10m}
|
||||
envsubst '${NGINX_PORT}${PORT}${NGINX_CLIENT_MAX_BODY_SIZE}' < /etc/nginx/nginx.template.conf > /etc/nginx/nginx.conf
|
||||
# 自动更新
|
||||
cd /
|
||||
/usr/local/bin/mp_update
|
||||
cd /app || exit
|
||||
# 更改 moviepilot userid 和 groupid
|
||||
groupmod -o -g "${PGID}" moviepilot
|
||||
usermod -o -u "${PUID}" moviepilot
|
||||
# 更改文件权限
|
||||
chown -R moviepilot:moviepilot \
|
||||
"${HOME}" \
|
||||
/app \
|
||||
/public \
|
||||
/config \
|
||||
/var/lib/nginx \
|
||||
/var/log/nginx
|
||||
chown moviepilot:moviepilot /etc/hosts /tmp
|
||||
# 下载浏览器内核
|
||||
if [[ "$HTTPS_PROXY" =~ ^https?:// ]] || [[ "$https_proxy" =~ ^https?:// ]] || [[ "$PROXY_HOST" =~ ^https?:// ]]; then
|
||||
HTTPS_PROXY="${HTTPS_PROXY:-${https_proxy:-$PROXY_HOST}}" gosu moviepilot:moviepilot playwright install chromium
|
||||
else
|
||||
gosu moviepilot:moviepilot playwright install chromium
|
||||
fi
|
||||
# 启动前端nginx服务
|
||||
nginx
|
||||
# 启动docker http proxy nginx
|
||||
if [ -S "/var/run/docker.sock" ]; then
|
||||
nginx -c /etc/nginx/docker_http_proxy.conf
|
||||
# 上面nginx是通过root启动的,会将目录权限改成root,所以需要重新再设置一遍权限
|
||||
chown -R moviepilot:moviepilot \
|
||||
/var/lib/nginx \
|
||||
/var/log/nginx
|
||||
fi
|
||||
# 设置后端服务权限掩码
|
||||
umask "${UMASK}"
|
||||
# 启动后端服务
|
||||
exec dumb-init gosu moviepilot:moviepilot python3 app/main.py
|
||||
142
nginx.conf
142
nginx.conf
@@ -1,142 +0,0 @@
|
||||
user moviepilot;
|
||||
worker_processes auto;
|
||||
worker_cpu_affinity auto;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
|
||||
# 设置缓存路径和缓存区大小
|
||||
proxy_cache_path /tmp levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=60m use_temp_path=off;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 3600;
|
||||
|
||||
client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE};
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
gzip_proxied any;
|
||||
gzip_min_length 256;
|
||||
gzip_vary on;
|
||||
gzip_comp_level 6;
|
||||
|
||||
server {
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
listen ${NGINX_PORT};
|
||||
listen [::]:${NGINX_PORT};
|
||||
server_name moviepilot;
|
||||
|
||||
location / {
|
||||
# 主目录
|
||||
expires off;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
root /public;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(png|jpg|jpeg|gif|ico|svg)$ {
|
||||
# 静态资源
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
root /public;
|
||||
}
|
||||
|
||||
location /assets {
|
||||
# 静态资源
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
root /public;
|
||||
}
|
||||
|
||||
location /api/v1/site/icon/ {
|
||||
# 站点图标缓存
|
||||
proxy_cache my_cache;
|
||||
# 缓存响应码为200和302的请求1小时
|
||||
proxy_cache_valid 200 302 1h;
|
||||
# 缓存其他响应码的请求5分钟
|
||||
proxy_cache_valid any 5m;
|
||||
# 缓存键的生成规则
|
||||
proxy_cache_key "$scheme$request_method$host$request_uri";
|
||||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
||||
|
||||
# 向后端API转发请求
|
||||
proxy_pass http://backend_api;
|
||||
}
|
||||
|
||||
location /cookiecloud {
|
||||
# 后端cookiecloud地址
|
||||
proxy_pass http://backend_api;
|
||||
rewrite ^.+mock-server/?(.*)$ /$1 break;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Nginx-Proxy true;
|
||||
|
||||
# 超时设置
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
location ~ ^/api/v1/system/(message|progress/) {
|
||||
# SSE MIME类型设置
|
||||
default_type text/event-stream;
|
||||
|
||||
# 禁用缓存
|
||||
add_header Cache-Control no-cache;
|
||||
add_header X-Accel-Buffering no;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# 代理设置
|
||||
proxy_pass http://backend_api;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# 超时设置
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
|
||||
location /api {
|
||||
# 后端API
|
||||
proxy_pass http://backend_api;
|
||||
rewrite ^.+mock-server/?(.*)$ /$1 break;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Nginx-Proxy true;
|
||||
|
||||
# 超时设置
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
}
|
||||
|
||||
upstream backend_api {
|
||||
# 后端API的地址和端口
|
||||
server 127.0.0.1:${PORT};
|
||||
# 可以添加更多后端服务器作为负载均衡
|
||||
}
|
||||
|
||||
}
|
||||
417
tests/cases/groups.py
Normal file
417
tests/cases/groups.py
Normal file
@@ -0,0 +1,417 @@
|
||||
release_group_cases = [
|
||||
# 0ff 组(示例结构)
|
||||
{
|
||||
"domain": "0ff",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFAB", "group": "FFAB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFWEB", "group": "FFWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFCD", "group": "FFCD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFEDU", "group": "FFEDU"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFEB", "group": "FFEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFTV", "group": "FFTV"}
|
||||
]
|
||||
},
|
||||
# audiences 组(示例结构)
|
||||
{
|
||||
"domain": "audiences",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Audies", "group": "Audies"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ADE", "group": "ADE"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ADAudio", "group": "ADAudio"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ADEbook", "group": "ADEbook"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ADMusic", "group": "ADMusic"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ADWeb", "group": "ADWeb"}
|
||||
]
|
||||
},
|
||||
# ---- 以下为新增结构化部分 ----
|
||||
# beitai 组
|
||||
{
|
||||
"domain": "beitai",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BeiTai", "group": "BeiTai"}
|
||||
]
|
||||
},
|
||||
# btschool 组
|
||||
{
|
||||
"domain": "btschool",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BtsCHOOL", "group": "BtsCHOOL"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BtsHD", "group": "BtsHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BtsPAD", "group": "BtsPAD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BtsTV", "group": "BtsTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Zone", "group": "Zone"}
|
||||
]
|
||||
},
|
||||
# carpt 组
|
||||
{
|
||||
"domain": "carpt",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CarPT", "group": "CarPT"}
|
||||
]
|
||||
},
|
||||
# chd 组
|
||||
{
|
||||
"domain": "chd",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CHD", "group": "CHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CHDBits", "group": "CHDBits"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CHDPAD", "group": "CHDPAD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CHDTV", "group": "CHDTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CHDHKTV", "group": "CHDHKTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CHDWEB", "group": "CHDWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-StBOX", "group": "StBOX"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-OneHD", "group": "OneHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Lee", "group": "Lee"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-xiaopie", "group": "xiaopie"}
|
||||
]
|
||||
},
|
||||
# eastgame 组
|
||||
{
|
||||
"domain": "eastgame",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-TLF", "group": "TLF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-iNT-TLF", "group": "iNT-TLF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HALFC-TLF", "group": "HALFC-TLF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MiniSD-TLF", "group": "MiniSD-TLF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MiniHD-TLF", "group": "MiniHD-TLF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MiniFHD-TLF", "group": "MiniFHD-TLF"}
|
||||
]
|
||||
},
|
||||
# gainbound 组
|
||||
{
|
||||
"domain": "gainbound",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-DGB", "group": "DGB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-GBWEB", "group": "GBWEB"}
|
||||
]
|
||||
},
|
||||
# hares 组
|
||||
{
|
||||
"domain": "hares",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Hares", "group": "Hares"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HaresMV", "group": "HaresMV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HaresTV", "group": "HaresTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HaresWeb", "group": "HaresWeb"}
|
||||
]
|
||||
},
|
||||
# hdarea 组
|
||||
{
|
||||
"domain": "hdarea",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDApad", "group": "HDApad"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDArea", "group": "HDArea"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDATV", "group": "HDATV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-EPiC", "group": "EPiC"}
|
||||
]
|
||||
},
|
||||
# hdchina 组
|
||||
{
|
||||
"domain": "hdchina",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDC", "group": "HDC"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDChina", "group": "HDChina"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDCTV", "group": "HDCTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-k9611", "group": "k9611"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-tudou", "group": "tudou"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-iHD", "group": "iHD"}
|
||||
]
|
||||
},
|
||||
# hddolby 组
|
||||
{
|
||||
"domain": "hddolby",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Dream", "group": "Dream"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-DBTV", "group": "DBTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDo", "group": "HDo"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-QHStudIo", "group": "QHStudIo"}
|
||||
]
|
||||
},
|
||||
# hdfans 组
|
||||
{
|
||||
"domain": "hdfans",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-beAst", "group": "beAst"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-beAstTV", "group": "beAstTV"}
|
||||
]
|
||||
},
|
||||
# hdhome 组
|
||||
{
|
||||
"domain": "hdhome",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDH", "group": "HDH"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDHome", "group": "HDHome"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDHPad", "group": "HDHPad"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDHTV", "group": "HDHTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDHWEB", "group": "HDHWEB"}
|
||||
]
|
||||
},
|
||||
# hdpt 组
|
||||
{
|
||||
"domain": "hdpt",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDPT", "group": "HDPT"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDPTWeb", "group": "HDPTWeb"}
|
||||
]
|
||||
},
|
||||
# hdsky 组
|
||||
{
|
||||
"domain": "hdsky",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDS", "group": "HDS"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDSky", "group": "HDSky"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDSTV", "group": "HDSTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDSPad", "group": "HDSPad"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDSWEB", "group": "HDSWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-AQLJ", "group": "AQLJ"}
|
||||
]
|
||||
},
|
||||
# hdzone 组
|
||||
{
|
||||
"domain": "hdzone",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDZ", "group": "HDZ"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HDZone", "group": "HDZone"}
|
||||
]
|
||||
},
|
||||
# hhanclub 组
|
||||
{
|
||||
"domain": "hhanclub",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HHWEB", "group": "HHWEB"}
|
||||
]
|
||||
},
|
||||
# htpt 组
|
||||
{
|
||||
"domain": "htpt",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HTPT", "group": "HTPT"}
|
||||
]
|
||||
},
|
||||
# keepfrds 组
|
||||
{
|
||||
"domain": "keepfrds",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FRDS", "group": "FRDS"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Yumi@FRDS", "group": "Yumi@FRDS"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-cXcY@FRDS", "group": "cXcY@FRDS"}
|
||||
]
|
||||
},
|
||||
# lemonhd 组
|
||||
{
|
||||
"domain": "lemonhd",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LeagueCD", "group": "LeagueCD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LeagueHD", "group": "LeagueHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LeagueMV", "group": "LeagueMV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LeagueTV", "group": "LeagueTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LeagueNF", "group": "LeagueNF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LeagueWEB", "group": "LeagueWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LHD", "group": "LHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-i18n", "group": "i18n"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CiNT", "group": "CiNT"}
|
||||
]
|
||||
},
|
||||
# mteam 组
|
||||
{
|
||||
"domain": "mteam",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MTeam", "group": "MTeam"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MTeamTV", "group": "MTeamTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MPAD", "group": "MPAD"}
|
||||
]
|
||||
},
|
||||
# ourbits 组
|
||||
{
|
||||
"domain": "ourbits",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-OurBits", "group": "OurBits"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-OurTV", "group": "OurTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FLTTH", "group": "FLTTH"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Ao", "group": "Ao"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PbK", "group": "PbK"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MGs", "group": "MGs"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-iLoveHD", "group": "iLoveHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-iLoveTV", "group": "iLoveTV"}
|
||||
]
|
||||
},
|
||||
# piggo 组
|
||||
{
|
||||
"domain": "piggo",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PiGoNF", "group": "PiGoNF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PiGoHB", "group": "PiGoHB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PiGoWEB", "group": "PiGoWEB"}
|
||||
]
|
||||
},
|
||||
# pterclub 组
|
||||
{
|
||||
"domain": "pterclub",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTer", "group": "PTer"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTerDIY", "group": "PTerDIY"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTerGame", "group": "PTerGame"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTerMV", "group": "PTerMV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTerTV", "group": "PTerTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTerWEB", "group": "PTerWEB"}
|
||||
]
|
||||
},
|
||||
# pthome 组
|
||||
{
|
||||
"domain": "pthome",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTH", "group": "PTH"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTHAudio", "group": "PTHAudio"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTHeBook", "group": "PTHeBook"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTHmusic", "group": "PTHmusic"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTHome", "group": "PTHome"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTHtv", "group": "PTHtv"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTHWEB", "group": "PTHWEB"}
|
||||
]
|
||||
},
|
||||
# ptsbao 组
|
||||
{
|
||||
"domain": "ptsbao",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PTsbao", "group": "PTsbao"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-OPS", "group": "OPS"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFansAIeNcE", "group": "FFansAIeNcE"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFansBD", "group": "FFansBD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFansDVD", "group": "FFansDVD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFansDIY", "group": "FFansDIY"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFansTV", "group": "FFansTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FFansWEB", "group": "FFansWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FHDMv", "group": "FHDMv"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SGXT", "group": "SGXT"}
|
||||
]
|
||||
},
|
||||
# putao 组
|
||||
{
|
||||
"domain": "putao",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PuTao", "group": "PuTao"}
|
||||
]
|
||||
},
|
||||
# ssd 组
|
||||
{
|
||||
"domain": "ssd",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CMCT", "group": "CMCT"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CMCT@制作者", "group": "CMCT"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CMCTV", "group": "CMCTV"}
|
||||
]
|
||||
},
|
||||
# sharkpt 组
|
||||
{
|
||||
"domain": "sharkpt",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Shark", "group": "Shark"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SharkWEB", "group": "SharkWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SharkDIY", "group": "SharkDIY"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SharkTV", "group": "SharkTV"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SharkMV", "group": "SharkMV"}
|
||||
]
|
||||
},
|
||||
# tjupt 组
|
||||
{
|
||||
"domain": "tjupt",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-TJUPT", "group": "TJUPT"}
|
||||
]
|
||||
},
|
||||
# ttg 组
|
||||
{
|
||||
"domain": "ttg",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-TTG", "group": "TTG"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-WiKi", "group": "WiKi"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-NGB", "group": "NGB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-DoA", "group": "DoA"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ARiN", "group": "ARiN"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ExREN", "group": "ExREN"}
|
||||
]
|
||||
},
|
||||
# others 组
|
||||
{
|
||||
"domain": "others",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BMDru", "group": "BMDru"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BeyondHD", "group": "BeyondHD"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-BTN", "group": "BTN"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Cfandora", "group": "Cfandora"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Ctrlhd", "group": "Ctrlhd"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-CMRG", "group": "CMRG"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-DON", "group": "DON"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-EVO", "group": "EVO"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FLUX", "group": "FLUX"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HONE", "group": "HONE"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HONEyG", "group": "HONEyG"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-NoGroup", "group": "NoGroup"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-NTb", "group": "NTb"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-NTG", "group": "NTG"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-PandaMoon", "group": "PandaMoon"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SMURF", "group": "SMURF"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-TEPES", "group": "TEPES"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Taengoo", "group": "Taengoo"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-TrollHD ", "group": "TrollHD "},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-UBWEB", "group": "UBWEB"}
|
||||
]
|
||||
},
|
||||
# anime 组
|
||||
{
|
||||
"domain": "anime",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-ANi", "group": "ANi"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-HYSUB", "group": "HYSUB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-KTXP", "group": "KTXP"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-LoliHouse", "group": "LoliHouse"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MCE", "group": "MCE"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Nekomoe kissaten", "group": "Nekomoe kissaten"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-SweetSub", "group": "SweetSub"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-MingY", "group": "MingY"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-Lilith-Raws", "group": "Lilith-Raws"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-NC-Raws", "group": "NC-Raws"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-织梦字幕组", "group": "织梦字幕组"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-枫叶字幕组", "group": "枫叶字幕组"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-猎户手抄部", "group": "猎户手抄部"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-喵萌奶茶屋", "group": "喵萌奶茶屋"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-漫猫字幕社", "group": "漫猫字幕社"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-霜庭云花Sub", "group": "霜庭云花Sub"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-北宇治字幕组", "group": "北宇治字幕组"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-氢气烤肉架", "group": "氢气烤肉架"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-云歌字幕组", "group": "云歌字幕组"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-萌樱字幕组", "group": "萌樱字幕组"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-极影字幕社", "group": "极影字幕社"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-悠哈璃羽字幕社", "group": "悠哈璃羽字幕社"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-❀拨雪寻春❀", "group": "❀拨雪寻春❀"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-沸羊羊制作", "group": "沸羊羊制作"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-沸羊羊字幕组", "group": "沸羊羊字幕组"},
|
||||
{
|
||||
"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-桜都字幕组",
|
||||
"group": "桜都字幕组",
|
||||
},
|
||||
{
|
||||
"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-樱都字幕组",
|
||||
"group": "樱都字幕组",
|
||||
},
|
||||
]
|
||||
},
|
||||
# frog 组
|
||||
{
|
||||
"domain": "frog",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FROG", "group": "FROG"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FROGE", "group": "FROGE"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-FROGWeb", "group": "FROGWeb"},
|
||||
]
|
||||
},
|
||||
# ubits 组
|
||||
{
|
||||
"domain": "ubits",
|
||||
"groups": [
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-UBits", "group": "UBits"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-UBWEB", "group": "UBWEB"},
|
||||
{"title": "Bluey S03 2021 2160p WEB-DL H.265 AAC 2.0-UBTV", "group": "UBTV"},
|
||||
]
|
||||
},
|
||||
]
|
||||
13
tests/test_release_group.py
Normal file
13
tests/test_release_group.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from unittest import TestCase
|
||||
from tests.cases.groups import release_group_cases
|
||||
from app.core.meta.releasegroup import ReleaseGroupsMatcher
|
||||
|
||||
class MetaInfoTest(TestCase):
|
||||
def test_release_group(self):
|
||||
for info in release_group_cases:
|
||||
print(f"开始测试 {info.get('domain')}")
|
||||
for item in info.get('groups', []):
|
||||
release_group = ReleaseGroupsMatcher().match(item.get("title"))
|
||||
print(f"\tmatch release group {release_group}, should be: {item.get('group')}")
|
||||
self.assertEqual(item.get("group"), release_group)
|
||||
print(f"完成 {info.get('domain')}")
|
||||
@@ -1,2 +1,2 @@
|
||||
APP_VERSION = 'v2.3.9'
|
||||
FRONTEND_VERSION = 'v2.3.9'
|
||||
APP_VERSION = 'v2.4.1'
|
||||
FRONTEND_VERSION = 'v2.4.1'
|
||||
|
||||
Reference in New Issue
Block a user