Repository navigation
Expand file tree
/
Copy pathProgram.cs
More file actions
214 lines (202 loc) · 8.6 KB
/
Copy pathProgram.cs
File metadata and controls
214 lines (202 loc) · 8.6 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
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using MoviePilot_V3.Services;
namespace MoviePilot_V3
{
internal static class Program
{
/// 单实例互斥锁(仅面板模式使用),防止重复启动多个面板
private static Mutex instanceMutex;
/// 应用程序的主入口点。
[STAThread]
static void Main(string[] args)
{
// 命令行模式:-c <start|stop|restart|update>,执行后退出,不显示窗口
// (日志通过命名管道发送到面板运行日志区;面板未运行时静默执行)
if (args != null && args.Length > 0)
{
// 命令行模式不占用面板单实例锁,且启动服务需要 config 模板,先释放内置资源
BundledResources.Deploy();
string arg0 = args[0].ToLowerInvariant();
if (arg0 == "-c")
{
if (args.Length >= 2)
{
RunCommandLine(args[1]);
}
else
{
SendLinesToPanel("[ERROR] 缺少命令参数,用法: MoviePilot-V3 -c <start|stop|restart|update>");
}
return;
}
// 未知参数:提示后退出,不启动面板
SendLinesToPanel("[ERROR] 未知参数: " + args[0] + "(支持: -c start / stop / restart / update)");
return;
}
// 单实例检查:已运行的面板则提示并退出。检查通过后才释放内置资源,
// 避免在不同目录启动的第二个实例误释放 ico / config(互斥锁被拒时直接退出)
bool createdNew;
instanceMutex = new Mutex(true, "Local\\MoviePilot-V3-Panel", out createdNew);
if (!createdNew)
{
MessageBox.Show("MoviePilot 服务管理面板已在运行,请勿重复启动。", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// 首次运行:把内置的 app.ico 与 config 模板(nginx.conf / common.conf)释放到 exe 同级目录
// (平层布局,已存在不覆盖)
BundledResources.Deploy();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
Application.Run(new Form1());
}
finally
{
instanceMutex.ReleaseMutex();
}
}
/// 将多行日志发送到面板运行日志区;面板未运行时静默(不阻塞、不报错)。
private static void SendLinesToPanel(params string[] lines)
{
if (lines == null || lines.Length == 0) return;
try
{
using (var client = new NamedPipeClientStream(".", AppConfig.PANEL_LOG_PIPE, PipeDirection.Out))
{
client.Connect(500);
using (var writer = new StreamWriter(client, Encoding.UTF8) { AutoFlush = true })
{
foreach (string line in lines)
{
writer.WriteLine(line);
}
}
}
}
catch
{
// 面板未运行:静默
}
}
/// 命令行模式:执行服务操作(update 与面板"检查MP更新"确认后的升级流程一致),日志实时发送到面板运行日志区。
private static void RunCommandLine(string command)
{
// 尝试连接面板日志管道;面板未运行时连接失败,日志转写文件(logs\cmd.log)
StreamWriter pipeWriter = null;
StreamWriter fileWriter = null;
try
{
var client = new NamedPipeClientStream(".", AppConfig.PANEL_LOG_PIPE, PipeDirection.Out);
client.Connect(500);
pipeWriter = new StreamWriter(client, Encoding.UTF8) { AutoFlush = true };
}
catch
{
// 面板未运行:日志写入文件,便于排查命令行模式问题
try
{
Directory.CreateDirectory(AppConfig.LOGS_DIR);
fileWriter = new StreamWriter(Path.Combine(AppConfig.LOGS_DIR, "cmd.log"), true, Encoding.UTF8) { AutoFlush = true };
}
catch
{
}
}
Action<string> log = msg =>
{
try
{
if (pipeWriter != null)
{
pipeWriter.WriteLine(msg);
}
else if (fileWriter != null)
{
fileWriter.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " " + msg);
}
}
catch
{
// 管道中断:后续日志静默丢弃
}
};
try
{
switch (command.ToLowerInvariant())
{
case "start":
// 环境就绪检查在 StartServices 内完成(缺失的便携版自动下载)
ServiceManager.StartServices(log);
break;
case "stop":
ServiceManager.StopServices(log);
break;
case "restart":
// 特例:app.env 中 MOVIEPILOT_AUTO_UPDATE 为 dev/release 时,执行启动检查更新并启动
if (IsAutoUpdateEnabled())
{
UpgradeService.Upgrade(log, (success, message) =>
{
if (success) { log.Info(message); }
else { log.Error(message); }
});
break;
}
// 与面板"重启服务"一致:先确保环境就绪,再停止服务,最后启动服务(纯重启,不检查更新)
EnvironmentSetup.EnsureEnvironment(log);
ServiceManager.StopServices(log);
ServiceManager.StartServices(log);
break;
case "update":
// 与面板"检查MP更新"确认后的升级流程一致:升级流程内部自行停止服务、更新代码、安装依赖并重启服务
UpgradeService.Upgrade(log, (success, message) =>
{
if (success) { log.Info(message); }
else { log.Error(message); }
});
break;
default:
log.Warn("未知命令: " + command + "(支持: start / stop / restart / update)");
break;
}
}
catch (Exception ex)
{
log.Error("执行失败: " + ex.Message);
}
finally
{
if (pipeWriter != null) pipeWriter.Dispose();
if (fileWriter != null) fileWriter.Dispose();
}
}
/// 读取后端配置 app.env 的 MOVIEPILOT_AUTO_UPDATE 值,判断是否为 dev/release 模式(均可升级)
private static bool IsAutoUpdateEnabled()
{
try
{
string envFile = Path.Combine(AppConfig.CurrentMpConfDir, "app.env");
if (!File.Exists(envFile)) return false;
foreach (string line in File.ReadAllLines(envFile))
{
string trimmed = line.Trim();
if (!trimmed.StartsWith("MOVIEPILOT_AUTO_UPDATE=", StringComparison.OrdinalIgnoreCase)) continue;
string value = trimmed.Substring("MOVIEPILOT_AUTO_UPDATE=".Length).Trim().Trim('\'', '"');
return string.Equals(value, "true", StringComparison.OrdinalIgnoreCase);
}
}
catch
{
// 读取失败按非 true 处理,走正常重启流程
}
return false;
}
}
}