sinish player

This commit is contained in:
2025-03-26 15:36:10 +02:00
parent 1d207b6ee2
commit 19344bcc7e
10 changed files with 266 additions and 19 deletions

View File

@@ -52,4 +52,11 @@ Contributions are welcome! Please feel free to submit a pull request or open an
## License ## License
This project is licensed under the MIT License. See the LICENSE file for more details. This project is licensed under the MIT License. See the LICENSE file for more details.
install the requirements for fpyplayer
sudo apt-get update
sudo apt-get install libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev libsdl2-mixer-dev
sudo apt-get install libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libswresample-dev libpostproc-dev
pip install ffpyplayer

View File

@@ -0,0 +1 @@
{"screen_orientation": "Landscape", "screen_name": "TvHolBa1", "quickconnect_key": "Initial01!", "server_ip": "192.168.0.115", "port": "5000"}

BIN
src/Resurse/home_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

View File

@@ -4,6 +4,10 @@
Video: Video:
id: video_player id: video_player
allow_stretch: True
keep_ratio: True
size_hint: (1, 1)
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
opacity: 0 opacity: 0
Image: Image:
@@ -12,4 +16,103 @@
keep_ratio: True keep_ratio: True
size_hint: (1, 1) size_hint: (1, 1)
pos_hint: {'center_x': 0.5, 'center_y': 0.5} pos_hint: {'center_x': 0.5, 'center_y': 0.5}
opacity: 0 opacity: 0
Button:
id: settings_button
size_hint: None, None
size: 90, 90
pos_hint: {'right': 0.98, 'bottom': 0.98}
background_normal: './Resurse/home_icon.png'
background_down: './Resurse/home_icon.png'
background_color: 10/255.0, 160/255.0, 255/255.0, 0.80
opacity: 1
on_release: app.open_settings()
<SettingsScreen>:
BoxLayout:
orientation: 'vertical'
padding: 20
spacing: 10
# Input fields in the upper half of the screen
BoxLayout:
orientation: 'vertical'
size_hint_y: 0.2 # Allocate 60% of the screen height for input fields
Label:
text: "Screen Orientation"
size_hint_y: None
height: 40
TextInput:
id: orientation_input
hint_text: "Enter 'portrait' or 'landscape'"
multiline: False
size_hint_y: None
height: 40
Label:
text: "Screen Name"
size_hint_y: None
height: 40
TextInput:
id: screen_name_input
hint_text: "Enter screen name"
multiline: False
size_hint_y: None
height: 40
Label:
text: "QuickConnect Key"
size_hint_y: None
height: 40
TextInput:
id: quickconnect_key_input
hint_text: "Enter QuickConnect key"
multiline: False
size_hint_y: None
height: 40
Label:
text: "Server IP / Hostname"
size_hint_y: None
height: 40
TextInput:
id: server_ip_input
hint_text: "Enter server IP or hostname"
multiline: False
size_hint_y: None
height: 40
Label:
text: "Set Port"
size_hint_y: None
height: 40
TextInput:
id: port_input
hint_text: "Enter port number"
multiline: False
size_hint_y: None
height: 40
# Buttons in the lower part of the screen
BoxLayout:
size_hint_y: 0.4 # Allocate 40% of the screen height for buttons
spacing: 20
Button:
text: "Save"
size_hint_y: None
height: 50
on_release: root.save_config()
Button:
text: "Exit App"
size_hint_y: None
height: 50
on_release: app.stop()

View File

@@ -1,28 +1,50 @@
from kivy.config import Config
Config.set('kivy', 'video', 'ffpyplayer') # Set ffpyplayer as the video provider
from kivy.app import App from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock from kivy.clock import Clock
from kivy.core.window import Window from kivy.core.window import Window
from kivy.uix.video import Video from kivy.uix.video import Video
from kivy.uix.image import Image from kivy.uix.image import Image
from kivy.config import Config
from kivy.logger import Logger from kivy.logger import Logger
from kivy.lang import Builder from kivy.lang import Builder
from python_functions import load_playlist, download_media_files
import os import os
import json
Config.set('kivy', 'keyboard_mode', 'systemanddock') # Import functions from python_functions.py
from python_functions import load_playlist, download_media_files, clean_unused_files
# Load the KV file # Load the KV file
Builder.load_file('kv/media_player.kv') Builder.load_file('kv/media_player.kv')
CONFIG_FILE = './Resurse/app_config.txt'
class MediaPlayer(Screen): class MediaPlayer(Screen):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(MediaPlayer, self).__init__(**kwargs) super(MediaPlayer, self).__init__(**kwargs)
self.playlist = [] self.playlist = []
self.current_index = 0 self.current_index = 0
self.video_player = self.ids.video_player
self.image_display = self.ids.image_display
Clock.schedule_interval(self.check_playlist_updates, 300) # Check for updates every 5 minutes Clock.schedule_interval(self.check_playlist_updates, 300) # Check for updates every 5 minutes
Window.bind(on_key_down=self.on_key_down) Window.bind(on_key_down=self.on_key_down)
# Start a timer to hide the settings button
self.hide_button_timer = Clock.schedule_once(self.hide_settings_button, 10)
def on_touch_down(self, touch):
# Reset the button visibility and restart the hide timer
self.ids.settings_button.opacity = 1
if hasattr(self, 'hide_button_timer'):
Clock.unschedule(self.hide_button_timer)
self.hide_button_timer = Clock.schedule_once(self.hide_settings_button, 10)
return super(MediaPlayer, self).on_touch_down(touch)
def hide_settings_button(self, *args):
# Hide the settings button after inactivity
self.ids.settings_button.opacity = 0
def on_key_down(self, window, key, *args): def on_key_down(self, window, key, *args):
if key == 102: # 'f' key if key == 102: # 'f' key
Window.fullscreen = not Window.fullscreen Window.fullscreen = not Window.fullscreen
@@ -30,6 +52,7 @@ class MediaPlayer(Screen):
def on_enter(self): def on_enter(self):
self.playlist = load_playlist() self.playlist = load_playlist()
download_media_files(self.playlist) download_media_files(self.playlist)
clean_unused_files(self.playlist) # Clean up unused files
self.play_media() self.play_media()
def play_media(self): def play_media(self):
@@ -43,21 +66,31 @@ class MediaPlayer(Screen):
base_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') # Update this to the correct path base_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') # Update this to the correct path
file_path = os.path.join(base_dir, file_name) file_path = os.path.join(base_dir, file_name)
Logger.info(f"Playing media: {file_path}")
if file_extension in ['.mp4', '.avi', '.mov']: if file_extension in ['.mp4', '.avi', '.mov']:
self.video_player.source = file_path self.play_video(file_path)
self.video_player.opacity = 1
self.video_player.play()
self.image_display.opacity = 0
Clock.schedule_once(self.next_media, duration)
elif file_extension in ['.jpg', '.jpeg', '.png', '.gif']: elif file_extension in ['.jpg', '.jpeg', '.png', '.gif']:
self.image_display.source = file_path self.show_image(file_path, duration)
self.image_display.opacity = 1
self.video_player.opacity = 0
self.image_display.reload()
Clock.schedule_once(self.next_media, duration)
else: else:
Logger.error(f"Unsupported media type for file: {file_name}") Logger.error(f"Unsupported media type for file: {file_name}")
def play_video(self, file_path):
Logger.info(f"Playing video: {file_path}")
self.video_player.source = file_path
self.video_player.opacity = 1
self.video_player.state = 'play'
self.image_display.opacity = 0
Clock.schedule_once(self.next_media, self.video_player.duration)
def show_image(self, file_path, duration):
Logger.info(f"Showing image: {file_path}")
self.image_display.source = file_path
self.image_display.opacity = 1
self.image_display.reload()
self.video_player.opacity = 0
Clock.schedule_once(self.next_media, duration)
def next_media(self, dt): def next_media(self, dt):
self.current_index = (self.current_index + 1) % len(self.playlist) self.current_index = (self.current_index + 1) % len(self.playlist)
self.play_media() self.play_media()
@@ -65,14 +98,61 @@ class MediaPlayer(Screen):
def check_playlist_updates(self, dt): def check_playlist_updates(self, dt):
self.playlist = load_playlist() self.playlist = load_playlist()
download_media_files(self.playlist) download_media_files(self.playlist)
clean_unused_files(self.playlist) # Clean up unused files
self.play_media() self.play_media()
class SettingsScreen(Screen):
def __init__(self, **kwargs):
super(SettingsScreen, self).__init__(**kwargs)
self.config_data = self.load_config()
def load_config(self):
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as file:
return json.load(file)
return {
"screen_orientation": "",
"screen_name": "",
"quickconnect_key": "",
"server_ip": "",
"port": "" # Default port
}
def save_config(self):
# Save the input values to the config file
self.config_data["screen_orientation"] = self.ids.orientation_input.text
self.config_data["screen_name"] = self.ids.screen_name_input.text
self.config_data["quickconnect_key"] = self.ids.quickconnect_key_input.text
self.config_data["server_ip"] = self.ids.server_ip_input.text
self.config_data["port"] = self.ids.port_input.text
with open(CONFIG_FILE, 'w') as file:
json.dump(self.config_data, file)
Logger.info("SettingsScreen: Configuration saved.")
# Return to the MediaPlayer screen after saving
self.manager.current = 'media_player'
def on_pre_enter(self):
# Populate input fields with current config data
self.ids.orientation_input.text = self.config_data.get("screen_orientation", "landscape")
self.ids.screen_name_input.text = self.config_data.get("screen_name", "")
self.ids.quickconnect_key_input.text = self.config_data.get("quickconnect_key", "")
self.ids.server_ip_input.text = self.config_data.get("server_ip", "")
self.ids.port_input.text = self.config_data.get("port", "8080")
class MediaPlayerApp(App): class MediaPlayerApp(App):
def build(self): def build(self):
Window.fullscreen = True Window.fullscreen = True
sm = ScreenManager() sm = ScreenManager()
sm.add_widget(MediaPlayer(name='media_player')) sm.add_widget(MediaPlayer(name='media_player'))
sm.add_widget(SettingsScreen(name='settings'))
return sm return sm
def open_settings(self):
self.root.current = 'settings'
if __name__ == '__main__': if __name__ == '__main__':
MediaPlayerApp().run() MediaPlayerApp().run()

View File

@@ -3,13 +3,45 @@ import os
import json import json
from kivy.logger import Logger from kivy.logger import Logger
CONFIG_FILE = './Resurse/app_config.txt'
def load_config():
"""Load configuration from app_config.txt."""
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as file:
return json.load(file)
else:
Logger.error(f"Configuration file {CONFIG_FILE} not found.")
return {
"screen_orientation": "Landscape",
"screen_name": "",
"quickconnect_key": "",
"server_ip": "",
"port": ""
}
# Load configuration and initialize variables
config_data = load_config()
server = config_data.get("server_ip", "")
host = config_data.get("screen_name", "")
quick = config_data.get("quickconnect_key", "")
port = config_data.get("port", "")
# Determine the configuration status
if server and host and quick and port:
config_status = "ok"
else:
config_status = "not_ok"
Logger.info(f"Configuration loaded: server={server}, host={host}, quick={quick}, port={port}")
Logger.info(f"Configuration status: {config_status}")
def load_playlist(): def load_playlist():
# Load playlist from the server or local storage # Load playlist from the server or local storage
try: try:
Logger.debug("Attempting to load playlist from server...") Logger.debug("Attempting to load playlist from server...")
server_ip = 'http://192.168.0.115:5000' server_ip = f'http://{server}:{port}'
hostname = 'TvHolBa1' hostname = host
quickconnect_code = 'Initial01!' quickconnect_code = quick
url = f'{server_ip}/api/playlists' url = f'{server_ip}/api/playlists'
params = { params = {
'hostname': hostname, 'hostname': hostname,
@@ -55,4 +87,28 @@ def download_media_files(playlist):
else: else:
Logger.error(f"Failed to download {file_name}: {response.status_code}") Logger.error(f"Failed to download {file_name}: {response.status_code}")
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
Logger.error(f"Failed to download {file_name}: {e}") Logger.error(f"Failed to download {file_name}: {e}")
def clean_unused_files(playlist):
base_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') # Update this to the correct path
if not os.path.exists(base_dir):
Logger.debug(f"Directory {base_dir} does not exist. No files to clean.")
return
# Get all file names from the playlist
playlist_files = {media.get('file_name', '') for media in playlist}
# Get all files in the directory
all_files = set(os.listdir(base_dir))
# Determine unused files
unused_files = all_files - playlist_files
# Delete unused files
for file_name in unused_files:
file_path = os.path.join(base_dir, file_name)
try:
os.remove(file_path)
Logger.debug(f"Deleted unused file: {file_path}")
except OSError as e:
Logger.error(f"Failed to delete {file_path}: {e}")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 742 KiB