Skip to content

Commit 97f1628

Browse files
committed
windows package
1 parent e15f5ab commit 97f1628

22 files changed

+217
-47
lines changed

.github/workflows/build-windows.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: MoviePilot Windows Builder
2+
on:
3+
workflow_dispatch:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- version.py
9+
- .github/workflows/build-windows.yml
10+
11+
jobs:
12+
Windows-build:
13+
runs-on: windows-latest
14+
steps:
15+
- name: Init Python 3.11.4
16+
uses: actions/setup-python@v4
17+
with:
18+
python-version: '3.11.4'
19+
- name: Install Dependent Packages
20+
run: |
21+
python -m pip install --upgrade pip
22+
pip install wheel numpy==1.23.5 pyparsing==3.0.9 wxpython==4.2.0 pyinstaller==5.7.0
23+
git clone --depth=1 -b main https://github.com/jxxghp/MoviePilot
24+
cd MoviePilot
25+
pip install -r requirements.txt
26+
shell: pwsh
27+
- name: Pyinstaller
28+
run: |
29+
cd MoviePilot
30+
pyinstaller windows.spec
31+
shell: pwsh
32+
- name: Upload Windows File
33+
uses: actions/upload-artifact@v3
34+
with:
35+
name: windows
36+
path: MoviePilot/dist/MoviePilot.exe

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: MoviePilot Builder
1+
name: MoviePilot Docker Builder
22
on:
33
workflow_dispatch:
44
push:

app.ico

174 KB
Binary file not shown.

app/core/config.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import os
22
import secrets
3+
import sys
34
from pathlib import Path
45
from typing import List
56

67
from pydantic import BaseSettings
78

9+
from app.utils.system import SystemUtils
10+
811

912
class Settings(BaseSettings):
1013
# 项目名称
@@ -28,7 +31,7 @@ class Settings(BaseSettings):
2831
# 是否开发模式
2932
DEV: bool = False
3033
# 配置文件目录
31-
CONFIG_DIR: str = "/config"
34+
CONFIG_DIR: str = None
3235
# 超级管理员
3336
SUPERUSER: str = "admin"
3437
# 超级管理员初始密码
@@ -209,7 +212,11 @@ def INNER_CONFIG_PATH(self):
209212
def CONFIG_PATH(self):
210213
if self.CONFIG_DIR:
211214
return Path(self.CONFIG_DIR)
212-
return self.INNER_CONFIG_PATH
215+
elif SystemUtils.is_docker():
216+
return Path("/config")
217+
elif SystemUtils.is_frozen():
218+
return Path(sys.executable).parent / "config"
219+
return self.ROOT_PATH / "config"
213220

214221
@property
215222
def TEMP_PATH(self):
@@ -274,6 +281,9 @@ def __init__(self, **kwargs):
274281
with self.CONFIG_PATH as p:
275282
if not p.exists():
276283
p.mkdir(parents=True, exist_ok=True)
284+
if SystemUtils.is_frozen():
285+
if not (p / "app.env").exists():
286+
SystemUtils.copy(self.INNER_CONFIG_PATH / "app.env", p / "app.env")
277287
with self.TEMP_PATH as p:
278288
if not p.exists():
279289
p.mkdir(parents=True, exist_ok=True)

app/db/init.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def update_db():
3939
更新数据库
4040
"""
4141
db_location = settings.CONFIG_PATH / 'user.db'
42-
script_location = settings.ROOT_PATH / 'alembic'
42+
script_location = settings.ROOT_PATH / 'database'
4343
try:
4444
alembic_cfg = Config()
4545
alembic_cfg.set_main_option('script_location', str(script_location))

app/helper/display.py

+3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
from app.log import logger
44
from app.utils.singleton import Singleton
5+
from app.utils.system import SystemUtils
56

67

78
class DisplayHelper(metaclass=Singleton):
89
_display: Display = None
910

1011
def __init__(self):
12+
if not SystemUtils.is_docker():
13+
return
1114
try:
1215
self._display = Display(visible=False, size=(1024, 768))
1316
self._display.start()

app/utils/system.py

+20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import platform
44
import re
55
import shutil
6+
import sys
67
from pathlib import Path
78
from typing import List, Union, Tuple
89

@@ -27,20 +28,39 @@ def execute(cmd: str) -> str:
2728

2829
@staticmethod
2930
def is_docker() -> bool:
31+
"""
32+
判断是否为Docker环境
33+
"""
3034
return Path("/.dockerenv").exists()
3135

3236
@staticmethod
3337
def is_synology() -> bool:
38+
"""
39+
判断是否为群晖系统
40+
"""
3441
if SystemUtils.is_windows():
3542
return False
3643
return True if "synology" in SystemUtils.execute('uname -a') else False
3744

3845
@staticmethod
3946
def is_windows() -> bool:
47+
"""
48+
判断是否为Windows系统
49+
"""
4050
return True if os.name == "nt" else False
4151

52+
@staticmethod
53+
def is_frozen() -> bool:
54+
"""
55+
判断是否为冻结的二进制文件
56+
"""
57+
return True if getattr(sys, 'frozen', False) else False
58+
4259
@staticmethod
4360
def is_macos() -> bool:
61+
"""
62+
判断是否为MacOS系统
63+
"""
4464
return True if platform.system() == 'Darwin' else False
4565

4666
@staticmethod

config/app.env

+42-42
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#######################################################################
44

55
####################################
6-
# 系统设置 #
6+
# 基础设置 #
77
####################################
88
# 时区
99
TZ=Asia/Shanghai
@@ -21,6 +21,10 @@ SUPERUSER_PASSWORD=password
2121
API_TOKEN=moviepilot
2222
# 网络代理 IP:PORT
2323
PROXY_HOST=
24+
# TMDB图片地址,无需修改需保留默认值
25+
TMDB_IMAGE_DOMAIN=image.tmdb.org
26+
# TMDB API地址,无需修改需保留默认值
27+
TMDB_API_DOMAIN=api.themoviedb.org
2428
# 大内存模式
2529
BIG_MEMORY_MODE=false
2630

@@ -35,13 +39,45 @@ SCRAP_METADATA=true
3539
SCRAP_FOLLOW_TMDB=true
3640
# 刮削来源 themoviedb/douban
3741
SCRAP_SOURCE=themoviedb
38-
# TMDB图片地址,无需修改需保留默认值
39-
TMDB_IMAGE_DOMAIN=image.tmdb.org
40-
# TMDB API地址,无需修改需保留默认值
41-
TMDB_API_DOMAIN=api.themoviedb.org
4242

4343
####################################
44-
# 订阅&搜索 #
44+
# 媒体库 #
45+
####################################
46+
# 【*】转移方式 link/copy/move/softlink
47+
TRANSFER_TYPE=copy
48+
# 【*】媒体库目录,多个目录使用,分隔
49+
LIBRARY_PATH=
50+
# 电影媒体库目录名,默认电影
51+
LIBRARY_MOVIE_NAME=
52+
# 电视剧媒体库目录名,默认电视剧
53+
LIBRARY_TV_NAME=
54+
# 动漫媒体库目录名,默认电视剧/动漫
55+
LIBRARY_ANIME_NAME=
56+
# 二级分类
57+
LIBRARY_CATEGORY=true
58+
# 电影重命名格式
59+
MOVIE_RENAME_FORMAT={{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if part %}-{{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}}
60+
# 电视剧重命名格式
61+
TV_RENAME_FORMAT={{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}} 集{% endif %}{{fileExt}}
62+
63+
####################################
64+
# 站点 #
65+
####################################
66+
# 【*】CookieCloud服务器地址,默认为公共服务器
67+
COOKIECLOUD_HOST=https://movie-pilot.org/cookiecloud
68+
# 【*】CookieCloud用户KEY
69+
COOKIECLOUD_KEY=
70+
# 【*】CookieCloud端对端加密密码
71+
COOKIECLOUD_PASSWORD=
72+
# 【*】CookieCloud同步间隔(分钟)
73+
COOKIECLOUD_INTERVAL=1440
74+
# OCR服务器地址
75+
OCR_HOST=https://movie-pilot.org
76+
# 【*】CookieCloud对应的浏览器UA
77+
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
78+
79+
####################################
80+
# 订阅 & 搜索 #
4581
####################################
4682
# 订阅模式 spider/rss
4783
SUBSCRIBE_MODE=spider
@@ -149,39 +185,3 @@ JELLYFIN_API_KEY=
149185
PLEX_HOST=
150186
# Plex Token
151187
PLEX_TOKEN=
152-
153-
####################################
154-
# 站点 #
155-
####################################
156-
# 【*】CookieCloud服务器地址,默认为公共服务器
157-
COOKIECLOUD_HOST=https://movie-pilot.org/cookiecloud
158-
# 【*】CookieCloud用户KEY
159-
COOKIECLOUD_KEY=
160-
# 【*】CookieCloud端对端加密密码
161-
COOKIECLOUD_PASSWORD=
162-
# 【*】CookieCloud同步间隔(分钟)
163-
COOKIECLOUD_INTERVAL=1440
164-
# OCR服务器地址
165-
OCR_HOST=https://movie-pilot.org
166-
# 【*】CookieCloud对应的浏览器UA
167-
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
168-
169-
####################################
170-
# 媒体库 #
171-
####################################
172-
# 【*】转移方式 link/copy/move/softlink
173-
TRANSFER_TYPE=copy
174-
# 【*】媒体库目录,多个目录使用,分隔
175-
LIBRARY_PATH=
176-
# 电影媒体库目录名,默认电影
177-
LIBRARY_MOVIE_NAME=
178-
# 电视剧媒体库目录名,默认电视剧
179-
LIBRARY_TV_NAME=
180-
# 动漫媒体库目录名,默认电视剧/动漫
181-
LIBRARY_ANIME_NAME=
182-
# 二级分类
183-
LIBRARY_CATEGORY=true
184-
# 电影重命名格式
185-
MOVIE_RENAME_FORMAT={{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if part %}-{{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}}
186-
# 电视剧重命名格式
187-
TV_RENAME_FORMAT={{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}} 集{% endif %}{{fileExt}}

alembic/env.py database/env.py

File renamed without changes.

alembic/gen.py database/gen.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
db_version = input("请输入版本号:")
1414
db_location = settings.CONFIG_PATH / 'user.db'
15-
script_location = settings.ROOT_PATH / 'alembic'
15+
script_location = settings.ROOT_PATH / 'database'
1616
alembic_cfg = AlembicConfig()
1717
alembic_cfg.set_main_option('script_location', str(script_location))
1818
alembic_cfg.set_main_option('sqlalchemy.url', f"sqlite:///{db_location}")
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

windows.spec

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# -*- mode: python ; coding: utf-8 -*-
2+
3+
def collect_pkg_data(package, include_py_files=False, subdir=None):
4+
"""
5+
Collect all data files from the given package.
6+
"""
7+
import os
8+
from PyInstaller.utils.hooks import get_package_paths, remove_prefix, PY_IGNORE_EXTENSIONS
9+
10+
# Accept only strings as packages.
11+
if type(package) is not str:
12+
raise ValueError
13+
14+
pkg_base, pkg_dir = get_package_paths(package)
15+
if subdir:
16+
pkg_dir = os.path.join(pkg_dir, subdir)
17+
# Walk through all file in the given package, looking for data files.
18+
data_toc = TOC()
19+
for dir_path, dir_names, files in os.walk(pkg_dir):
20+
for f in files:
21+
extension = os.path.splitext(f)[1]
22+
if include_py_files or (extension not in PY_IGNORE_EXTENSIONS):
23+
source_file = os.path.join(dir_path, f)
24+
dest_folder = remove_prefix(dir_path, os.path.dirname(pkg_base) + os.sep)
25+
dest_file = os.path.join(dest_folder, f)
26+
data_toc.append((dest_file, source_file, 'DATA'))
27+
return data_toc
28+
29+
30+
def collect_local_submodules(package):
31+
"""
32+
Collect all local submodules from the given package.
33+
"""
34+
import os
35+
base_dir = '..'
36+
package_dir = os.path.join(base_dir, package.replace('.', os.sep))
37+
submodules = []
38+
for dir_path, dir_names, files in os.walk(package_dir):
39+
for f in files:
40+
if f == '__init__.py':
41+
submodules.append(f"{package}.{os.path.basename(dir_path)}")
42+
elif f.endswith('.py'):
43+
submodules.append(f"{package}.{os.path.basename(dir_path)}.{os.path.splitext(f)[0]}")
44+
for d in dir_names:
45+
submodules.append(f"{package}.{os.path.basename(dir_path)}.{d}")
46+
return submodules
47+
48+
49+
hiddenimports = [
50+
'passlib.handlers.bcrypt',
51+
'app.modules',
52+
'app.plugins',
53+
] + collect_local_submodules('app.modules') \
54+
+ collect_local_submodules('app.plugins')
55+
56+
a = Analysis(
57+
['app/main.py'],
58+
pathex=[],
59+
binaries=[],
60+
datas=[],
61+
hiddenimports=hiddenimports,
62+
hookspath=[],
63+
hooksconfig={},
64+
runtime_hooks=[],
65+
excludes=[],
66+
noarchive=False,
67+
)
68+
69+
pyz = PYZ(a.pure)
70+
71+
exe = EXE(
72+
pyz,
73+
a.scripts,
74+
[],
75+
exclude_binaries=True,
76+
name='MoviePilot',
77+
debug=False,
78+
bootloader_ignore_signals=False,
79+
strip=False,
80+
upx=True,
81+
console=True,
82+
disable_windowed_traceback=False,
83+
argv_emulation=False,
84+
target_arch=None,
85+
codesign_identity=None,
86+
entitlements_file=None,
87+
icon="app.ico"
88+
)
89+
90+
coll = COLLECT(
91+
exe,
92+
a.binaries,
93+
a.datas,
94+
collect_pkg_data('config'),
95+
collect_pkg_data('cf_clearance'),
96+
collect_pkg_data('database', include_py_files=True),
97+
strip=False,
98+
upx=True,
99+
upx_exclude=[],
100+
name='MoviePilot',
101+
)

0 commit comments

Comments
 (0)