Create desktop app due to mkv issue with chrome
This commit is contained in:
107
desktop-client/vlc_player.py
Normal file
107
desktop-client/vlc_player.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import vlc
|
||||
import sys
|
||||
from PyQt6.QtWidgets import QFrame
|
||||
from PyQt6.QtCore import Qt, pyqtSignal, QObject
|
||||
import threading
|
||||
|
||||
class VLCSignals(QObject):
|
||||
state_changed = pyqtSignal(bool, int) # is_playing, time_ms
|
||||
time_changed = pyqtSignal(int) # time_ms
|
||||
|
||||
class VLCSyncPlayer:
|
||||
def __init__(self, frame: QFrame):
|
||||
self.frame = frame
|
||||
self.signals = VLCSignals()
|
||||
|
||||
# Initialize VLC instance
|
||||
# --no-xlib prevents crashes on Linux
|
||||
# --drop-late-frames improves sync by not delaying playback when CPU is slow
|
||||
self.instance = vlc.Instance("--no-xlib", "--drop-late-frames")
|
||||
self.media_player = self.instance.media_player_new()
|
||||
|
||||
# Embed the VLC player into the provided PyQt QFrame
|
||||
# On Windows, PyQt6 widgets don't have a native handle by default
|
||||
self.frame.setAttribute(Qt.WidgetAttribute.WA_NativeWindow)
|
||||
|
||||
if sys.platform.startswith('linux'):
|
||||
self.media_player.set_xwindow(int(self.frame.winId()))
|
||||
elif sys.platform == "win32":
|
||||
# For Windows, we must explicitly cast winId() to int
|
||||
self.media_player.set_hwnd(int(self.frame.winId()))
|
||||
elif sys.platform == "darwin":
|
||||
self.media_player.set_nsobject(int(self.frame.winId()))
|
||||
|
||||
# Register Event Callbacks
|
||||
self.events = self.media_player.event_manager()
|
||||
self.events.event_attach(vlc.EventType.MediaPlayerPlaying, self._on_playing)
|
||||
self.events.event_attach(vlc.EventType.MediaPlayerPaused, self._on_paused)
|
||||
self.events.event_attach(vlc.EventType.MediaPlayerTimeChanged, self._on_time_changed)
|
||||
|
||||
# Local State
|
||||
self.is_playing = False
|
||||
self.current_time_ms = 0
|
||||
self.ignore_next_event = False
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def load_media(self, path: str):
|
||||
media = self.instance.media_new(path)
|
||||
self.media_player.set_media(media)
|
||||
|
||||
def play(self):
|
||||
with self.lock:
|
||||
self.ignore_next_event = True
|
||||
self.media_player.play()
|
||||
|
||||
def pause(self):
|
||||
with self.lock:
|
||||
self.ignore_next_event = True
|
||||
self.media_player.set_pause(1)
|
||||
|
||||
def seek(self, position_ms: int):
|
||||
with self.lock:
|
||||
self.ignore_next_event = True
|
||||
self.media_player.set_time(position_ms)
|
||||
|
||||
def set_volume(self, volume: int):
|
||||
self.media_player.audio_set_volume(volume)
|
||||
|
||||
def get_volume(self) -> int:
|
||||
return self.media_player.audio_get_volume()
|
||||
|
||||
# --- Internal VLC Callbacks ---
|
||||
|
||||
@vlc.callbackmethod
|
||||
def _on_playing(self, event):
|
||||
self.is_playing = True
|
||||
with self.lock:
|
||||
if self.ignore_next_event:
|
||||
self.ignore_next_event = False
|
||||
return
|
||||
|
||||
time_ms = self.media_player.get_time()
|
||||
# Fire signal to PyQt thread
|
||||
self.signals.state_changed.emit(True, time_ms)
|
||||
|
||||
@vlc.callbackmethod
|
||||
def _on_paused(self, event):
|
||||
self.is_playing = False
|
||||
with self.lock:
|
||||
if self.ignore_next_event:
|
||||
self.ignore_next_event = False
|
||||
return
|
||||
|
||||
time_ms = self.media_player.get_time()
|
||||
self.signals.state_changed.emit(False, time_ms)
|
||||
|
||||
@vlc.callbackmethod
|
||||
def _on_time_changed(self, event):
|
||||
# Emitted constantly during playback
|
||||
self.current_time_ms = event.u.new_time
|
||||
# We also want to fire this signal so the UI scrubber/time label can update
|
||||
self.signals.time_changed.emit(self.current_time_ms)
|
||||
|
||||
def get_length(self):
|
||||
return self.media_player.get_length()
|
||||
|
||||
def stop(self):
|
||||
self.media_player.stop()
|
||||
Reference in New Issue
Block a user