sinish player
This commit is contained in:
@@ -53,3 +53,10 @@ 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.
|
||||
|
||||
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
|
||||
|
||||
1
src/Resurse/app_config.txt
Normal file
1
src/Resurse/app_config.txt
Normal 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
BIN
src/Resurse/home_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Binary file not shown.
@@ -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:
|
||||
@@ -13,3 +17,102 @@
|
||||
size_hint: (1, 1)
|
||||
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
|
||||
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()
|
||||
@@ -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()
|
||||
@@ -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,
|
||||
@@ -56,3 +88,27 @@ def download_media_files(playlist):
|
||||
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}")
|
||||
|
||||
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 |
Reference in New Issue
Block a user