mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-14 10:14:36 +08:00
* wip(v3): 移植监控与整理韧性修复到 v3 基线 包含:监控看门狗隔离/挂载探测、整理队列持久化、文件系统子进程代理、 写入原子化。迁移重挂到 v3 链 8a4c7e1d2f90 -> 7f5c1d2e3a4b -> e3d9f4b7c806。 tmdb 相关测试尚未通过,待定位。 * fix(v3): 修正移植引入的 16 项测试失败 - poller.py:合并时我方保留的行仍用旧变量名 merged_snapshot,而 v3 已统一 改名为 current_snapshot,导致 NameError 被外层 except 吞掉、快照从未保存 - smb.py:采纳 f-string 拆分写法,恢复 Python 3.11 可解析 - dispatcher 测试:历史查重由 _should_skip_by_history 统一承担,mock 点随之调整 - tmdb 缓存测试:补充 v3 新增的 media_source/media_id 字段 - tmdb 重试测试:为 fake 补充 match_multi/async_match_multi 尚余 3 项与 v3 识别流程的连接失败处理有关,待单独判断。 * fix(v3): 测试适配 v3 的 media_source/media_id 重构 v3 将媒体标识从 tmdbid 统一重构为 media_source + media_id,recognize_media 的 tmdbid 参数已被 **kwargs 静默吞掉——传了也不生效,流程会误降级到名称搜索。 tmdb 重试用例改用新参数后恢复正确路径。 同时修正 fake 的 match_multi 语义:真实实现(tmdbapi.match_multi)吞掉所有 异常并返回 None,连接失败与「未找到」在该路径上本就不可区分,fake 需保持一致。 至此移植引入的 19 项失败全部清零。 --------- Co-authored-by: Aqr-K <Aqr-K@users.noreply.github.com>
171 lines
6.1 KiB
Docker
171 lines
6.1 KiB
Docker
FROM python:3.12.13-slim-bookworm AS base
|
||
|
||
|
||
# 准备软件包
|
||
FROM base AS prepare_package
|
||
|
||
ENV LANG="C.UTF-8" \
|
||
TZ="Asia/Shanghai" \
|
||
HOME="/moviepilot" \
|
||
CONFIG_DIR="/config" \
|
||
TERM="xterm" \
|
||
DISPLAY=:987 \
|
||
PUID=0 \
|
||
PGID=0 \
|
||
UMASK=000 \
|
||
VENV_PATH="/opt/venv"
|
||
|
||
ENV PATH="${VENV_PATH}/bin:${PATH}"
|
||
|
||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||
nginx \
|
||
gettext-base \
|
||
locales \
|
||
procps \
|
||
gosu \
|
||
bash \
|
||
ca-certificates \
|
||
curl \
|
||
wget \
|
||
git \
|
||
gh \
|
||
busybox \
|
||
tini \
|
||
cron \
|
||
jq \
|
||
ripgrep \
|
||
less \
|
||
unzip \
|
||
fuse3 \
|
||
rsync \
|
||
openssh-client \
|
||
sshpass \
|
||
iproute2 \
|
||
netcat-openbsd \
|
||
lsof \
|
||
nano \
|
||
unar \
|
||
libchromaprint-tools \
|
||
libjemalloc2 \
|
||
&& dpkg-reconfigure --frontend noninteractive tzdata \
|
||
&& curl https://rclone.org/install.sh | bash \
|
||
&& ln -s /usr/lib/*-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
|
||
&& apt-get autoremove -y \
|
||
&& apt-get clean \
|
||
&& rm -rf \
|
||
/tmp/* \
|
||
/var/lib/apt/lists/* \
|
||
/var/tmp/*
|
||
|
||
|
||
# 准备 python 环境
|
||
FROM base AS prepare_venv
|
||
|
||
# 设置环境变量
|
||
ENV LANG="C.UTF-8" \
|
||
TZ="Asia/Shanghai" \
|
||
VENV_PATH="/opt/venv"
|
||
|
||
ENV PATH="${VENV_PATH}/bin:${PATH}"
|
||
|
||
# 安装系统构建依赖
|
||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||
build-essential \
|
||
curl \
|
||
busybox \
|
||
jq \
|
||
wget
|
||
|
||
# 安装 Python 构建依赖并创建虚拟环境
|
||
WORKDIR /app
|
||
COPY requirements.in requirements.in
|
||
COPY scripts/uv-pip-compat.sh /usr/local/bin/uv-pip-compat
|
||
RUN python3 -m venv ${VENV_PATH} \
|
||
&& env UV_INSTALL_DIR=/usr/local/bin sh -c "$(curl -LsSf https://astral.sh/uv/install.sh)" \
|
||
&& chmod +x /usr/local/bin/uv-pip-compat \
|
||
&& ln -sf /usr/local/bin/uv ${VENV_PATH}/bin/uv \
|
||
&& ln -sf /usr/local/bin/uv-pip-compat ${VENV_PATH}/bin/pip \
|
||
&& ln -sf /usr/local/bin/uv-pip-compat ${VENV_PATH}/bin/pip3 \
|
||
&& ln -sf /usr/local/bin/uv-pip-compat ${VENV_PATH}/bin/pip3.12 \
|
||
&& ln -sf /usr/local/bin/uv-pip-compat ${VENV_PATH}/bin/pip-compile \
|
||
&& ln -sf /usr/local/bin/uv-pip-compat ${VENV_PATH}/bin/pip-sync \
|
||
&& pip-compile requirements.in -o requirements.txt \
|
||
&& pip install -r requirements.txt
|
||
|
||
# 下载准备代码
|
||
FROM prepare_package AS prepare_code
|
||
|
||
WORKDIR /app
|
||
|
||
COPY . .
|
||
# 构建上下文中存在 frontend-dist/ 时优先使用本地前端产物(fork 自有前端),
|
||
# 否则回退下载官方 release 的 dist.zip
|
||
RUN if [ -d /app/frontend-dist ]; then \
|
||
mv /app/frontend-dist /public; \
|
||
else \
|
||
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; \
|
||
fi \
|
||
&& 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 -fsSL "https://raw.githubusercontent.com/jxxghp/MoviePilot-Resources/main/resources.v3/user.sites.v3.bin" -o /app/app/helper/user.sites.v3.bin \
|
||
&& python_ver=$(python3 -c 'import sys; print(f"cpython-{sys.version_info.major}{sys.version_info.minor}")') \
|
||
&& ARCH=$(uname -m) \
|
||
&& if [ "$ARCH" = "aarch64" ]; then SUFFIX="aarch64-linux-gnu"; else SUFFIX="x86_64-linux-gnu"; fi \
|
||
&& curl -fsSL "https://raw.githubusercontent.com/jxxghp/MoviePilot-Resources/main/resources.v3/sites.${python_ver}-${SUFFIX}.so" -o "/app/app/helper/sites.${python_ver}-${SUFFIX}.so"
|
||
|
||
# final 阶段: 安装运行时依赖和配置最终镜像
|
||
FROM prepare_package AS final
|
||
|
||
ENV LD_PRELOAD="/usr/local/lib/libjemalloc.so" \
|
||
MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE="true"
|
||
|
||
# 引入支持 amr 编码的静态 ffmpeg
|
||
COPY --from=mwader/static-ffmpeg:8.1.1 /ffmpeg /usr/local/bin/
|
||
COPY --from=mwader/static-ffmpeg:8.1.1 /ffprobe /usr/local/bin/
|
||
|
||
# python 环境
|
||
COPY --from=prepare_venv --chmod=777 ${VENV_PATH} ${VENV_PATH}
|
||
COPY --from=prepare_venv /usr/local/bin/uv /usr/local/bin/uv
|
||
COPY --from=prepare_venv /usr/local/bin/uv-pip-compat /usr/local/bin/uv-pip-compat
|
||
|
||
# 浏览器运行依赖
|
||
RUN playwright install-deps chromium \
|
||
&& apt-get autoremove -y \
|
||
&& apt-get clean \
|
||
&& rm -rf \
|
||
/tmp/* \
|
||
/var/lib/apt/lists/* \
|
||
/var/tmp/*
|
||
|
||
# 准备运行代码
|
||
WORKDIR /app
|
||
|
||
COPY --from=prepare_code /app /app
|
||
COPY --from=prepare_code /public /public
|
||
|
||
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 \
|
||
&& printf '%s\n' '#!/usr/bin/env bash' 'set -euo pipefail' 'cd /app' 'exec "${VENV_PATH:-/opt/venv}/bin/python3" -m app.cli "$@"' > /usr/local/bin/moviepilot \
|
||
&& chmod +x /entrypoint.sh /usr/local/bin/mp_update.sh /usr/local/bin/moviepilot \
|
||
&& 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/" > ${VENV_PATH}/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 \
|
||
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
||
&& locale-gen zh_CN.UTF-8
|
||
|
||
EXPOSE 3000
|
||
VOLUME [ "${CONFIG_DIR}" ]
|
||
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 CMD curl -fsS "http://127.0.0.1:${PORT:-3001}/api/v1/system/global?token=moviepilot" >/dev/null || exit 1
|
||
ENTRYPOINT [ "/usr/bin/tini", "-g", "--", "/entrypoint.sh" ]
|