Skip to content

Commit 29f3667

Browse files
committed
Condense media section, fix btn-ghost, add optional deps to installer
CEP panel: tighten #clipSection vertical spacing (padding, margins, thumbnail size), add missing .btn-ghost CSS class for the Recent button. Server: safe_pip_install now finds system Python for frozen builds instead of using sys.executable; _setup_system_site_packages() appends system site-packages to sys.path so optional pip packages are visible. Installer: new "Optional Tools" section on OptionsPage with checkboxes for auto-editor, edge-tts, mediapipe. DependencyInstaller service finds system Python and runs pip install per selected package (step 16/17).
1 parent 10c280b commit 29f3667

9 files changed

Lines changed: 285 additions & 18 deletions

File tree

CLAUDE.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@
232232
- **Registry InstallPath** — Inno Setup now writes HKCU `Software\OpenCut\InstallPath`
233233
- **PyInstaller spec** — added mediapipe, auto_editor, transnetv2, resemble_enhance hidden imports
234234
- **ExtendScript safety** — getProjectFolder() null-checks `f.parent` before accessing `.fsName`
235+
- **Frozen-build pip install**`safe_pip_install` uses system Python from PATH when running as PyInstaller exe
236+
- **System site-packages discovery**`_setup_system_site_packages()` in server.py appends system Python's site-packages to `sys.path` for frozen builds
237+
- **Installer optional deps** — OptionsPage has "Optional Tools" section (auto-editor, edge-tts, mediapipe) with DependencyInstaller service
238+
- **Condensed media section** — reduced padding/margins throughout #clipSection for tighter vertical layout
239+
- **`.btn-ghost` CSS class** — added transparent/bordered button style for the "Recent" button
235240

236241
## v1.3.0 New Optional Dependencies
237242
```toml
@@ -247,7 +252,8 @@ enhance = ["resemble-enhance>=0.0.1"]
247252
- **Payload**: Self-extracting exe (ZIP appended with `[data][8-byte size][OCPAYLOAD]` trailer) or adjacent `payload.zip` fallback
248253
- **Key Files**:
249254
- `Models/AppConstants.cs` — version, GUIDs, registry paths
250-
- `Services/InstallEngine.cs` — orchestrator: 16 install steps matching Inno Setup operations
255+
- `Services/InstallEngine.cs` — orchestrator: 17 install steps (step 16 = optional Python deps)
256+
- `Services/DependencyInstaller.cs` — finds system Python, pip installs optional deps (auto-editor, edge-tts, mediapipe)
251257
- `Services/UninstallEngine.cs` — reverse all operations, schedule self-delete
252258
- `Themes/CatppuccinMocha.xaml` — full theme with all control styles
253259
- `Controls/LogPanel.xaml` — auto-scrolling color-coded log

extension/com.opencut.panel/client/style.css

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ body.job-active .btn-install {
12431243
#clipSection {
12441244
background: var(--bg-surface);
12451245
border-left: 3px solid var(--neon-cyan);
1246-
padding: var(--sp-sm) var(--sp-md);
1246+
padding: 6px var(--sp-md);
12471247
border-radius: var(--r-sm);
12481248
}
12491249

@@ -1259,8 +1259,8 @@ body.job-active .btn-install {
12591259
}
12601260

12611261
#clipSection .card-header {
1262-
margin-bottom: 8px;
1263-
padding-bottom: 6px;
1262+
margin-bottom: 4px;
1263+
padding-bottom: 2px;
12641264
}
12651265

12661266
/* ============================================================
@@ -1368,6 +1368,20 @@ body.job-active .btn-install {
13681368

13691369
.btn-sm { padding: 8px 14px; font-size: 10px; }
13701370

1371+
/* Ghost buttons (transparent background, border only) */
1372+
.btn-ghost {
1373+
background: transparent;
1374+
border: 1px solid var(--border-light);
1375+
color: var(--text-secondary);
1376+
cursor: pointer;
1377+
transition: background 0.15s, border-color 0.15s, color 0.15s;
1378+
}
1379+
.btn-ghost:hover {
1380+
background: rgba(255, 255, 255, 0.06);
1381+
border-color: var(--text-muted);
1382+
color: var(--text-primary);
1383+
}
1384+
13711385
/* Secondary buttons (less emphasis than primary) */
13721386
.btn-secondary {
13731387
display: inline-flex;
@@ -1487,7 +1501,7 @@ body.job-active .btn-install {
14871501
transform: scale(1.05);
14881502
}
14891503

1490-
.btn-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; padding-bottom: 2px; }
1504+
.btn-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; padding-bottom: 2px; }
14911505
.btn-sep { width: 1px; height: 16px; background: var(--border-light); }
14921506
.btn-row-split { display: flex; gap: 10px; margin-top: 8px; }
14931507

@@ -1722,8 +1736,8 @@ input[type="checkbox"] {
17221736
FILE INFO BOX
17231737
============================================================ */
17241738
.file-info {
1725-
margin-top: 10px;
1726-
padding: 10px 14px;
1739+
margin-top: 6px;
1740+
padding: 6px 10px;
17271741
background: linear-gradient(135deg, var(--bg-elevated), var(--bg-raised));
17281742
border-radius: var(--r-sm);
17291743
border: 1px solid var(--border);
@@ -2656,12 +2670,12 @@ input[type="checkbox"] {
26562670
gap: 8px;
26572671
border: 1px dashed var(--border-light);
26582672
border-radius: var(--r-sm);
2659-
padding: 8px 12px;
2673+
padding: 6px 10px;
26602674
color: var(--text-muted);
26612675
font-size: 11px;
26622676
transition: border-color 0.2s, background 0.2s;
26632677
cursor: pointer;
2664-
margin-bottom: 6px;
2678+
margin-bottom: 4px;
26652679
}
26662680
.drop-zone.drag-over {
26672681
border-color: var(--neon-cyan);
@@ -3581,16 +3595,16 @@ input[type="range"][title] {
35813595
display: flex;
35823596
align-items: center;
35833597
gap: 10px;
3584-
padding: var(--sp-md) var(--sp-xl);
3598+
padding: 6px 12px;
35853599
background: var(--bg-card);
35863600
border-radius: var(--r-xs);
3587-
margin-bottom: var(--sp-md);
3601+
margin-bottom: 6px;
35883602
border: 1px solid var(--border);
35893603
}
35903604
.clip-preview-row.hidden { display: none; }
35913605
.clip-thumb {
3592-
width: 80px;
3593-
height: 45px;
3606+
width: 64px;
3607+
height: 36px;
35943608
border-radius: 4px;
35953609
overflow: hidden;
35963610
background: var(--bg-void);

installer/src/OpenCut.Installer/Models/InstallConfig.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class InstallConfig
1010
public bool SetPlayerDebugMode { get; set; } = true;
1111
public bool DownloadWhisperModel { get; set; }
1212
public string WhisperModel { get; set; } = "turbo";
13+
public bool InstallOptionalDeps { get; set; }
14+
public List<string> SelectedDeps { get; set; } = [];
1315

1416
public string ServerPath => Path.Combine(InstallPath, "server");
1517
public string FfmpegPath => Path.Combine(InstallPath, "ffmpeg");

installer/src/OpenCut.Installer/Pages/OptionsPage.xaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@
5959
<!-- Separator -->
6060
<Rectangle Height="1" Fill="{DynamicResource Surface1}" Margin="0,12,0,12" />
6161

62+
<!-- Optional Tools -->
63+
<TextBlock Text="Optional Tools" FontSize="13" FontWeight="SemiBold"
64+
Foreground="{DynamicResource Subtext1}" Margin="0,0,0,6" />
65+
<CheckBox x:Name="OptionalDepsCheck" Content="Install optional Python tools (requires Python in PATH)"
66+
Checked="OptionalDepsCheck_Changed" Unchecked="OptionalDepsCheck_Changed" />
67+
<StackPanel x:Name="OptionalDepsPanel" Margin="20,6,0,0" Visibility="Collapsed">
68+
<CheckBox x:Name="DepAutoEditor" Content="auto-editor (motion-based auto-editing)" IsChecked="True" />
69+
<CheckBox x:Name="DepEdgeTts" Content="edge-tts (text-to-speech)" IsChecked="True" />
70+
<CheckBox x:Name="DepMediapipe" Content="mediapipe (face tracking for reframe)" IsChecked="True" />
71+
</StackPanel>
72+
73+
<!-- Separator -->
74+
<Rectangle Height="1" Fill="{DynamicResource Surface1}" Margin="0,12,0,12" />
75+
6276
<!-- AI Models -->
6377
<TextBlock Text="AI Models" FontSize="13" FontWeight="SemiBold"
6478
Foreground="{DynamicResource Subtext1}" Margin="0,0,0,6" />

installer/src/OpenCut.Installer/Pages/OptionsPage.xaml.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ private void WhisperCheck_Changed(object sender, RoutedEventArgs e)
6161
? Visibility.Visible : Visibility.Collapsed;
6262
}
6363

64+
private void OptionalDepsCheck_Changed(object sender, RoutedEventArgs e)
65+
{
66+
OptionalDepsPanel.Visibility = OptionalDepsCheck.IsChecked == true
67+
? Visibility.Visible : Visibility.Collapsed;
68+
}
69+
6470
private void Back_Click(object sender, RoutedEventArgs e)
6571
{
6672
_mainWindow.SetStep(1);
@@ -78,6 +84,15 @@ private void Install_Click(object sender, RoutedEventArgs e)
7884
config.CreateStartMenuShortcut = StartMenuCheck.IsChecked == true;
7985
config.CreateStartupShortcut = AutostartCheck.IsChecked == true;
8086
config.DownloadWhisperModel = WhisperCheck.IsChecked == true;
87+
config.InstallOptionalDeps = OptionalDepsCheck.IsChecked == true;
88+
89+
if (config.InstallOptionalDeps)
90+
{
91+
config.SelectedDeps.Clear();
92+
if (DepAutoEditor.IsChecked == true) config.SelectedDeps.Add("auto-editor");
93+
if (DepEdgeTts.IsChecked == true) config.SelectedDeps.Add("edge-tts");
94+
if (DepMediapipe.IsChecked == true) config.SelectedDeps.Add("mediapipe");
95+
}
8196

8297
if (config.DownloadWhisperModel)
8398
{
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System.Diagnostics;
2+
using OpenCut.Installer.Models;
3+
4+
namespace OpenCut.Installer.Services;
5+
6+
/// <summary>
7+
/// Installs optional Python packages via pip using system Python.
8+
/// </summary>
9+
public class DependencyInstaller
10+
{
11+
private static readonly string[] PythonCandidates = ["python", "python3", "py"];
12+
13+
public void InstallDeps(InstallConfig config, IProgress<InstallProgress> progress, int step, int totalSteps)
14+
{
15+
var stepName = "Installing optional tools";
16+
17+
if (!config.InstallOptionalDeps || config.SelectedDeps.Count == 0)
18+
{
19+
Report(progress, step, totalSteps, stepName, "Skipped (none selected).", LogLevel.Debug);
20+
return;
21+
}
22+
23+
// Find system Python
24+
var python = FindPython();
25+
if (python == null)
26+
{
27+
Report(progress, step, totalSteps, stepName,
28+
"Python not found in PATH — optional tools skipped. Install from python.org and add to PATH.",
29+
LogLevel.Warning);
30+
return;
31+
}
32+
33+
Report(progress, step, totalSteps, stepName, $"Using Python: {python}");
34+
35+
foreach (var package in config.SelectedDeps)
36+
{
37+
Report(progress, step, totalSteps, stepName, $"Installing {package}...");
38+
try
39+
{
40+
var psi = new ProcessStartInfo
41+
{
42+
FileName = python,
43+
Arguments = $"-m pip install {package} -q",
44+
RedirectStandardOutput = true,
45+
RedirectStandardError = true,
46+
UseShellExecute = false,
47+
CreateNoWindow = true
48+
};
49+
50+
using var process = Process.Start(psi);
51+
if (process == null)
52+
{
53+
Report(progress, step, totalSteps, stepName,
54+
$"Failed to start pip for {package}.", LogLevel.Error);
55+
continue;
56+
}
57+
58+
process.WaitForExit(300000); // 5 min per package
59+
60+
if (process.ExitCode == 0)
61+
{
62+
Report(progress, step, totalSteps, stepName,
63+
$"{package} installed successfully.", LogLevel.Success);
64+
}
65+
else
66+
{
67+
var stderr = process.StandardError.ReadToEnd();
68+
var msg = string.IsNullOrWhiteSpace(stderr) ? $"exit code {process.ExitCode}" : stderr.Trim();
69+
Report(progress, step, totalSteps, stepName,
70+
$"Failed to install {package}: {msg}", LogLevel.Warning);
71+
}
72+
}
73+
catch (Exception ex)
74+
{
75+
Report(progress, step, totalSteps, stepName,
76+
$"Error installing {package}: {ex.Message}", LogLevel.Warning);
77+
}
78+
}
79+
}
80+
81+
private static string? FindPython()
82+
{
83+
foreach (var name in PythonCandidates)
84+
{
85+
try
86+
{
87+
var psi = new ProcessStartInfo
88+
{
89+
FileName = name,
90+
Arguments = "--version",
91+
RedirectStandardOutput = true,
92+
RedirectStandardError = true,
93+
UseShellExecute = false,
94+
CreateNoWindow = true
95+
};
96+
97+
using var process = Process.Start(psi);
98+
if (process == null) continue;
99+
process.WaitForExit(10000);
100+
101+
if (process.ExitCode == 0)
102+
return name;
103+
}
104+
catch
105+
{
106+
// Not found, try next
107+
}
108+
}
109+
110+
return null;
111+
}
112+
113+
private static void Report(IProgress<InstallProgress> progress, int step, int total,
114+
string stepName, string message, LogLevel level = LogLevel.Info)
115+
{
116+
progress.Report(new InstallProgress
117+
{
118+
StepNumber = step,
119+
TotalSteps = total,
120+
StepName = stepName,
121+
Message = message,
122+
Level = level
123+
});
124+
}
125+
}

installer/src/OpenCut.Installer/Services/InstallEngine.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class InstallEngine
1515
private readonly ShortcutCreator _shortcutCreator = new();
1616
private readonly CepInstaller _cepInstaller = new();
1717
private readonly WhisperDownloader _whisperDownloader = new();
18+
private readonly DependencyInstaller _dependencyInstaller = new();
1819

1920
public InstallEngine(InstallConfig config)
2021
{
@@ -23,7 +24,7 @@ public InstallEngine(InstallConfig config)
2324

2425
public void RunInstall(IProgress<InstallProgress> progress)
2526
{
26-
int totalSteps = 16;
27+
int totalSteps = 17;
2728
int step = 0;
2829

2930
var tempDir = Path.Combine(Path.GetTempPath(), $"OpenCut-Install-{Guid.NewGuid():N}");
@@ -144,8 +145,12 @@ public void RunInstall(IProgress<InstallProgress> progress)
144145
else
145146
Report(progress, step, totalSteps, "Whisper model", "Skipped (not selected).", LogLevel.Debug);
146147

147-
// Step 16: Cleanup temp
148+
// Step 16: Install optional Python tools
148149
step = 16;
150+
_dependencyInstaller.InstallDeps(_config, progress, step, totalSteps);
151+
152+
// Step 17: Cleanup temp
153+
step = 17;
149154
Report(progress, step, totalSteps, "Cleaning up", "Removing temporary files...");
150155
try
151156
{

0 commit comments

Comments
 (0)