update location and setting
BIN
config/resources/arrow.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
config/resources/backward.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
config/resources/exit.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
config/resources/forward.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/resources/pause.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
config/resources/play.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
config/resources/settings.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
46
src/main.py
@@ -18,6 +18,7 @@ from kivy.uix.popup import Popup
|
||||
from kivy.uix.textinput import TextInput
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
from kivy.properties import BooleanProperty
|
||||
from kivy.logger import Logger
|
||||
from kivy.animation import Animation
|
||||
from kivy.lang import Builder
|
||||
@@ -61,9 +62,20 @@ class SettingsPopup(Popup):
|
||||
|
||||
|
||||
class SignagePlayer(Widget):
|
||||
from kivy.properties import StringProperty
|
||||
resources_path = StringProperty()
|
||||
from kivy.properties import NumericProperty
|
||||
screen_width = NumericProperty(0)
|
||||
screen_height = NumericProperty(0)
|
||||
|
||||
def set_screen_size(self):
|
||||
"""Get the current screen size and set as properties."""
|
||||
self.screen_width, self.screen_height = Window.size
|
||||
Logger.info(f"Screen size detected: {self.screen_width}x{self.screen_height}")
|
||||
|
||||
is_paused = BooleanProperty(False)
|
||||
def __init__(self, **kwargs):
|
||||
super(SignagePlayer, self).__init__(**kwargs)
|
||||
|
||||
# Initialize variables
|
||||
self.playlist = []
|
||||
self.current_index = 0
|
||||
@@ -72,27 +84,33 @@ class SignagePlayer(Widget):
|
||||
self.is_paused = False
|
||||
self.config = {}
|
||||
self.playlist_version = None
|
||||
|
||||
# Paths
|
||||
self.base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
self.config_dir = os.path.join(self.base_dir, 'config')
|
||||
self.media_dir = os.path.join(self.base_dir, 'media')
|
||||
self.playlists_dir = os.path.join(self.base_dir, 'playlists')
|
||||
self.config_file = os.path.join(self.config_dir, 'app_config.json')
|
||||
|
||||
self.resources_path = os.path.join(self.config_dir, 'resources')
|
||||
# Create directories if they don't exist
|
||||
for directory in [self.config_dir, self.media_dir, self.playlists_dir]:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
|
||||
# Get and set screen size
|
||||
self.set_screen_size()
|
||||
# Bind to window size for fullscreen
|
||||
Window.bind(size=self._update_size)
|
||||
self._update_size(Window, Window.size)
|
||||
# Initialize player
|
||||
Clock.schedule_once(self.initialize_player, 0.1)
|
||||
|
||||
# Hide controls timer
|
||||
self.controls_timer = None
|
||||
|
||||
# Auto-hide controls
|
||||
self.schedule_hide_controls()
|
||||
|
||||
def _update_size(self, instance, value):
|
||||
self.size = value
|
||||
if hasattr(self, 'ids') and 'content_area' in self.ids:
|
||||
self.ids.content_area.size = value
|
||||
|
||||
def initialize_player(self, dt):
|
||||
"""Initialize the player - load config and start playlist checking"""
|
||||
Logger.info("SignagePlayer: Initializing player...")
|
||||
@@ -311,15 +329,11 @@ class SignagePlayer(Widget):
|
||||
source=image_path,
|
||||
allow_stretch=True,
|
||||
keep_ratio=False,
|
||||
size_hint=(1, 1),
|
||||
pos_hint={'x': 0, 'y': 0}
|
||||
size_hint=(1, 1)
|
||||
)
|
||||
|
||||
self.ids.content_area.add_widget(self.current_widget)
|
||||
|
||||
# Schedule next media after duration
|
||||
Clock.schedule_once(self.next_media, duration)
|
||||
|
||||
except Exception as e:
|
||||
Logger.error(f"SignagePlayer: Error playing image {image_path}: {e}")
|
||||
self.next_media()
|
||||
@@ -436,17 +450,13 @@ class SignagePlayer(Widget):
|
||||
|
||||
class SignagePlayerApp(App):
|
||||
def build(self):
|
||||
# Get screen resolution info
|
||||
# Force fullscreen and borderless
|
||||
Window.fullscreen = True
|
||||
Window.borderless = True
|
||||
Logger.info(f"SignagePlayerApp: Screen size: {Window.size}")
|
||||
Logger.info(f"SignagePlayerApp: Available screen size: {Window.system_size if hasattr(Window, 'system_size') else 'N/A'}")
|
||||
|
||||
# Set window to fullscreen and borderless
|
||||
Window.fullscreen = 'auto'
|
||||
Window.borderless = True
|
||||
|
||||
# Hide cursor after 3 seconds of inactivity
|
||||
Clock.schedule_once(self.hide_cursor, 3)
|
||||
|
||||
return SignagePlayer()
|
||||
|
||||
def hide_cursor(self, dt):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#:kivy 2.1.0
|
||||
|
||||
<SignagePlayer>:
|
||||
<SignagePlayer@FloatLayout>:
|
||||
size: root.screen_width, root.screen_height
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0, 0, 0, 1
|
||||
@@ -8,18 +9,19 @@
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
|
||||
# Main content area - FULLSCREEN WIDGET
|
||||
Widget:
|
||||
# Main content area for images/videos
|
||||
FloatLayout:
|
||||
id: content_area
|
||||
size_hint: 1, 1
|
||||
size: root.screen_width, root.screen_height
|
||||
size_hint: None, None
|
||||
pos_hint: {'x': 0, 'y': 0}
|
||||
|
||||
# Status label overlay (shown when no content or errors)
|
||||
# Status label overlay (centered)
|
||||
Label:
|
||||
id: status_label
|
||||
text: 'Loading...'
|
||||
size_hint: None, None
|
||||
size: dp(400), dp(60)
|
||||
size_hint: 0.5, 0.5
|
||||
size: dp(600), dp(120)
|
||||
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
|
||||
color: 1, 1, 1, 1
|
||||
font_size: sp(18)
|
||||
@@ -34,109 +36,65 @@
|
||||
pos: self.pos
|
||||
radius: [dp(15)]
|
||||
|
||||
# Control panel overlay (always on top)
|
||||
# Control panel overlay (bottom center, 5 columns, 1 row)
|
||||
BoxLayout:
|
||||
id: controls_layout
|
||||
orientation: 'horizontal'
|
||||
size_hint: None, None
|
||||
size: dp(450), dp(60)
|
||||
pos_hint: {'right': 0.98, 'top': 0.98}
|
||||
opacity: 0
|
||||
spacing: dp(5)
|
||||
size: dp(340), dp(70)
|
||||
pos_hint: {'center_x': 0.5, 'y': 0.02}
|
||||
opacity: 1
|
||||
spacing: dp(10)
|
||||
padding: dp(10)
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.1, 0.1, 0.1, 0.9
|
||||
rgba: 0.1, 0.1, 0.1, 0.9 # 90% transparent
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(15)]
|
||||
|
||||
# Control buttons
|
||||
radius: [dp(20)]
|
||||
Button:
|
||||
id: prev_btn
|
||||
text: '⏮'
|
||||
id: backward_btn
|
||||
size_hint: None, None
|
||||
size: dp(60), dp(50)
|
||||
font_size: sp(18)
|
||||
background_color: 0.2, 0.2, 0.2, 0.9
|
||||
color: 1, 1, 1, 1
|
||||
size: dp(60), dp(60)
|
||||
background_normal: root.resources_path + '/backward.png'
|
||||
background_down: root.resources_path + '/backward.png'
|
||||
background_color: 1, 1, 1, 0
|
||||
on_press: root.previous_media()
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: self.background_color
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(8)]
|
||||
|
||||
Button:
|
||||
id: play_pause_btn
|
||||
text: '⏸'
|
||||
size_hint: None, None
|
||||
size: dp(60), dp(50)
|
||||
font_size: sp(18)
|
||||
background_color: 0.2, 0.6, 0.2, 0.9
|
||||
color: 1, 1, 1, 1
|
||||
size: dp(60), dp(60)
|
||||
background_normal: root.resources_path + '/play.png'
|
||||
background_down: root.resources_path + '/pause.png'
|
||||
background_color: 1, 1, 1, 0
|
||||
on_press: root.toggle_pause()
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: self.background_color
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(8)]
|
||||
|
||||
Button:
|
||||
id: next_btn
|
||||
text: '⏭'
|
||||
size_hint: None, None
|
||||
size: dp(60), dp(50)
|
||||
font_size: sp(18)
|
||||
background_color: 0.2, 0.2, 0.2, 0.9
|
||||
color: 1, 1, 1, 1
|
||||
on_press: root.next_media()
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: self.background_color
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(8)]
|
||||
|
||||
Button:
|
||||
id: settings_btn
|
||||
text: '⚙'
|
||||
size_hint: None, None
|
||||
size: dp(60), dp(50)
|
||||
font_size: sp(18)
|
||||
background_color: 0.4, 0.4, 0.2, 0.9
|
||||
color: 1, 1, 1, 1
|
||||
size: dp(60), dp(60)
|
||||
background_normal: root.resources_path + '/settings.png'
|
||||
background_down: root.resources_path + '/settings.png'
|
||||
background_color: 1, 1, 1, 0
|
||||
on_press: root.show_settings()
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: self.background_color
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(8)]
|
||||
|
||||
Button:
|
||||
id: exit_btn
|
||||
text: '⏻'
|
||||
size_hint: None, None
|
||||
size: dp(60), dp(50)
|
||||
font_size: sp(18)
|
||||
background_color: 0.6, 0.2, 0.2, 0.9
|
||||
color: 1, 1, 1, 1
|
||||
on_press: root.exit_app()
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: self.background_color
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(8)]
|
||||
size: dp(60), dp(60)
|
||||
background_normal: root.resources_path + '/exit.png'
|
||||
background_down: root.resources_path + '/exit.png'
|
||||
background_color: 1, 1, 1, 0
|
||||
on_press: app.stop()
|
||||
Button:
|
||||
id: forward_btn
|
||||
size_hint: None, None
|
||||
size: dp(60), dp(60)
|
||||
background_normal: root.resources_path + '/forward.png'
|
||||
background_down: root.resources_path + '/forward.png'
|
||||
background_color: 1, 1, 1, 0
|
||||
on_press: root.next_media()
|
||||
|
||||
|
||||
|
||||
|
||||
# Settings popup content
|
||||
|
||||