feat: auto-detect iPod connection via QTimer polling + fix AlreadyMounted in _mount_linux
This commit is contained in:
parent
71d162520c
commit
25bb8105ac
23
src/app.py
23
src/app.py
@ -16,6 +16,7 @@ from PyQt6.QtWidgets import (
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from config_loader import ConfigLoader
|
||||
from device_monitor import DeviceMonitor
|
||||
from hotkeys import HotkeyManager
|
||||
from ui.library_tab import LibraryTab
|
||||
from ui.ipod_tab import iPodTab
|
||||
@ -36,6 +37,7 @@ class MainWindow(QMainWindow):
|
||||
|
||||
self.config_loader = ConfigLoader()
|
||||
self.hotkey_manager: Optional[HotkeyManager] = None
|
||||
self.device_monitor: Optional[DeviceMonitor] = None
|
||||
self.ipod_tab_index = 1
|
||||
|
||||
self._setup_ui()
|
||||
@ -46,6 +48,7 @@ class MainWindow(QMainWindow):
|
||||
self.library_tab._scan_library()
|
||||
self.library_tab._resume_playback_if_saved()
|
||||
self.ipod_tab._on_refresh_devices_clicked()
|
||||
self._start_device_monitor()
|
||||
|
||||
def _setup_ui(self):
|
||||
main_widget = QWidget()
|
||||
@ -74,6 +77,7 @@ class MainWindow(QMainWindow):
|
||||
self.settings_tab.hide_library_buttons_changed.connect(
|
||||
self.library_tab.set_buttons_visible
|
||||
)
|
||||
self.settings_tab.auto_detect_changed.connect(self._on_auto_detect_toggled)
|
||||
|
||||
def _on_device_mounted(self, mount_point: str):
|
||||
self.library_tab.on_device_mounted(mount_point)
|
||||
@ -85,6 +89,23 @@ class MainWindow(QMainWindow):
|
||||
self.tab_widget.setTabVisible(self.ipod_tab_index, False)
|
||||
self.statusBar().showMessage("iPod disconnected")
|
||||
|
||||
def _start_device_monitor(self):
|
||||
auto_detect = self.config_loader.get_boolean("Device", "auto_detect", fallback=True)
|
||||
self.device_monitor = DeviceMonitor()
|
||||
if auto_detect:
|
||||
self.ipod_tab.start_monitoring(self.device_monitor)
|
||||
self.device_monitor.start()
|
||||
|
||||
def _on_auto_detect_toggled(self, enabled: bool):
|
||||
if not self.device_monitor:
|
||||
return
|
||||
if enabled:
|
||||
self.ipod_tab.start_monitoring(self.device_monitor)
|
||||
self.device_monitor.start()
|
||||
else:
|
||||
self.device_monitor.stop()
|
||||
self.ipod_tab.stop_monitoring(self.device_monitor)
|
||||
|
||||
def _load_settings(self):
|
||||
config = self.config_loader
|
||||
self.library_tab.load_player_settings(config)
|
||||
@ -128,6 +149,8 @@ class MainWindow(QMainWindow):
|
||||
self.settings_tab.refresh_shortcuts_table()
|
||||
|
||||
def closeEvent(self, event):
|
||||
if self.device_monitor:
|
||||
self.device_monitor.stop()
|
||||
self._save_settings()
|
||||
event.accept()
|
||||
|
||||
|
||||
38
src/device_monitor.py
Normal file
38
src/device_monitor.py
Normal file
@ -0,0 +1,38 @@
|
||||
import logging
|
||||
from typing import Set
|
||||
|
||||
from PyQt6.QtCore import QObject, QTimer, pyqtSignal
|
||||
|
||||
from ipod_device import IPodDevice
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DeviceMonitor(QObject):
|
||||
|
||||
state_changed = pyqtSignal(list)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self._poll)
|
||||
self._device_ids: Set[str] = set()
|
||||
|
||||
def start(self, interval_ms: int = 3000):
|
||||
self._device_ids.clear()
|
||||
self._poll()
|
||||
self.timer.start(interval_ms)
|
||||
|
||||
def stop(self):
|
||||
self.timer.stop()
|
||||
self._device_ids.clear()
|
||||
|
||||
def _poll(self):
|
||||
try:
|
||||
devices = IPodDevice().detect_devices()
|
||||
current_ids = {d["id"] for d in devices}
|
||||
if current_ids != self._device_ids:
|
||||
self._device_ids = current_ids
|
||||
self.state_changed.emit(devices)
|
||||
except Exception:
|
||||
logger.exception("DeviceMonitor poll failed")
|
||||
@ -294,6 +294,24 @@ class IPodDevice:
|
||||
self.logger.error(f"Device {device_id} does not exist")
|
||||
return None
|
||||
|
||||
# Check if already mounted via findmnt (avoids udisksctl AlreadyMounted error)
|
||||
try:
|
||||
fm_result = subprocess.run(
|
||||
["findmnt", "-n", "-o", "TARGET", device_id],
|
||||
capture_output=True, check=False, text=True
|
||||
)
|
||||
if fm_result.returncode == 0 and fm_result.stdout.strip():
|
||||
mount_point = fm_result.stdout.strip()
|
||||
if os.path.exists(mount_point):
|
||||
self.mount_point = mount_point
|
||||
self._block_device = device_id
|
||||
name = device_id[5:]
|
||||
self._parent_drive = f"/dev/{name.rstrip('0123456789')}"
|
||||
self.logger.info(f"Device already mounted at: {mount_point}")
|
||||
return mount_point
|
||||
except Exception:
|
||||
self.logger.warning("findmnt check failed, falling through to udisksctl")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["udisksctl", "mount", "-b", device_id],
|
||||
|
||||
@ -11,6 +11,7 @@ from PyQt6.QtWidgets import (
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
|
||||
from ipod_device import IPodDevice
|
||||
from device_monitor import DeviceMonitor
|
||||
from worker import WorkerThread
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -486,6 +487,20 @@ class iPodTab(QWidget):
|
||||
QMessageBox.information(self, "Duplicates" if success else "Error", message)
|
||||
self._cleanup_worker()
|
||||
|
||||
def start_monitoring(self, monitor: DeviceMonitor):
|
||||
monitor.state_changed.connect(self._on_auto_state_changed)
|
||||
|
||||
def stop_monitoring(self, monitor: DeviceMonitor):
|
||||
try:
|
||||
monitor.state_changed.disconnect(self._on_auto_state_changed)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def _on_auto_state_changed(self, devices):
|
||||
if self.worker_thread and self.worker_thread.isRunning():
|
||||
return
|
||||
self._on_device_detection_finished(True, f"Found {len(devices)} devices", devices)
|
||||
|
||||
def _cleanup_worker(self):
|
||||
if self.worker_thread:
|
||||
self.worker_thread.wait(3000)
|
||||
|
||||
@ -20,6 +20,7 @@ class SettingsTab(QWidget):
|
||||
"""Settings tab widget."""
|
||||
|
||||
hide_library_buttons_changed = pyqtSignal(bool)
|
||||
auto_detect_changed = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
@ -80,8 +81,9 @@ class SettingsTab(QWidget):
|
||||
self.clean_temp_check.setChecked(True)
|
||||
advanced_layout.addWidget(self.clean_temp_check)
|
||||
|
||||
self.auto_detect_check = QCheckBox("Auto-Detect iPod on Startup")
|
||||
self.auto_detect_check = QCheckBox("Auto-Detect iPod")
|
||||
self.auto_detect_check.setChecked(True)
|
||||
self.auto_detect_check.toggled.connect(self.auto_detect_changed.emit)
|
||||
advanced_layout.addWidget(self.auto_detect_check)
|
||||
|
||||
self.hide_library_buttons_check = QCheckBox("Hide Library Toolbar Buttons")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user