Merge remote-tracking branch 'origin/main'

This commit is contained in:
thsrite 2023-10-10 11:15:45 +08:00
commit 3bb6f8a0c0
27 changed files with 276 additions and 111 deletions

View File

@ -8,23 +8,20 @@ on:
- version.py - version.py
jobs: jobs:
build: Docker-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Build Docker Image name: Build Docker Image
steps: steps:
- - name: Checkout
name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- - name: Release version
name: Release version
id: release_version id: release_version
run: | run: |
app_version=$(cat version.py |sed -ne "s/APP_VERSION\s=\s'v\(.*\)'/\1/gp") app_version=$(cat version.py |sed -ne "s/APP_VERSION\s=\s'v\(.*\)'/\1/gp")
echo "app_version=$app_version" >> $GITHUB_ENV echo "app_version=$app_version" >> $GITHUB_ENV
- - name: Docker Meta
name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
with: with:
@ -33,23 +30,19 @@ jobs:
type=raw,value=${{ env.app_version }} type=raw,value=${{ env.app_version }}
type=raw,value=latest type=raw,value=latest
- - name: Set Up QEMU
name: Set Up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
- - name: Set Up Buildx
name: Set Up Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- - name: Login DockerHub
name: Login DockerHub
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- - name: Build Image
name: Build Image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
@ -64,3 +57,76 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha, scope=${{ github.workflow }} cache-from: type=gha, scope=${{ github.workflow }}
cache-to: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }}
Windows-build:
runs-on: windows-latest
name: Build Windows Binary
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Init Python 3.11.4
uses: actions/setup-python@v4
with:
python-version: '3.11.4'
- name: Install Dependent Packages
run: |
python -m pip install --upgrade pip
pip install wheel pyinstaller
pip install -r requirements.txt
shell: pwsh
- name: Pyinstaller
run: |
pyinstaller windows.spec
shell: pwsh
- name: Upload Windows File
uses: actions/upload-artifact@v3
with:
name: windows
path: dist/MoviePilot.exe
Create-release:
permissions: write-all
runs-on: ubuntu-latest
needs: [ Windows-build, Docker-build ]
steps:
- uses: actions/checkout@v2
- 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: Download Artifact
uses: actions/download-artifact@v3
- name: get release_informations
shell: bash
run: |
mkdir releases
mv ./windows/MoviePilot.exe ./releases/MoviePilot_v${{ env.app_version }}.exe
- name: Create Release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.app_version }}
release_name: v${{ env.app_version }}
body: ${{ github.event.commits[0].message }}
draft: false
prerelease: false
- name: Upload Release Asset
uses: dwenegar/upload-release-assets@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
release_id: ${{ steps.create_release.outputs.id }}
assets_path: |
./releases/

View File

@ -1,36 +0,0 @@
name: MoviePilot Release
on:
workflow_dispatch:
push:
branches:
- main
paths:
- version.py
jobs:
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: Generate Release
uses: actions/create-release@latest
with:
tag_name: v${{ env.app_version }}
release_name: v${{ env.app_version }}
body: ${{ github.event.commits[0].message }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,6 +1,7 @@
FROM python:3.11.4-slim-bullseye FROM python:3.11.4-slim-bullseye
ARG MOVIEPILOT_VERSION ARG MOVIEPILOT_VERSION
ENV LANG="C.UTF-8" \ ENV LANG="C.UTF-8" \
TZ="Asia/Shanghai" \
HOME="/moviepilot" \ HOME="/moviepilot" \
TERM="xterm" \ TERM="xterm" \
PUID=0 \ PUID=0 \

View File

@ -4,8 +4,6 @@
# 仅用于学习交流使用,请勿在任何国内平台宣传该项目! # 仅用于学习交流使用,请勿在任何国内平台宣传该项目!
Dockerhttps://hub.docker.com/r/jxxghp/moviepilot
发布频道https://t.me/moviepilot_channel 发布频道https://t.me/moviepilot_channel
## 主要特性 ## 主要特性
@ -33,11 +31,19 @@ MoviePilot需要配套下载器和媒体服务器配合使用。
### 4. **安装MoviePilot** ### 4. **安装MoviePilot**
目前仅提供docker镜像点击 [这里](https://hub.docker.com/r/jxxghp/moviepilot) 或执行命令: - Docker镜像
```shell 点击 [这里](https://hub.docker.com/r/jxxghp/moviepilot) 或执行命令:
docker pull jxxghp/moviepilot:latest
``` ```shell
docker pull jxxghp/moviepilot:latest
```
- Window
后端https://github.com/jxxghp/MoviePilot/releases
前端https://github.com/jxxghp/MoviePilot-Frontend/releases
## 配置 ## 配置

BIN
app.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

View File

@ -1,10 +1,13 @@
import os import os
import secrets import secrets
import sys
from pathlib import Path from pathlib import Path
from typing import List from typing import List
from pydantic import BaseSettings from pydantic import BaseSettings
from app.utils.system import SystemUtils
class Settings(BaseSettings): class Settings(BaseSettings):
# 项目名称 # 项目名称
@ -28,7 +31,7 @@ class Settings(BaseSettings):
# 是否开发模式 # 是否开发模式
DEV: bool = False DEV: bool = False
# 配置文件目录 # 配置文件目录
CONFIG_DIR: str = "/config" CONFIG_DIR: str = None
# 超级管理员 # 超级管理员
SUPERUSER: str = "admin" SUPERUSER: str = "admin"
# 超级管理员初始密码 # 超级管理员初始密码
@ -209,7 +212,11 @@ class Settings(BaseSettings):
def CONFIG_PATH(self): def CONFIG_PATH(self):
if self.CONFIG_DIR: if self.CONFIG_DIR:
return Path(self.CONFIG_DIR) return Path(self.CONFIG_DIR)
return self.INNER_CONFIG_PATH elif SystemUtils.is_docker():
return Path("/config")
elif SystemUtils.is_frozen():
return Path(sys.executable).parent / "config"
return self.ROOT_PATH / "config"
@property @property
def TEMP_PATH(self): def TEMP_PATH(self):
@ -274,6 +281,9 @@ class Settings(BaseSettings):
with self.CONFIG_PATH as p: with self.CONFIG_PATH as p:
if not p.exists(): if not p.exists():
p.mkdir(parents=True, exist_ok=True) p.mkdir(parents=True, exist_ok=True)
if SystemUtils.is_frozen():
if not (p / "app.env").exists():
SystemUtils.copy(self.INNER_CONFIG_PATH / "app.env", p / "app.env")
with self.TEMP_PATH as p: with self.TEMP_PATH as p:
if not p.exists(): if not p.exists():
p.mkdir(parents=True, exist_ok=True) p.mkdir(parents=True, exist_ok=True)
@ -286,6 +296,6 @@ class Settings(BaseSettings):
settings = Settings( settings = Settings(
_env_file=Path(os.environ.get("CONFIG_DIR", "/config")) / "app.env", _env_file=Settings().CONFIG_PATH / "app.env",
_env_file_encoding="utf-8" _env_file_encoding="utf-8"
) )

View File

@ -39,7 +39,7 @@ def update_db():
更新数据库 更新数据库
""" """
db_location = settings.CONFIG_PATH / 'user.db' db_location = settings.CONFIG_PATH / 'user.db'
script_location = settings.ROOT_PATH / 'alembic' script_location = settings.ROOT_PATH / 'database'
try: try:
alembic_cfg = Config() alembic_cfg = Config()
alembic_cfg.set_main_option('script_location', str(script_location)) alembic_cfg.set_main_option('script_location', str(script_location))

View File

@ -2,12 +2,15 @@ from pyvirtualdisplay import Display
from app.log import logger from app.log import logger
from app.utils.singleton import Singleton from app.utils.singleton import Singleton
from app.utils.system import SystemUtils
class DisplayHelper(metaclass=Singleton): class DisplayHelper(metaclass=Singleton):
_display: Display = None _display: Display = None
def __init__(self): def __init__(self):
if not SystemUtils.is_docker():
return
try: try:
self._display = Display(visible=False, size=(1024, 768)) self._display = Display(visible=False, size=(1024, 768))
self._display.start() self._display.start()

View File

@ -22,7 +22,7 @@ class InvitesSignin(_PluginBase):
# 插件图标 # 插件图标
plugin_icon = "invites.png" plugin_icon = "invites.png"
# 主题色 # 主题色
plugin_color = "#4FB647" plugin_color = "#FFFFFF"
# 插件版本 # 插件版本
plugin_version = "1.0" plugin_version = "1.0"
# 插件作者 # 插件作者

View File

@ -109,7 +109,7 @@ class IYUUAutoSeed(_PluginBase):
self._nolabels = config.get("nolabels") self._nolabels = config.get("nolabels")
self._nopaths = config.get("nopaths") self._nopaths = config.get("nopaths")
self._clearcache = config.get("clearcache") self._clearcache = config.get("clearcache")
self._permanent_error_caches = config.get("permanent_error_caches") or [] self._permanent_error_caches = [] if self._clearcache else config.get("permanent_error_caches") or []
self._error_caches = [] if self._clearcache else config.get("error_caches") or [] self._error_caches = [] if self._clearcache else config.get("error_caches") or []
self._success_caches = [] if self._clearcache else config.get("success_caches") or [] self._success_caches = [] if self._clearcache else config.get("success_caches") or []

View File

@ -3,6 +3,7 @@ import os
import platform import platform
import re import re
import shutil import shutil
import sys
from pathlib import Path from pathlib import Path
from typing import List, Union, Tuple from typing import List, Union, Tuple
@ -27,20 +28,39 @@ class SystemUtils:
@staticmethod @staticmethod
def is_docker() -> bool: def is_docker() -> bool:
"""
判断是否为Docker环境
"""
return Path("/.dockerenv").exists() return Path("/.dockerenv").exists()
@staticmethod @staticmethod
def is_synology() -> bool: def is_synology() -> bool:
"""
判断是否为群晖系统
"""
if SystemUtils.is_windows(): if SystemUtils.is_windows():
return False return False
return True if "synology" in SystemUtils.execute('uname -a') else False return True if "synology" in SystemUtils.execute('uname -a') else False
@staticmethod @staticmethod
def is_windows() -> bool: def is_windows() -> bool:
"""
判断是否为Windows系统
"""
return True if os.name == "nt" else False return True if os.name == "nt" else False
@staticmethod
def is_frozen() -> bool:
"""
判断是否为冻结的二进制文件
"""
return True if getattr(sys, 'frozen', False) else False
@staticmethod @staticmethod
def is_macos() -> bool: def is_macos() -> bool:
"""
判断是否为MacOS系统
"""
return True if platform.system() == 'Darwin' else False return True if platform.system() == 'Darwin' else False
@staticmethod @staticmethod
@ -77,7 +97,7 @@ class SystemUtils:
""" """
try: try:
# link到当前目录并改名 # link到当前目录并改名
tmp_path = (src.parent / dest.name).with_suffix(".mp") tmp_path = src.parent / (dest.name + ".mp")
tmp_path.hardlink_to(src) tmp_path.hardlink_to(src)
# 移动到目标目录 # 移动到目标目录
shutil.move(tmp_path, dest) shutil.move(tmp_path, dest)

View File

@ -3,10 +3,8 @@
####################################################################### #######################################################################
#################################### ####################################
# 系统设置 # # 基础设置 #
#################################### ####################################
# 时区
TZ=Asia/Shanghai
# 【*】API监听地址 # 【*】API监听地址
HOST=0.0.0.0 HOST=0.0.0.0
# 是否调试模式 # 是否调试模式
@ -21,6 +19,10 @@ SUPERUSER_PASSWORD=password
API_TOKEN=moviepilot API_TOKEN=moviepilot
# 网络代理 IP:PORT # 网络代理 IP:PORT
PROXY_HOST= PROXY_HOST=
# TMDB图片地址无需修改需保留默认值
TMDB_IMAGE_DOMAIN=image.tmdb.org
# TMDB API地址无需修改需保留默认值
TMDB_API_DOMAIN=api.themoviedb.org
# 大内存模式 # 大内存模式
BIG_MEMORY_MODE=false BIG_MEMORY_MODE=false
@ -35,13 +37,45 @@ SCRAP_METADATA=true
SCRAP_FOLLOW_TMDB=true SCRAP_FOLLOW_TMDB=true
# 刮削来源 themoviedb/douban # 刮削来源 themoviedb/douban
SCRAP_SOURCE=themoviedb SCRAP_SOURCE=themoviedb
# TMDB图片地址无需修改需保留默认值
TMDB_IMAGE_DOMAIN=image.tmdb.org
# TMDB API地址无需修改需保留默认值
TMDB_API_DOMAIN=api.themoviedb.org
#################################### ####################################
# 订阅&搜索 # # 媒体库 #
####################################
# 【*】转移方式 link/copy/move/softlink
TRANSFER_TYPE=copy
# 【*】媒体库目录,多个目录使用,分隔
LIBRARY_PATH=
# 电影媒体库目录名,默认电影
LIBRARY_MOVIE_NAME=
# 电视剧媒体库目录名,默认电视剧
LIBRARY_TV_NAME=
# 动漫媒体库目录名,默认电视剧/动漫
LIBRARY_ANIME_NAME=
# 二级分类
LIBRARY_CATEGORY=true
# 电影重命名格式
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={{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}}{% endif %}{{fileExt}}
####################################
# 站点 #
####################################
# 【*】CookieCloud服务器地址默认为公共服务器
COOKIECLOUD_HOST=https://movie-pilot.org/cookiecloud
# 【*】CookieCloud用户KEY
COOKIECLOUD_KEY=
# 【*】CookieCloud端对端加密密码
COOKIECLOUD_PASSWORD=
# 【*】CookieCloud同步间隔分钟
COOKIECLOUD_INTERVAL=1440
# OCR服务器地址
OCR_HOST=https://movie-pilot.org
# 【*】CookieCloud对应的浏览器UA
USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.57
####################################
# 订阅 & 搜索 #
#################################### ####################################
# 订阅模式 spider/rss # 订阅模式 spider/rss
SUBSCRIBE_MODE=spider SUBSCRIBE_MODE=spider
@ -149,39 +183,3 @@ JELLYFIN_API_KEY=
PLEX_HOST= PLEX_HOST=
# Plex Token # Plex Token
PLEX_TOKEN= PLEX_TOKEN=
####################################
# 站点 #
####################################
# 【*】CookieCloud服务器地址默认为公共服务器
COOKIECLOUD_HOST=https://movie-pilot.org/cookiecloud
# 【*】CookieCloud用户KEY
COOKIECLOUD_KEY=
# 【*】CookieCloud端对端加密密码
COOKIECLOUD_PASSWORD=
# 【*】CookieCloud同步间隔分钟
COOKIECLOUD_INTERVAL=1440
# OCR服务器地址
OCR_HOST=https://movie-pilot.org
# 【*】CookieCloud对应的浏览器UA
USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.57
####################################
# 媒体库 #
####################################
# 【*】转移方式 link/copy/move/softlink
TRANSFER_TYPE=copy
# 【*】媒体库目录,多个目录使用,分隔
LIBRARY_PATH=
# 电影媒体库目录名,默认电影
LIBRARY_MOVIE_NAME=
# 电视剧媒体库目录名,默认电视剧
LIBRARY_TV_NAME=
# 动漫媒体库目录名,默认电视剧/动漫
LIBRARY_ANIME_NAME=
# 二级分类
LIBRARY_CATEGORY=true
# 电影重命名格式
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={{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}}{% endif %}{{fileExt}}

View File

@ -12,7 +12,7 @@ for module in Path(__file__).with_name("models").glob("*.py"):
db_version = input("请输入版本号:") db_version = input("请输入版本号:")
db_location = settings.CONFIG_PATH / 'user.db' db_location = settings.CONFIG_PATH / 'user.db'
script_location = settings.ROOT_PATH / 'alembic' script_location = settings.ROOT_PATH / 'database'
alembic_cfg = AlembicConfig() alembic_cfg = AlembicConfig()
alembic_cfg.set_main_option('script_location', str(script_location)) alembic_cfg.set_main_option('script_location', str(script_location))
alembic_cfg.set_main_option('sqlalchemy.url', f"sqlite:///{db_location}") alembic_cfg.set_main_option('sqlalchemy.url', f"sqlite:///{db_location}")

View File

@ -1 +1 @@
APP_VERSION = 'v1.2.9' APP_VERSION = 'v1.3.0'

97
windows.spec Normal file
View File

@ -0,0 +1,97 @@
# -*- mode: python ; coding: utf-8 -*-
def collect_pkg_data(package, include_py_files=False, subdir=None):
"""
Collect all data files from the given package.
"""
import os
from PyInstaller.utils.hooks import get_package_paths, remove_prefix, PY_IGNORE_EXTENSIONS
# Accept only strings as packages.
if type(package) is not str:
raise ValueError
pkg_base, pkg_dir = get_package_paths(package)
if subdir:
pkg_dir = os.path.join(pkg_dir, subdir)
# Walk through all file in the given package, looking for data files.
data_toc = TOC()
for dir_path, dir_names, files in os.walk(pkg_dir):
for f in files:
extension = os.path.splitext(f)[1]
if include_py_files or (extension not in PY_IGNORE_EXTENSIONS):
source_file = os.path.join(dir_path, f)
dest_folder = remove_prefix(dir_path, os.path.dirname(pkg_base) + os.sep)
dest_file = os.path.join(dest_folder, f)
data_toc.append((dest_file, source_file, 'DATA'))
return data_toc
def collect_local_submodules(package):
"""
Collect all local submodules from the given package.
"""
import os
base_dir = '..'
package_dir = os.path.join(base_dir, package.replace('.', os.sep))
submodules = []
for dir_path, dir_names, files in os.walk(package_dir):
for f in files:
if f == '__init__.py':
submodules.append(f"{package}.{os.path.basename(dir_path)}")
elif f.endswith('.py'):
submodules.append(f"{package}.{os.path.basename(dir_path)}.{os.path.splitext(f)[0]}")
for d in dir_names:
submodules.append(f"{package}.{os.path.basename(dir_path)}.{d}")
return submodules
hiddenimports = [
'passlib.handlers.bcrypt',
'app.modules',
'app.plugins',
] + collect_local_submodules('app.modules') \
+ collect_local_submodules('app.plugins')
block_cipher = None
a = Analysis(
['app/main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
collect_pkg_data('config'),
collect_pkg_data('cf_clearance'),
collect_pkg_data('database', include_py_files=True),
[],
name='MoviePilot',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon="app.ico"
)