Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5baadd9dbb | |||
| 997bfef893 |
149
whipper/gui.py
149
whipper/gui.py
@@ -36,6 +36,7 @@ from whipper.result import result
|
|||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
APP_ID = "com.github.whipper_team.Whipper"
|
||||||
|
|
||||||
|
|
||||||
class RipCancelledError(Exception):
|
class RipCancelledError(Exception):
|
||||||
@@ -86,7 +87,9 @@ def _release_duration_distance(metadata, duration_ms):
|
|||||||
class WhipperGui(Gtk.Application if Gtk is not None else object):
|
class WhipperGui(Gtk.Application if Gtk is not None else object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
require_gui_runtime()
|
require_gui_runtime()
|
||||||
super().__init__(application_id="com.github.whipper_team.WhipperGui")
|
super().__init__(application_id=APP_ID)
|
||||||
|
GLib.set_application_name("Whipper")
|
||||||
|
Gtk.Window.set_default_icon_name(APP_ID)
|
||||||
self.window = None
|
self.window = None
|
||||||
self.main_pane = None
|
self.main_pane = None
|
||||||
|
|
||||||
@@ -122,6 +125,7 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
self.logger_combo = None
|
self.logger_combo = None
|
||||||
self.track_template_entry = None
|
self.track_template_entry = None
|
||||||
self.disc_template_entry = None
|
self.disc_template_entry = None
|
||||||
|
self.compact_mode_label = None
|
||||||
|
|
||||||
self.release_store = None
|
self.release_store = None
|
||||||
self.release_view = None
|
self.release_view = None
|
||||||
@@ -149,17 +153,16 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
def _build_window(self):
|
def _build_window(self):
|
||||||
window = Gtk.ApplicationWindow(application=self)
|
window = Gtk.ApplicationWindow(application=self)
|
||||||
window.set_title("Whipper")
|
window.set_title("Whipper")
|
||||||
window.set_icon_name("com.github.whipper_team.Whipper")
|
window.set_icon_name(APP_ID)
|
||||||
window.set_default_size(1380, 900)
|
window.set_default_size(1380, 820)
|
||||||
window.set_border_width(8)
|
window.set_border_width(6)
|
||||||
|
|
||||||
root = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
root = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
window.add(root)
|
window.add(root)
|
||||||
|
|
||||||
root.pack_start(self._build_transport_strip(), False, False, 0)
|
root.pack_start(self._build_transport_strip(), False, False, 0)
|
||||||
root.pack_start(self._build_progress(), False, False, 0)
|
root.pack_start(self._build_progress(), False, False, 0)
|
||||||
root.pack_start(self._build_main_content(), True, True, 0)
|
root.pack_start(self._build_main_content(), True, True, 0)
|
||||||
root.pack_start(self._build_log(), False, True, 0)
|
|
||||||
|
|
||||||
self._refresh_devices()
|
self._refresh_devices()
|
||||||
self._load_gui_settings()
|
self._load_gui_settings()
|
||||||
@@ -168,14 +171,14 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
|
|
||||||
def _build_transport_strip(self):
|
def _build_transport_strip(self):
|
||||||
frame = Gtk.Frame(label="Extraction Control Center")
|
frame = Gtk.Frame(label="Extraction Control Center")
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8, margin=8)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=6)
|
||||||
frame.add(box)
|
frame.add(box)
|
||||||
box.pack_start(self._build_controls(), False, False, 0)
|
box.pack_start(self._build_controls(), False, False, 0)
|
||||||
box.pack_start(self._build_actions(), False, False, 0)
|
box.pack_start(self._build_actions(), False, False, 0)
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
def _build_controls(self):
|
def _build_controls(self):
|
||||||
grid = Gtk.Grid(column_spacing=8, row_spacing=8)
|
grid = Gtk.Grid(column_spacing=8, row_spacing=6)
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Drive", xalign=0), 0, 0, 1, 1)
|
grid.attach(Gtk.Label(label="Drive", xalign=0), 0, 0, 1, 1)
|
||||||
self.device_combo = Gtk.ComboBoxText()
|
self.device_combo = Gtk.ComboBoxText()
|
||||||
@@ -209,23 +212,45 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
self.logger_combo.connect("changed", self._on_settings_changed)
|
self.logger_combo.connect("changed", self._on_settings_changed)
|
||||||
grid.attach(self.logger_combo, 4, 1, 2, 1)
|
grid.attach(self.logger_combo, 4, 1, 2, 1)
|
||||||
|
|
||||||
|
grid.attach(Gtk.Label(label="Cover Art", xalign=0), 0, 2, 1, 1)
|
||||||
|
self.cover_art_combo = Gtk.ComboBoxText()
|
||||||
|
self.cover_art_combo.append("", "Disabled")
|
||||||
|
self.cover_art_combo.append("file", "Save file")
|
||||||
|
self.cover_art_combo.append("embed", "Embed only")
|
||||||
|
self.cover_art_combo.append("complete", "File + embed")
|
||||||
|
self.cover_art_combo.set_active(0)
|
||||||
|
self.cover_art_combo.connect("changed", self._on_settings_changed)
|
||||||
|
grid.attach(self.cover_art_combo, 1, 2, 1, 1)
|
||||||
|
|
||||||
|
grid.attach(Gtk.Label(label="Offset", xalign=0), 2, 2, 1, 1)
|
||||||
|
self.offset_spin = Gtk.SpinButton.new_with_range(-5000, 5000, 1)
|
||||||
|
self.offset_spin.set_value(0)
|
||||||
|
self.offset_spin.connect("value-changed", self._on_settings_changed)
|
||||||
|
grid.attach(self.offset_spin, 3, 2, 1, 1)
|
||||||
|
|
||||||
|
grid.attach(Gtk.Label(label="Retries", xalign=0), 4, 2, 1, 1)
|
||||||
|
self.max_retries_spin = Gtk.SpinButton.new_with_range(0, 20, 1)
|
||||||
|
self.max_retries_spin.set_value(5)
|
||||||
|
self.max_retries_spin.connect("value-changed", self._on_settings_changed)
|
||||||
|
grid.attach(self.max_retries_spin, 5, 2, 1, 1)
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Working Dir", xalign=0), 0, 2, 1, 1)
|
grid.attach(Gtk.Label(label="Working Dir", xalign=0), 0, 2, 1, 1)
|
||||||
self.working_directory_entry = Gtk.Entry()
|
self.working_directory_entry = Gtk.Entry()
|
||||||
self.working_directory_entry.set_placeholder_text("Optional working directory")
|
self.working_directory_entry.set_placeholder_text("Optional working directory")
|
||||||
self.working_directory_entry.connect("changed", self._on_settings_changed)
|
self.working_directory_entry.connect("changed", self._on_settings_changed)
|
||||||
grid.attach(self.working_directory_entry, 1, 2, 2, 1)
|
grid.attach(self.working_directory_entry, 1, 3, 2, 1)
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Country", xalign=0), 0, 3, 1, 1)
|
grid.attach(Gtk.Label(label="Country", xalign=0), 0, 4, 1, 1)
|
||||||
self.country_entry = Gtk.Entry()
|
self.country_entry = Gtk.Entry()
|
||||||
self.country_entry.set_placeholder_text("Optional MusicBrainz country filter")
|
self.country_entry.set_placeholder_text("Optional MusicBrainz country filter")
|
||||||
self.country_entry.connect("changed", self._on_settings_changed)
|
self.country_entry.connect("changed", self._on_settings_changed)
|
||||||
grid.attach(self.country_entry, 1, 3, 1, 1)
|
grid.attach(self.country_entry, 1, 4, 1, 1)
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Release ID", xalign=0), 2, 3, 1, 1)
|
grid.attach(Gtk.Label(label="Release ID", xalign=0), 2, 4, 1, 1)
|
||||||
self.release_id_entry = Gtk.Entry()
|
self.release_id_entry = Gtk.Entry()
|
||||||
self.release_id_entry.set_placeholder_text("Optional release override")
|
self.release_id_entry.set_placeholder_text("Optional release override")
|
||||||
self.release_id_entry.connect("changed", self._on_settings_changed)
|
self.release_id_entry.connect("changed", self._on_settings_changed)
|
||||||
grid.attach(self.release_id_entry, 3, 3, 3, 1)
|
grid.attach(self.release_id_entry, 3, 4, 3, 1)
|
||||||
|
|
||||||
return grid
|
return grid
|
||||||
|
|
||||||
@@ -251,16 +276,36 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
|
|
||||||
box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.VERTICAL), False, False, 6)
|
box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.VERTICAL), False, False, 6)
|
||||||
|
|
||||||
action_hint = Gtk.Label(
|
self.unknown_check = Gtk.CheckButton(label="Unknown")
|
||||||
label="EAC-style flow: Detect TOC -> pick release -> Secure Rip",
|
self.unknown_check.set_active(True)
|
||||||
|
self.unknown_check.connect("toggled", self._on_unknown_toggled)
|
||||||
|
box.pack_start(self.unknown_check, False, False, 0)
|
||||||
|
|
||||||
|
self.cdr_check = Gtk.CheckButton(label="CD-R")
|
||||||
|
self.cdr_check.connect("toggled", self._on_settings_changed)
|
||||||
|
box.pack_start(self.cdr_check, False, False, 0)
|
||||||
|
|
||||||
|
self.keep_going_check = Gtk.CheckButton(label="Keep Going")
|
||||||
|
self.keep_going_check.set_active(True)
|
||||||
|
self.keep_going_check.connect("toggled", self._on_settings_changed)
|
||||||
|
box.pack_start(self.keep_going_check, False, False, 0)
|
||||||
|
|
||||||
|
self.overread_check = Gtk.CheckButton(label="Overread")
|
||||||
|
self.overread_check.connect("toggled", self._on_settings_changed)
|
||||||
|
box.pack_start(self.overread_check, False, False, 0)
|
||||||
|
|
||||||
|
box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.VERTICAL), False, False, 6)
|
||||||
|
|
||||||
|
self.compact_mode_label = Gtk.Label(
|
||||||
|
label="Compact EAC-style flow: Detect TOC -> pick release -> Secure Rip",
|
||||||
xalign=0,
|
xalign=0,
|
||||||
)
|
)
|
||||||
box.pack_start(action_hint, True, True, 0)
|
box.pack_start(self.compact_mode_label, True, True, 0)
|
||||||
return box
|
return box
|
||||||
|
|
||||||
def _build_progress(self):
|
def _build_progress(self):
|
||||||
frame = Gtk.Frame(label="Extraction Progress")
|
frame = Gtk.Frame(label="Extraction Progress")
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=8)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4, margin=6)
|
||||||
frame.add(box)
|
frame.add(box)
|
||||||
|
|
||||||
self.progress_label = Gtk.Label(label="No task running", xalign=0)
|
self.progress_label = Gtk.Label(label="No task running", xalign=0)
|
||||||
@@ -281,6 +326,7 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
pane.set_wide_handle(True)
|
pane.set_wide_handle(True)
|
||||||
pane.pack1(self._build_workspace(), resize=True, shrink=False)
|
pane.pack1(self._build_workspace(), resize=True, shrink=False)
|
||||||
pane.pack2(self._build_log(), resize=False, shrink=False)
|
pane.pack2(self._build_log(), resize=False, shrink=False)
|
||||||
|
pane.set_position(560)
|
||||||
self.main_pane = pane
|
self.main_pane = pane
|
||||||
return pane
|
return pane
|
||||||
|
|
||||||
@@ -289,13 +335,14 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
pane.set_wide_handle(True)
|
pane.set_wide_handle(True)
|
||||||
pane.pack1(self._build_left_panel(), resize=False, shrink=False)
|
pane.pack1(self._build_left_panel(), resize=False, shrink=False)
|
||||||
pane.pack2(self._build_right_panel(), resize=True, shrink=False)
|
pane.pack2(self._build_right_panel(), resize=True, shrink=False)
|
||||||
|
pane.set_position(360)
|
||||||
return pane
|
return pane
|
||||||
|
|
||||||
def _build_left_panel(self):
|
def _build_left_panel(self):
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
||||||
|
|
||||||
info_frame = Gtk.Frame(label="Disc")
|
info_frame = Gtk.Frame(label="Disc")
|
||||||
info_grid = Gtk.Grid(column_spacing=10, row_spacing=6, margin=10)
|
info_grid = Gtk.Grid(column_spacing=8, row_spacing=4, margin=6)
|
||||||
info_frame.add(info_grid)
|
info_frame.add(info_grid)
|
||||||
|
|
||||||
rows = [
|
rows = [
|
||||||
@@ -315,7 +362,7 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
box.pack_start(info_frame, False, False, 0)
|
box.pack_start(info_frame, False, False, 0)
|
||||||
|
|
||||||
release_frame = Gtk.Frame(label="Metadata / Releases")
|
release_frame = Gtk.Frame(label="Metadata / Releases")
|
||||||
release_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8, margin=8)
|
release_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=6)
|
||||||
release_frame.add(release_box)
|
release_frame.add(release_box)
|
||||||
|
|
||||||
self.release_store = Gtk.ListStore(str, str, str, str, str, object)
|
self.release_store = Gtk.ListStore(str, str, str, str, str, object)
|
||||||
@@ -343,7 +390,7 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
details_scroll = Gtk.ScrolledWindow()
|
details_scroll = Gtk.ScrolledWindow()
|
||||||
details_scroll.set_hexpand(True)
|
details_scroll.set_hexpand(True)
|
||||||
details_scroll.set_vexpand(False)
|
details_scroll.set_vexpand(False)
|
||||||
details_scroll.set_min_content_height(120)
|
details_scroll.set_min_content_height(90)
|
||||||
details_frame.add(details_scroll)
|
details_frame.add(details_scroll)
|
||||||
|
|
||||||
self.release_details = Gtk.Label(label="Select a release to inspect it.", xalign=0, yalign=0)
|
self.release_details = Gtk.Label(label="Select a release to inspect it.", xalign=0, yalign=0)
|
||||||
@@ -357,10 +404,10 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
return box
|
return box
|
||||||
|
|
||||||
def _build_right_panel(self):
|
def _build_right_panel(self):
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
|
||||||
tracks_frame = Gtk.Frame(label="Track Extraction Matrix")
|
tracks_frame = Gtk.Frame(label="Track Extraction Matrix")
|
||||||
tracks_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=8)
|
tracks_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=6)
|
||||||
tracks_frame.add(tracks_box)
|
tracks_frame.add(tracks_box)
|
||||||
|
|
||||||
self.track_columns = {
|
self.track_columns = {
|
||||||
@@ -401,67 +448,27 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
|
|
||||||
def _build_rip_options(self):
|
def _build_rip_options(self):
|
||||||
frame = Gtk.Frame(label="Extraction Setup")
|
frame = Gtk.Frame(label="Extraction Setup")
|
||||||
grid = Gtk.Grid(column_spacing=8, row_spacing=6, margin=8)
|
grid = Gtk.Grid(column_spacing=8, row_spacing=4, margin=6)
|
||||||
frame.add(grid)
|
frame.add(grid)
|
||||||
|
|
||||||
self.unknown_check = Gtk.CheckButton(label="Allow unknown disc")
|
grid.attach(Gtk.Label(label="Track Tpl", xalign=0), 0, 0, 1, 1)
|
||||||
self.unknown_check.set_active(True)
|
|
||||||
self.unknown_check.connect("toggled", self._on_unknown_toggled)
|
|
||||||
grid.attach(self.unknown_check, 0, 0, 2, 1)
|
|
||||||
|
|
||||||
self.cdr_check = Gtk.CheckButton(label="Allow CD-R")
|
|
||||||
self.cdr_check.connect("toggled", self._on_settings_changed)
|
|
||||||
grid.attach(self.cdr_check, 2, 0, 1, 1)
|
|
||||||
|
|
||||||
self.keep_going_check = Gtk.CheckButton(label="Continue on failed tracks")
|
|
||||||
self.keep_going_check.set_active(True)
|
|
||||||
self.keep_going_check.connect("toggled", self._on_settings_changed)
|
|
||||||
grid.attach(self.keep_going_check, 0, 1, 2, 1)
|
|
||||||
|
|
||||||
self.overread_check = Gtk.CheckButton(label="Force overread")
|
|
||||||
self.overread_check.connect("toggled", self._on_settings_changed)
|
|
||||||
grid.attach(self.overread_check, 2, 1, 1, 1)
|
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Cover Art", xalign=0), 0, 2, 1, 1)
|
|
||||||
self.cover_art_combo = Gtk.ComboBoxText()
|
|
||||||
self.cover_art_combo.append("", "Disabled")
|
|
||||||
self.cover_art_combo.append("file", "Save file")
|
|
||||||
self.cover_art_combo.append("embed", "Embed only")
|
|
||||||
self.cover_art_combo.append("complete", "File + embed")
|
|
||||||
self.cover_art_combo.set_active(0)
|
|
||||||
self.cover_art_combo.connect("changed", self._on_settings_changed)
|
|
||||||
grid.attach(self.cover_art_combo, 1, 2, 1, 1)
|
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Max Retries", xalign=0), 2, 2, 1, 1)
|
|
||||||
self.max_retries_spin = Gtk.SpinButton.new_with_range(0, 20, 1)
|
|
||||||
self.max_retries_spin.set_value(5)
|
|
||||||
self.max_retries_spin.connect("value-changed", self._on_settings_changed)
|
|
||||||
grid.attach(self.max_retries_spin, 3, 2, 1, 1)
|
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Offset", xalign=0), 0, 3, 1, 1)
|
|
||||||
self.offset_spin = Gtk.SpinButton.new_with_range(-5000, 5000, 1)
|
|
||||||
self.offset_spin.set_value(0)
|
|
||||||
self.offset_spin.connect("value-changed", self._on_settings_changed)
|
|
||||||
grid.attach(self.offset_spin, 1, 3, 1, 1)
|
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Track Tpl", xalign=0), 0, 4, 1, 1)
|
|
||||||
self.track_template_entry = Gtk.Entry()
|
self.track_template_entry = Gtk.Entry()
|
||||||
self.track_template_entry.set_text(cd_command.DEFAULT_TRACK_TEMPLATE)
|
self.track_template_entry.set_text(cd_command.DEFAULT_TRACK_TEMPLATE)
|
||||||
self.track_template_entry.connect("changed", self._on_settings_changed)
|
self.track_template_entry.connect("changed", self._on_settings_changed)
|
||||||
grid.attach(self.track_template_entry, 1, 4, 3, 1)
|
grid.attach(self.track_template_entry, 1, 0, 3, 1)
|
||||||
|
|
||||||
grid.attach(Gtk.Label(label="Disc Tpl", xalign=0), 0, 5, 1, 1)
|
grid.attach(Gtk.Label(label="Disc Tpl", xalign=0), 0, 1, 1, 1)
|
||||||
self.disc_template_entry = Gtk.Entry()
|
self.disc_template_entry = Gtk.Entry()
|
||||||
self.disc_template_entry.set_text(cd_command.DEFAULT_DISC_TEMPLATE)
|
self.disc_template_entry.set_text(cd_command.DEFAULT_DISC_TEMPLATE)
|
||||||
self.disc_template_entry.connect("changed", self._on_settings_changed)
|
self.disc_template_entry.connect("changed", self._on_settings_changed)
|
||||||
grid.attach(self.disc_template_entry, 1, 5, 3, 1)
|
grid.attach(self.disc_template_entry, 1, 1, 3, 1)
|
||||||
|
|
||||||
note = Gtk.Label(
|
note = Gtk.Label(
|
||||||
label="Secure extraction is driven by whipper's native cdparanoia/cdrdao pipeline. The configured drive offset is loaded automatically when available.",
|
label="Advanced templates stay here; fast extraction switches moved to the control center to reduce vertical space.",
|
||||||
xalign=0,
|
xalign=0,
|
||||||
)
|
)
|
||||||
note.set_line_wrap(True)
|
note.set_line_wrap(True)
|
||||||
grid.attach(note, 0, 6, 4, 1)
|
grid.attach(note, 0, 2, 4, 1)
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
@@ -470,7 +477,7 @@ class WhipperGui(Gtk.Application if Gtk is not None else object):
|
|||||||
scroll = Gtk.ScrolledWindow()
|
scroll = Gtk.ScrolledWindow()
|
||||||
scroll.set_hexpand(True)
|
scroll.set_hexpand(True)
|
||||||
scroll.set_vexpand(False)
|
scroll.set_vexpand(False)
|
||||||
scroll.set_min_content_height(220)
|
scroll.set_min_content_height(150)
|
||||||
frame.add(scroll)
|
frame.add(scroll)
|
||||||
|
|
||||||
text_view = Gtk.TextView()
|
text_view = Gtk.TextView()
|
||||||
|
|||||||
Reference in New Issue
Block a user