windows package
This commit is contained in:
parent
e15f5ab93e
commit
97f16289c9
36
.github/workflows/build-windows.yml
vendored
Normal file
36
.github/workflows/build-windows.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: MoviePilot Windows Builder
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- version.py
|
||||
- .github/workflows/build-windows.yml
|
||||
|
||||
jobs:
|
||||
Windows-build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- 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 numpy==1.23.5 pyparsing==3.0.9 wxpython==4.2.0 pyinstaller==5.7.0
|
||||
git clone --depth=1 -b main https://github.com/jxxghp/MoviePilot
|
||||
cd MoviePilot
|
||||
pip install -r requirements.txt
|
||||
shell: pwsh
|
||||
- name: Pyinstaller
|
||||
run: |
|
||||
cd MoviePilot
|
||||
pyinstaller windows.spec
|
||||
shell: pwsh
|
||||
- name: Upload Windows File
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows
|
||||
path: MoviePilot/dist/MoviePilot.exe
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: MoviePilot Builder
|
||||
name: MoviePilot Docker Builder
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
|
@ -1,10 +1,13 @@
|
||||
import os
|
||||
import secrets
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseSettings
|
||||
|
||||
from app.utils.system import SystemUtils
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# 项目名称
|
||||
@ -28,7 +31,7 @@ class Settings(BaseSettings):
|
||||
# 是否开发模式
|
||||
DEV: bool = False
|
||||
# 配置文件目录
|
||||
CONFIG_DIR: str = "/config"
|
||||
CONFIG_DIR: str = None
|
||||
# 超级管理员
|
||||
SUPERUSER: str = "admin"
|
||||
# 超级管理员初始密码
|
||||
@ -209,7 +212,11 @@ class Settings(BaseSettings):
|
||||
def CONFIG_PATH(self):
|
||||
if 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
|
||||
def TEMP_PATH(self):
|
||||
@ -274,6 +281,9 @@ class Settings(BaseSettings):
|
||||
with self.CONFIG_PATH as p:
|
||||
if not p.exists():
|
||||
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:
|
||||
if not p.exists():
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
|
@ -39,7 +39,7 @@ def update_db():
|
||||
更新数据库
|
||||
"""
|
||||
db_location = settings.CONFIG_PATH / 'user.db'
|
||||
script_location = settings.ROOT_PATH / 'alembic'
|
||||
script_location = settings.ROOT_PATH / 'database'
|
||||
try:
|
||||
alembic_cfg = Config()
|
||||
alembic_cfg.set_main_option('script_location', str(script_location))
|
||||
|
@ -2,12 +2,15 @@ from pyvirtualdisplay import Display
|
||||
|
||||
from app.log import logger
|
||||
from app.utils.singleton import Singleton
|
||||
from app.utils.system import SystemUtils
|
||||
|
||||
|
||||
class DisplayHelper(metaclass=Singleton):
|
||||
_display: Display = None
|
||||
|
||||
def __init__(self):
|
||||
if not SystemUtils.is_docker():
|
||||
return
|
||||
try:
|
||||
self._display = Display(visible=False, size=(1024, 768))
|
||||
self._display.start()
|
||||
|
@ -3,6 +3,7 @@ import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Union, Tuple
|
||||
|
||||
@ -27,20 +28,39 @@ class SystemUtils:
|
||||
|
||||
@staticmethod
|
||||
def is_docker() -> bool:
|
||||
"""
|
||||
判断是否为Docker环境
|
||||
"""
|
||||
return Path("/.dockerenv").exists()
|
||||
|
||||
@staticmethod
|
||||
def is_synology() -> bool:
|
||||
"""
|
||||
判断是否为群晖系统
|
||||
"""
|
||||
if SystemUtils.is_windows():
|
||||
return False
|
||||
return True if "synology" in SystemUtils.execute('uname -a') else False
|
||||
|
||||
@staticmethod
|
||||
def is_windows() -> bool:
|
||||
"""
|
||||
判断是否为Windows系统
|
||||
"""
|
||||
return True if os.name == "nt" else False
|
||||
|
||||
@staticmethod
|
||||
def is_frozen() -> bool:
|
||||
"""
|
||||
判断是否为冻结的二进制文件
|
||||
"""
|
||||
return True if getattr(sys, 'frozen', False) else False
|
||||
|
||||
@staticmethod
|
||||
def is_macos() -> bool:
|
||||
"""
|
||||
判断是否为MacOS系统
|
||||
"""
|
||||
return True if platform.system() == 'Darwin' else False
|
||||
|
||||
@staticmethod
|
||||
|
@ -3,7 +3,7 @@
|
||||
#######################################################################
|
||||
|
||||
####################################
|
||||
# 系统设置 #
|
||||
# 基础设置 #
|
||||
####################################
|
||||
# 时区
|
||||
TZ=Asia/Shanghai
|
||||
@ -21,6 +21,10 @@ SUPERUSER_PASSWORD=password
|
||||
API_TOKEN=moviepilot
|
||||
# 网络代理 IP:PORT
|
||||
PROXY_HOST=
|
||||
# TMDB图片地址,无需修改需保留默认值
|
||||
TMDB_IMAGE_DOMAIN=image.tmdb.org
|
||||
# TMDB API地址,无需修改需保留默认值
|
||||
TMDB_API_DOMAIN=api.themoviedb.org
|
||||
# 大内存模式
|
||||
BIG_MEMORY_MODE=false
|
||||
|
||||
@ -35,13 +39,45 @@ SCRAP_METADATA=true
|
||||
SCRAP_FOLLOW_TMDB=true
|
||||
# 刮削来源 themoviedb/douban
|
||||
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
|
||||
SUBSCRIBE_MODE=spider
|
||||
@ -149,39 +185,3 @@ JELLYFIN_API_KEY=
|
||||
PLEX_HOST=
|
||||
# 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}}
|
||||
|
@ -12,7 +12,7 @@ for module in Path(__file__).with_name("models").glob("*.py"):
|
||||
|
||||
db_version = input("请输入版本号:")
|
||||
db_location = settings.CONFIG_PATH / 'user.db'
|
||||
script_location = settings.ROOT_PATH / 'alembic'
|
||||
script_location = settings.ROOT_PATH / 'database'
|
||||
alembic_cfg = AlembicConfig()
|
||||
alembic_cfg.set_main_option('script_location', str(script_location))
|
||||
alembic_cfg.set_main_option('sqlalchemy.url', f"sqlite:///{db_location}")
|
101
windows.spec
Normal file
101
windows.spec
Normal file
@ -0,0 +1,101 @@
|
||||
# -*- 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')
|
||||
|
||||
a = Analysis(
|
||||
['app/main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='MoviePilot',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon="app.ico"
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
collect_pkg_data('config'),
|
||||
collect_pkg_data('cf_clearance'),
|
||||
collect_pkg_data('database', include_py_files=True),
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='MoviePilot',
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user