diff --git a/README.md b/README.md index f6631de..8c507c1 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,11 @@ Contributions are welcome! Please feel free to submit a pull request or open an ## License -This project is licensed under the MIT License. See the LICENSE file for more details. \ No newline at end of file +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 diff --git a/src/Resurse/app_config.txt b/src/Resurse/app_config.txt new file mode 100644 index 0000000..c8c68c3 --- /dev/null +++ b/src/Resurse/app_config.txt @@ -0,0 +1 @@ +{"screen_orientation": "Landscape", "screen_name": "TvHolBa1", "quickconnect_key": "Initial01!", "server_ip": "192.168.0.115", "port": "5000"} \ No newline at end of file diff --git a/src/Resurse/home_icon.png b/src/Resurse/home_icon.png new file mode 100644 index 0000000..7be1c59 Binary files /dev/null and b/src/Resurse/home_icon.png differ diff --git a/src/__pycache__/python_functions.cpython-311.pyc b/src/__pycache__/python_functions.cpython-311.pyc index 3b84cfd..3277728 100644 Binary files a/src/__pycache__/python_functions.cpython-311.pyc and b/src/__pycache__/python_functions.cpython-311.pyc differ diff --git a/src/instance/dashboard.db b/src/instance/dashboard.db deleted file mode 100644 index c7310f7..0000000 Binary files a/src/instance/dashboard.db and /dev/null differ diff --git a/src/kv/media_player.kv b/src/kv/media_player.kv index 2412504..4cb8380 100644 --- a/src/kv/media_player.kv +++ b/src/kv/media_player.kv @@ -4,6 +4,10 @@ Video: 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 Image: @@ -12,4 +16,103 @@ keep_ratio: True size_hint: (1, 1) pos_hint: {'center_x': 0.5, 'center_y': 0.5} - opacity: 0 \ No newline at end of file + 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() + +: + 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() \ No newline at end of file diff --git a/src/media_player.py b/src/media_player.py index 47c02fd..e77c3bf 100644 --- a/src/media_player.py +++ b/src/media_player.py @@ -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.uix.screenmanager import ScreenManager, Screen from kivy.clock import Clock from kivy.core.window import Window from kivy.uix.video import Video from kivy.uix.image import Image -from kivy.config import Config from kivy.logger import Logger from kivy.lang import Builder -from python_functions import load_playlist, download_media_files 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 Builder.load_file('kv/media_player.kv') +CONFIG_FILE = './Resurse/app_config.txt' + class MediaPlayer(Screen): def __init__(self, **kwargs): super(MediaPlayer, self).__init__(**kwargs) self.playlist = [] 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 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): if key == 102: # 'f' key Window.fullscreen = not Window.fullscreen @@ -30,6 +52,7 @@ class MediaPlayer(Screen): def on_enter(self): self.playlist = load_playlist() download_media_files(self.playlist) + clean_unused_files(self.playlist) # Clean up unused files self.play_media() 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 file_path = os.path.join(base_dir, file_name) + Logger.info(f"Playing media: {file_path}") + if file_extension in ['.mp4', '.avi', '.mov']: - self.video_player.source = file_path - self.video_player.opacity = 1 - self.video_player.play() - self.image_display.opacity = 0 - Clock.schedule_once(self.next_media, duration) + self.play_video(file_path) elif file_extension in ['.jpg', '.jpeg', '.png', '.gif']: - self.image_display.source = file_path - self.image_display.opacity = 1 - self.video_player.opacity = 0 - self.image_display.reload() - Clock.schedule_once(self.next_media, duration) + self.show_image(file_path, duration) else: 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): self.current_index = (self.current_index + 1) % len(self.playlist) self.play_media() @@ -65,14 +98,61 @@ class MediaPlayer(Screen): def check_playlist_updates(self, dt): self.playlist = load_playlist() download_media_files(self.playlist) + clean_unused_files(self.playlist) # Clean up unused files 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): def build(self): Window.fullscreen = True sm = ScreenManager() sm.add_widget(MediaPlayer(name='media_player')) + sm.add_widget(SettingsScreen(name='settings')) return sm + def open_settings(self): + self.root.current = 'settings' + + if __name__ == '__main__': MediaPlayerApp().run() \ No newline at end of file diff --git a/src/python_functions.py b/src/python_functions.py index de91d12..0a30d05 100644 --- a/src/python_functions.py +++ b/src/python_functions.py @@ -3,13 +3,45 @@ import os import json 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(): # Load playlist from the server or local storage try: Logger.debug("Attempting to load playlist from server...") - server_ip = 'http://192.168.0.115:5000' - hostname = 'TvHolBa1' - quickconnect_code = 'Initial01!' + server_ip = f'http://{server}:{port}' + hostname = host + quickconnect_code = quick url = f'{server_ip}/api/playlists' params = { 'hostname': hostname, @@ -55,4 +87,28 @@ def download_media_files(playlist): else: Logger.error(f"Failed to download {file_name}: {response.status_code}") except requests.exceptions.RequestException as e: - Logger.error(f"Failed to download {file_name}: {e}") \ No newline at end of file + 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}") \ No newline at end of file diff --git a/src/static/resurse/harting_contact_2.jpg b/src/static/resurse/harting_contact_2.jpg deleted file mode 100644 index efc69e4..0000000 Binary files a/src/static/resurse/harting_contact_2.jpg and /dev/null differ diff --git a/src/static/resurse/harting_contactor.jpg b/src/static/resurse/harting_contactor.jpg deleted file mode 100644 index d188777..0000000 Binary files a/src/static/resurse/harting_contactor.jpg and /dev/null differ