riplex 0.3.5__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {riplex-0.3.5/src/riplex.egg-info → riplex-0.3.6}/PKG-INFO +1 -1
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/disc/makemkv.py +15 -1
- {riplex-0.3.5 → riplex-0.3.6/src/riplex.egg-info}/PKG-INFO +1 -1
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/progress.py +83 -19
- {riplex-0.3.5 → riplex-0.3.6}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/.github/agents/riplex.agent.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/.github/copilot-instructions.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/.github/workflows/publish.yml +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/.github/workflows/release.yml +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/.gitignore +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/LICENSE +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/README.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/REFACTOR_PLAN.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/architecture.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/changelog.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/getting-started/configuration.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/getting-started/installation.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/guide/lookup.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/guide/orchestrate.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/guide/organize.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/guide/workflow.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/index.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/naming-rules.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/reference/cli.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/docs/troubleshooting.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/issues/cross-disc-dvdcompare-matching.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/issues/orchestrate-dvdcompare-fallback.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/issues/planned-features.md +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/mkdocs.yml +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/pyproject.toml +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/setup.cfg +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/cache.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/config.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/dedup.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/detect.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/disc/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/disc/analysis.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/disc/provider.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/formatter.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/lookup.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/manifest.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/matcher.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/metadata/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/metadata/planner.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/metadata/provider.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/metadata/sources/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/metadata/sources/tmdb.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/models.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/normalize.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/organizer.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/scanner.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/snapshot.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/splitter.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/tagger.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/title.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex/ui.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex.egg-info/SOURCES.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex.egg-info/dependency_links.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex.egg-info/entry_points.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex.egg-info/requires.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex.egg-info/top_level.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/main.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/disc_detection.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/done.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/folder_picker.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/metadata.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/organize_done.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/organize_preview.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/release.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/selection.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/screens/welcome.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_app/updater.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/commands/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/commands/lookup.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/commands/orchestrate.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/commands/organize.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/commands/rip.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/commands/setup.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/formatting.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/src/riplex_cli/main.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/__init__.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/fixtures/makemkvcon_list.txt +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/Batman Begins.snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/snapshots/Waterworld.snapshot.json +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_cache.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_cli_utils.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_config.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_dedup.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_detect.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_disc_analysis.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_disc_provider.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_formatter.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_makemkv.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_matcher.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_normalize.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_organizer.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_planner.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_rip_guide.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_scanner.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_snapshot.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_splitter.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_tagger.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_ui.py +0 -0
- {riplex-0.3.5 → riplex-0.3.6}/tests/test_updater.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Automates the tedious manual work around MakeMKV: figuring out what to rip, which MKV files are actually what, and organizing everything into Plex-compatible folder structures.
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -6,6 +6,7 @@ import logging
|
|
|
6
6
|
import platform
|
|
7
7
|
import re
|
|
8
8
|
import subprocess
|
|
9
|
+
import threading
|
|
9
10
|
from dataclasses import dataclass, field
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
@@ -471,6 +472,7 @@ class MakeMKV:
|
|
|
471
472
|
title_index: int,
|
|
472
473
|
output_dir: Path,
|
|
473
474
|
progress_callback=None,
|
|
475
|
+
cancel_event: threading.Event | None = None,
|
|
474
476
|
) -> RipResult:
|
|
475
477
|
"""Rip a single title from disc via ``makemkvcon mkv``."""
|
|
476
478
|
exe = self._require_exe()
|
|
@@ -494,6 +496,15 @@ class MakeMKV:
|
|
|
494
496
|
|
|
495
497
|
try:
|
|
496
498
|
for line in proc.stdout:
|
|
499
|
+
if cancel_event and cancel_event.is_set():
|
|
500
|
+
proc.terminate()
|
|
501
|
+
proc.wait(timeout=5)
|
|
502
|
+
return RipResult(
|
|
503
|
+
title_index=title_index,
|
|
504
|
+
success=False,
|
|
505
|
+
output_file="",
|
|
506
|
+
error_message="Cancelled by user",
|
|
507
|
+
)
|
|
497
508
|
line = line.rstrip("\n\r")
|
|
498
509
|
log.debug("makemkvcon: %s", line)
|
|
499
510
|
raw_lines.append(line)
|
|
@@ -559,12 +570,15 @@ def run_rip(
|
|
|
559
570
|
output_dir: Path,
|
|
560
571
|
makemkvcon: Path | None = None,
|
|
561
572
|
progress_callback=None,
|
|
573
|
+
cancel_event: threading.Event | None = None,
|
|
562
574
|
) -> RipResult:
|
|
563
575
|
"""Rip a single title from a disc via makemkvcon mkv.
|
|
564
576
|
|
|
565
577
|
.. deprecated:: Use :pyclass:`MakeMKV` instead.
|
|
566
578
|
"""
|
|
567
|
-
return MakeMKV(makemkvcon).rip(
|
|
579
|
+
return MakeMKV(makemkvcon).rip(
|
|
580
|
+
drive, title_index, output_dir, progress_callback, cancel_event
|
|
581
|
+
)
|
|
568
582
|
|
|
569
583
|
|
|
570
584
|
# ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Automates the tedious manual work around MakeMKV: figuring out what to rip, which MKV files are actually what, and organizing everything into Plex-compatible folder structures.
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -13,13 +13,25 @@ from riplex.snapshot import copy_debug_log, get_debug_dir, save_rip_snapshot
|
|
|
13
13
|
log = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
def _format_eta(seconds: int) -> str:
|
|
17
|
+
"""Format seconds into HH:MM:SS or MM:SS."""
|
|
18
|
+
if seconds < 0:
|
|
19
|
+
return "..."
|
|
20
|
+
if seconds >= 3600:
|
|
21
|
+
h, rem = divmod(seconds, 3600)
|
|
22
|
+
m, s = divmod(rem, 60)
|
|
23
|
+
return f"{h}:{m:02d}:{s:02d}"
|
|
24
|
+
m, s = divmod(seconds, 60)
|
|
25
|
+
return f"{m}:{s:02d}"
|
|
26
|
+
|
|
27
|
+
|
|
16
28
|
class ProgressScreen:
|
|
17
29
|
def __init__(self, app):
|
|
18
30
|
self.app = app
|
|
19
|
-
self.
|
|
31
|
+
self._cancel_event = threading.Event()
|
|
20
32
|
|
|
21
33
|
def build(self) -> ft.Control:
|
|
22
|
-
self.
|
|
34
|
+
self._cancel_event.clear()
|
|
23
35
|
selected = self.app.state["selected_titles"]
|
|
24
36
|
disc_info = self.app.state["disc_info"]
|
|
25
37
|
titles = disc_info.titles if disc_info else []
|
|
@@ -29,6 +41,10 @@ class ProgressScreen:
|
|
|
29
41
|
self.total_count = len(selected)
|
|
30
42
|
self.completed_count = 0
|
|
31
43
|
|
|
44
|
+
self._rip_start_time = 0.0
|
|
45
|
+
self._last_pct = -1
|
|
46
|
+
self._current_title_bytes = 0
|
|
47
|
+
|
|
32
48
|
self.overall_text = ft.Text(
|
|
33
49
|
f"Ripping 0/{self.total_count} titles...",
|
|
34
50
|
size=16,
|
|
@@ -36,11 +52,14 @@ class ProgressScreen:
|
|
|
36
52
|
)
|
|
37
53
|
self.current_title_text = ft.Text("Preparing...", size=14, color=ft.Colors.GREY_400)
|
|
38
54
|
self.progress_bar = ft.ProgressBar(width=700, value=0)
|
|
39
|
-
self.
|
|
40
|
-
self.
|
|
55
|
+
self.progress_pct = ft.Text("0%", size=12, weight=ft.FontWeight.BOLD)
|
|
56
|
+
self.progress_size = ft.Text("", size=12, color=ft.Colors.GREY_400)
|
|
57
|
+
self.progress_speed = ft.Text("", size=12, color=ft.Colors.GREY_400)
|
|
58
|
+
self.progress_eta = ft.Text("", size=12, color=ft.Colors.GREY_400)
|
|
59
|
+
self.log = ft.ListView(spacing=4, height=200, auto_scroll=True)
|
|
41
60
|
self.cancel_btn = ft.ElevatedButton(
|
|
42
|
-
"
|
|
43
|
-
icon=ft.Icons.
|
|
61
|
+
"Stop Ripping",
|
|
62
|
+
icon=ft.Icons.STOP,
|
|
44
63
|
on_click=self._cancel,
|
|
45
64
|
style=ft.ButtonStyle(bgcolor=ft.Colors.RED_700),
|
|
46
65
|
)
|
|
@@ -59,7 +78,15 @@ class ProgressScreen:
|
|
|
59
78
|
self.current_title_text,
|
|
60
79
|
ft.Container(height=10),
|
|
61
80
|
self.progress_bar,
|
|
62
|
-
|
|
81
|
+
ft.Row(
|
|
82
|
+
[
|
|
83
|
+
self.progress_pct,
|
|
84
|
+
self.progress_size,
|
|
85
|
+
self.progress_speed,
|
|
86
|
+
self.progress_eta,
|
|
87
|
+
],
|
|
88
|
+
spacing=20,
|
|
89
|
+
),
|
|
63
90
|
ft.Container(height=20),
|
|
64
91
|
ft.Text("Log", size=14, weight=ft.FontWeight.BOLD),
|
|
65
92
|
self.log,
|
|
@@ -84,8 +111,8 @@ class ProgressScreen:
|
|
|
84
111
|
results: list[RipResult] = []
|
|
85
112
|
|
|
86
113
|
for i, title_idx in enumerate(selected):
|
|
87
|
-
if self.
|
|
88
|
-
self._log_message("
|
|
114
|
+
if self._cancel_event.is_set():
|
|
115
|
+
self._log_message("Stopped by user.", ft.Colors.ORANGE)
|
|
89
116
|
break
|
|
90
117
|
|
|
91
118
|
title = self.title_map.get(title_idx)
|
|
@@ -96,7 +123,13 @@ class ProgressScreen:
|
|
|
96
123
|
self.overall_text.value = f"Ripping {i + 1}/{self.total_count} titles..."
|
|
97
124
|
self.current_title_text.value = f"Title #{title_idx}: {title_name} ({size_gb:.1f} GB)"
|
|
98
125
|
self.progress_bar.value = 0
|
|
99
|
-
self.
|
|
126
|
+
self.progress_pct.value = "0%"
|
|
127
|
+
self.progress_size.value = f"0.0/{size_gb:.1f} GB"
|
|
128
|
+
self.progress_speed.value = ""
|
|
129
|
+
self.progress_eta.value = ""
|
|
130
|
+
self._rip_start_time = time.monotonic()
|
|
131
|
+
self._last_pct = -1
|
|
132
|
+
self._current_title_bytes = title.size_bytes if title else 0
|
|
100
133
|
self._log_message(f"Starting title #{title_idx}: {title_name}")
|
|
101
134
|
self._update()
|
|
102
135
|
|
|
@@ -108,6 +141,7 @@ class ProgressScreen:
|
|
|
108
141
|
output_dir,
|
|
109
142
|
makemkvcon=makemkvcon,
|
|
110
143
|
progress_callback=self._on_progress,
|
|
144
|
+
cancel_event=self._cancel_event,
|
|
111
145
|
)
|
|
112
146
|
results.append(result)
|
|
113
147
|
elapsed = time.time() - start_time
|
|
@@ -137,7 +171,10 @@ class ProgressScreen:
|
|
|
137
171
|
self.overall_text.value = f"Complete: {sum(1 for r in results if r.success)}/{len(results)} successful"
|
|
138
172
|
self.current_title_text.value = ""
|
|
139
173
|
self.progress_bar.value = 1.0
|
|
140
|
-
self.
|
|
174
|
+
self.progress_pct.value = "100%"
|
|
175
|
+
self.progress_size.value = ""
|
|
176
|
+
self.progress_speed.value = ""
|
|
177
|
+
self.progress_eta.value = ""
|
|
141
178
|
self.cancel_btn.visible = False
|
|
142
179
|
self._update()
|
|
143
180
|
|
|
@@ -180,11 +217,38 @@ class ProgressScreen:
|
|
|
180
217
|
|
|
181
218
|
def _on_progress(self, progress: RipProgress):
|
|
182
219
|
"""Callback from run_rip for progress updates."""
|
|
183
|
-
if progress.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
220
|
+
if progress.max_val <= 0:
|
|
221
|
+
return
|
|
222
|
+
pct = progress.current * 100 // progress.max_val
|
|
223
|
+
if pct == self._last_pct:
|
|
224
|
+
return # avoid excessive UI updates
|
|
225
|
+
self._last_pct = pct
|
|
226
|
+
|
|
227
|
+
self.progress_bar.value = pct / 100
|
|
228
|
+
self.progress_pct.value = f"{pct}%"
|
|
229
|
+
|
|
230
|
+
total_bytes = self._current_title_bytes
|
|
231
|
+
if total_bytes > 0:
|
|
232
|
+
done_bytes = total_bytes * pct // 100
|
|
233
|
+
done_gb = done_bytes / (1024 ** 3)
|
|
234
|
+
total_gb = total_bytes / (1024 ** 3)
|
|
235
|
+
self.progress_size.value = f"{done_gb:.1f}/{total_gb:.1f} GB"
|
|
236
|
+
|
|
237
|
+
elapsed = time.monotonic() - self._rip_start_time
|
|
238
|
+
if elapsed > 1:
|
|
239
|
+
speed_mbs = (done_bytes / (1024 ** 2)) / elapsed
|
|
240
|
+
self.progress_speed.value = f"{speed_mbs:.0f} MB/s"
|
|
241
|
+
if pct > 0 and speed_mbs > 0:
|
|
242
|
+
remaining_bytes = total_bytes - done_bytes
|
|
243
|
+
eta_secs = int(remaining_bytes / (speed_mbs * 1024 * 1024))
|
|
244
|
+
self.progress_eta.value = f"ETA {_format_eta(eta_secs)}"
|
|
245
|
+
else:
|
|
246
|
+
self.progress_eta.value = "ETA ..."
|
|
247
|
+
else:
|
|
248
|
+
self.progress_speed.value = ""
|
|
249
|
+
self.progress_eta.value = "ETA ..."
|
|
250
|
+
|
|
251
|
+
self._update()
|
|
188
252
|
|
|
189
253
|
def _log_message(self, message: str, color=None):
|
|
190
254
|
"""Append a message to the log."""
|
|
@@ -193,10 +257,10 @@ class ProgressScreen:
|
|
|
193
257
|
)
|
|
194
258
|
|
|
195
259
|
def _cancel(self, e):
|
|
196
|
-
"""
|
|
197
|
-
self.
|
|
260
|
+
"""Signal cancellation — terminates the active makemkvcon process."""
|
|
261
|
+
self._cancel_event.set()
|
|
198
262
|
self.cancel_btn.disabled = True
|
|
199
|
-
self.cancel_btn.text = "
|
|
263
|
+
self.cancel_btn.text = "Stopping..."
|
|
200
264
|
self._update()
|
|
201
265
|
|
|
202
266
|
def _update(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|