-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
219 lines (184 loc) · 7.48 KB
/
main.py
File metadata and controls
219 lines (184 loc) · 7.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env python3
"""
多平台推流工具 - 主入口程序
支持:GUI界面 / 无头运行 / systemd服务管理
功能概览:
├── GUI模式:桌面可视化操作
├── Headless:服务器后台运行
├── Service:systemd自动启动
└── 自愈:断线自动重连
依赖:PyQt5 + FFmpeg
"""
import sys
import os
import signal
import argparse
from typing import Dict, NoReturn
from PyQt5.QtWidgets import QApplication
# 项目内部模块
from multipush.constants import PREF_PATH, APP_VERSION
from multipush.ffmpeg_utils import ensure_dependencies
from multipush.ui.main_window import MainWindow
from multipush.headless import run_headless
from multipush.systemd_utils import install_systemd_unit, uninstall_systemd_unit
# =============================================================================
# 主函数:统一入口 + 命令行解析
# =============================================================================
def main() -> int:
"""
程序主入口 - 智能模式分发
Returns:
int: 标准退出码
0: 正常退出
1: FFmpeg检查失败
5: 权限不足
其他: 运行时异常
"""
# ---------- 0. 命令行参数解析 ----------
parser = argparse.ArgumentParser(
description=f"🎥 多平台推流工具 v{APP_VERSION} - RTSP/文件 → RTMP/RTMPS(tee)+ 自愈",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
python main.py # 🚀 GUI模式(默认)
python main.py --headless # 🖥️ 无头后台运行
python main.py --config custom.json # 📄 指定配置
sudo python main.py --install-service # ⚙️ 安装systemd服务
sudo python main.py --uninstall-service # 🗑️ 卸载服务
"""
)
parser.add_argument(
'--headless',
action='store_true',
help='🖥️ 无UI运行(服务器/systemd专用)'
)
parser.add_argument(
'--config',
type=str,
default=PREF_PATH,
help=f'📄 配置JSON路径(默认:{PREF_PATH})'
)
parser.add_argument(
'--install-service',
action='store_true',
help='⚙️ 安装systemd服务(需root权限)'
)
parser.add_argument(
'--uninstall-service',
action='store_true',
help='🗑️ 卸载systemd服务(需root权限)'
)
args = parser.parse_args()
# ---------- 1. 服务管理模式(优先级最高) ----------
if args.install_service:
return _handle_service_install(args.config)
if args.uninstall_service:
return _handle_service_uninstall()
# ---------- 2. 前置检查:FFmpeg环境 ----------
if not ensure_dependencies(None, silent=True):
print(f"[FATAL] ❌ FFmpeg未就绪,请先安装!", flush=True)
print("💡 Linux: sudo apt install ffmpeg", flush=True)
print("💡 macOS: brew install ffmpeg", flush=True)
print("💡 Windows: 下载 https://ffmpeg.org", flush=True)
return 1
# ---------- 3. 运行模式分发 ----------
if args.headless:
return _run_headless_mode(args.config)
else:
return _run_gui_mode()
# =============================================================================
# 服务管理函数
# =============================================================================
def _handle_service_install(config_path: str) -> int:
"""安装systemd服务"""
if not _check_root():
print("[FATAL] ❌ 需要root权限执行 --install-service", flush=True)
return 5
print(f"[INFO] 🔧 安装systemd服务,配置:{config_path}", flush=True)
try:
# 获取当前脚本目录作为工作目录
work_dir = os.path.abspath(".")
install_systemd_unit(
config_path=os.path.abspath(config_path),
work_dir=work_dir
)
print(f"[SUCCESS] ✅ 服务安装成功!", flush=True)
print(f"💡 启动: sudo systemctl start multipush", flush=True)
print(f"💡 开机自启: sudo systemctl enable multipush", flush=True)
return 0
except Exception as e:
print(f"[FATAL] ❌ 安装失败:{e}", flush=True)
return 5
def _handle_service_uninstall() -> int:
"""卸载systemd服务"""
if not _check_root():
print("[FATAL] ❌ 需要root权限执行 --uninstall-service", flush=True)
return 5
print("[INFO] 🗑️ 卸载systemd服务", flush=True)
try:
uninstall_systemd_unit()
print("[SUCCESS] ✅ 服务卸载成功!", flush=True)
return 0
except Exception as e:
print(f"[FATAL] ❌ 卸载失败:{e}", flush=True)
return 5
# =============================================================================
# 运行模式函数
# =============================================================================
def _run_headless_mode(config_path: str) -> int:
"""无头模式运行"""
print(f"[INFO] 🖥️ 启动无头模式,配置:{config_path}", flush=True)
# 创建停止信号
stop_flag: Dict[str, bool] = {'stop': False}
# 注册信号处理器(SIGINT/SIGTERM)
def signal_handler(signum, frame):
print(f"\n[INFO] 💡 接收信号 {signum},优雅停止...", flush=True)
stop_flag['stop'] = True
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
# 启动无头运行
return run_headless(config_path, stop_flag)
def _run_gui_mode() -> int:
"""GUI模式运行"""
print(f"[INFO] 🚀 启动GUI模式 v{APP_VERSION}", flush=True)
os.environ["QT_LOGGING_RULES"] = "qt.qpa.fonts=false;qt.fonts=false"
# 创建Qt应用
app = QApplication(sys.argv)
app.setApplicationName("MultiPush")
app.setApplicationVersion(APP_VERSION)
# 创建主窗口
window = MainWindow()
window.show()
# 进入事件循环
return app.exec_()
# =============================================================================
# 工具函数
# =============================================================================
def _check_root() -> bool:
"""检查root权限(兼容Windows)"""
return not hasattr(os, 'geteuid') or os.geteuid() == 0
# =============================================================================
# 程序入口
# =============================================================================
if __name__ == "__main__":
"""
快速启动指南:
╔══════════════════════════════════════╗
║ MultiPush v{APP_VERSION} ║
║──────────────────────────────────────║
║ GUI桌面 │ python main.py ║
║ 服务器后台 │ python main.py --headless ║
║ systemd服务 │ sudo python main.py --install-service ║
╚══════════════════════════════════════╝
"""
try:
exit_code = main()
sys.exit(exit_code)
except KeyboardInterrupt:
print("\n[INFO] 👋 用户中断,退出", flush=True)
sys.exit(130) # SIGINT标准退出码
except Exception as e:
print(f"\n[FATAL] 💥 未捕获异常:{e}", flush=True)
sys.exit(1)
# finally:
# input("Press Enter to exit...") # Keep window open