final touches
This commit is contained in:
146
tkinter_app/src/media_playback_controller.py
Normal file
146
tkinter_app/src/media_playback_controller.py
Normal file
@@ -0,0 +1,146 @@
|
||||
import threading
|
||||
import os
|
||||
from logging_config import Logger
|
||||
from PIL import Image, ImageTk
|
||||
|
||||
class MediaPlaybackController:
|
||||
def __init__(self, app, ui):
|
||||
self.app = app # Reference to SimpleMediaPlayerApp
|
||||
self.ui = ui # Reference to PlayerUI
|
||||
self.auto_advance_timer = None
|
||||
self.current_index = 0
|
||||
self.playlist = []
|
||||
self.scaling_mode = 'fit'
|
||||
|
||||
def set_playlist(self, playlist):
|
||||
self.playlist = playlist
|
||||
self.current_index = 0
|
||||
|
||||
def play_current_media(self):
|
||||
if not self.playlist or self.current_index >= len(self.playlist):
|
||||
self.show_no_content_message()
|
||||
return
|
||||
media = self.playlist[self.current_index]
|
||||
file_path = media.get('url', '')
|
||||
file_name = media.get('file_name', '')
|
||||
duration = media.get('duration', 10)
|
||||
if file_path.startswith('static/resurse/'):
|
||||
absolute_path = os.path.join(os.path.dirname(__file__), file_path)
|
||||
file_path = absolute_path
|
||||
Logger.info(f"Playing media: {file_name} from {file_path}")
|
||||
self.log_event(file_name, "STARTED")
|
||||
self.cancel_timers()
|
||||
if file_path.startswith('text://'):
|
||||
self.show_text_content(file_path[7:], duration)
|
||||
elif file_path.lower().endswith(('.mp4', '.avi', '.mov', '.mkv')):
|
||||
self.play_video(file_path)
|
||||
elif os.path.exists(file_path) and file_path.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp')):
|
||||
self.show_image(file_path, duration)
|
||||
else:
|
||||
Logger.error(f"Unsupported or missing media: {file_path}")
|
||||
self.ui.status_label.config(text=f"Missing or unsupported media:\n{file_name}")
|
||||
self.auto_advance_timer = self.ui.root.after(5000, self.next_media)
|
||||
|
||||
def play_video(self, file_path):
|
||||
self.ui.status_label.place_forget()
|
||||
def run_vlc_subprocess():
|
||||
import subprocess
|
||||
try:
|
||||
Logger.info(f"Starting system VLC subprocess for video: {file_path}")
|
||||
vlc_cmd = [
|
||||
'cvlc',
|
||||
'--fullscreen',
|
||||
'--no-osd',
|
||||
'--no-video-title-show',
|
||||
'--play-and-exit',
|
||||
'--quiet',
|
||||
file_path
|
||||
]
|
||||
proc = subprocess.Popen(vlc_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
proc.wait()
|
||||
Logger.info(f"VLC subprocess finished: {file_path}")
|
||||
except Exception as e:
|
||||
Logger.error(f"VLC subprocess error: {e}")
|
||||
finally:
|
||||
self.ui.root.after_idle(lambda: setattr(self, 'auto_advance_timer', self.ui.root.after(1000, self.next_media)))
|
||||
threading.Thread(target=run_vlc_subprocess, daemon=True).start()
|
||||
|
||||
def show_image(self, file_path, duration):
|
||||
try:
|
||||
self.ui.status_label.place_forget()
|
||||
self.ui.status_label.config(text="")
|
||||
img = Image.open(file_path)
|
||||
original_size = img.size
|
||||
screen_width = self.ui.root.winfo_width()
|
||||
screen_height = self.ui.root.winfo_height()
|
||||
if screen_width <= 1 or screen_height <= 1:
|
||||
screen_width = 1920
|
||||
screen_height = 1080
|
||||
final_img, offset = self.ui.scale_image_to_screen(img, screen_width, screen_height, self.scaling_mode)
|
||||
photo = ImageTk.PhotoImage(final_img)
|
||||
self.ui.image_label.config(image=photo)
|
||||
self.ui.image_label.image = photo
|
||||
Logger.info(f"Successfully displayed image: {os.path.basename(file_path)} "
|
||||
f"(Original: {original_size}, Screen: {screen_width}x{screen_height}, "
|
||||
f"Mode: {self.scaling_mode}, Offset: {offset})")
|
||||
self.auto_advance_timer = self.ui.root.after(
|
||||
int(duration * 1000),
|
||||
self.next_media
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.error(f"Failed to show image {file_path}: {e}")
|
||||
self.ui.image_label.config(image='')
|
||||
self.ui.status_label.config(text=f"Image Error:\n{os.path.basename(file_path)}\n{str(e)}")
|
||||
self.ui.status_label.place(relx=0.5, rely=0.5, anchor='center')
|
||||
self.auto_advance_timer = self.ui.root.after(5000, self.next_media)
|
||||
|
||||
def show_text_content(self, text, duration):
|
||||
self.ui.image_label.config(image='')
|
||||
self.ui.status_label.config(text=text)
|
||||
self.auto_advance_timer = self.ui.root.after(
|
||||
int(duration * 1000),
|
||||
self.next_media
|
||||
)
|
||||
|
||||
def next_media(self):
|
||||
self.cancel_timers()
|
||||
if not self.playlist:
|
||||
return
|
||||
self.current_index = (self.current_index + 1) % len(self.playlist)
|
||||
self.play_current_media()
|
||||
|
||||
def previous_media(self):
|
||||
self.cancel_timers()
|
||||
if not self.playlist:
|
||||
return
|
||||
self.current_index = (self.current_index - 1) % len(self.playlist)
|
||||
self.play_current_media()
|
||||
|
||||
def toggle_play_pause(self):
|
||||
self.app.is_paused = not self.app.is_paused
|
||||
if self.app.is_paused:
|
||||
self.ui.play_pause_btn.config(text="▶ Play")
|
||||
self.cancel_timers()
|
||||
else:
|
||||
self.ui.play_pause_btn.config(text="⏸ Pause")
|
||||
self.play_current_media()
|
||||
Logger.info(f"Media {'paused' if self.app.is_paused else 'resumed'}")
|
||||
|
||||
def cancel_timers(self):
|
||||
if self.auto_advance_timer:
|
||||
self.ui.root.after_cancel(self.auto_advance_timer)
|
||||
self.auto_advance_timer = None
|
||||
|
||||
def show_no_content_message(self):
|
||||
self.ui.status_label.config(text="No content available.")
|
||||
|
||||
def log_event(self, file_name, event):
|
||||
import datetime
|
||||
try:
|
||||
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
log_message = f"{timestamp} - {event}: {file_name}\n"
|
||||
log_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'log.txt')
|
||||
with open(log_file, 'a') as f:
|
||||
f.write(log_message)
|
||||
except Exception as e:
|
||||
Logger.error(f"Failed to log event: {e}")
|
||||
Reference in New Issue
Block a user