updated player to version
@@ -3,7 +3,7 @@
|
||||
# filepath: /home/pi/Desktop/signage-player/run_app.sh
|
||||
|
||||
# Navigate to the application directory
|
||||
cd /home/pi/signage-player/src || exit
|
||||
cd /home/pi/Desktop/signage-player/src || exit
|
||||
|
||||
# Check for the --verbose flag
|
||||
if [[ "$1" == "--verbose" ]]; then
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"screen_orientation": "Landscape", "screen_name": "tv-terasa", "quickconnect_key": "8887779", "server_ip": "digi-signage.moto-adv.com", "port": "80", "screen_w": "1920", "screen_h": "1080"}
|
||||
{"screen_orientation": "Landscape", "screen_name": "rpi-tv11", "quickconnect_key": "8887779", "server_ip": "192.168.1.74", "port": "5000", "screen_w": "1920", "screen_h": "1080"}
|
||||
@@ -1,15 +1,2 @@
|
||||
2025-06-19 16:03:02 - STARTED: IMG-20250526-WA0003.jpg
|
||||
2025-06-19 16:03:03 - STARTED: IMG-20250602-WA0011.jpg
|
||||
2025-06-19 16:03:23 - STARTED: IMG_20250601_192845.jpg
|
||||
2025-06-19 16:03:43 - STARTED: IMG_20250601_185017.jpg
|
||||
2025-06-19 16:04:03 - STARTED: IMG_20250601_185019.jpg
|
||||
2025-06-19 16:04:23 - STARTED: IMG_20250601_180727.jpg
|
||||
2025-06-19 16:04:43 - STARTED: IMG_20250601_174724.jpg
|
||||
2025-06-19 16:05:03 - STARTED: IMG-20250531-WA0070.jpg
|
||||
2025-06-19 16:05:23 - STARTED: IMG-20250604-WA0006.jpg
|
||||
2025-06-19 16:05:43 - STARTED: IMG-20250526-WA0003.jpg
|
||||
2025-06-19 16:06:03 - STARTED: IMG-20250602-WA0011.jpg
|
||||
2025-06-19 16:06:23 - STARTED: IMG_20250601_192845.jpg
|
||||
2025-06-19 16:06:43 - STARTED: IMG_20250601_185017.jpg
|
||||
2025-06-19 16:39:23 - STARTED: IMG-20250526-WA0003.jpg
|
||||
2025-06-19 16:39:23 - STARTED: IMG-20250602-WA0011.jpg
|
||||
2025-06-20 16:32:40 - STARTED: edit_pencil.png
|
||||
2025-06-20 16:33:00 - STARTED: delete.png
|
||||
|
||||
BIN
src/__pycache__/python_functions2.cpython-311.pyc
Normal file
@@ -23,7 +23,7 @@ import datetime # Import datetime for timestamping logs
|
||||
import subprocess
|
||||
|
||||
# Import functions from python_functions.py
|
||||
from python_functions import load_playlist, download_media_files, clean_unused_files
|
||||
from python_functions import load_local_playlist, download_media_files, clean_unused_files, fetch_server_playlist
|
||||
|
||||
# Load the KV file for UI layout
|
||||
Builder.load_file('kv/media_player.kv')
|
||||
@@ -102,10 +102,36 @@ class MediaPlayer(Screen):
|
||||
|
||||
def on_enter(self):
|
||||
"""Called when the screen is entered."""
|
||||
self.playlist = load_playlist() # Load the playlist (local or server)
|
||||
if self.playlist: # Only proceed if the playlist is not empty
|
||||
download_media_files(self.playlist) # Download media files if the playlist has changed
|
||||
clean_unused_files(self.playlist) # Remove unused files from the resource folder
|
||||
Logger.info("MediaPlayer: Entering screen...")
|
||||
|
||||
# Attempt to load the local playlist
|
||||
self.playlist = load_local_playlist()
|
||||
Logger.info(f"MediaPlayer: Loaded local playlist: {self.playlist}")
|
||||
|
||||
if not self.playlist: # If no local playlist exists
|
||||
Logger.warning("MediaPlayer: No local playlist found. Fetching from server...")
|
||||
|
||||
# Fetch the server playlist
|
||||
server_playlist_data = fetch_server_playlist()
|
||||
server_playlist = server_playlist_data.get('playlist', [])
|
||||
server_version = server_playlist_data.get('version', 0)
|
||||
|
||||
if server_playlist: # If server playlist is valid
|
||||
Logger.info("MediaPlayer: Server playlist fetched successfully.")
|
||||
|
||||
# Download media files and save the playlist locally
|
||||
download_media_files(server_playlist, server_version)
|
||||
self.playlist = load_local_playlist() # Reload the updated local playlist
|
||||
|
||||
if self.playlist:
|
||||
Logger.info("MediaPlayer: Local playlist updated successfully.")
|
||||
else:
|
||||
Logger.error("MediaPlayer: Failed to update local playlist.")
|
||||
else:
|
||||
Logger.error("MediaPlayer: Failed to fetch server playlist. No media to play.")
|
||||
return
|
||||
|
||||
if self.playlist: # If the playlist is loaded successfully
|
||||
self.play_media() # Start playing media
|
||||
self.show_buttons() # Ensure buttons are visible when the screen is entered
|
||||
else:
|
||||
@@ -159,7 +185,7 @@ class MediaPlayer(Screen):
|
||||
Logger.error(f"Failed to clean up old logs: {e}")
|
||||
|
||||
def play_media(self):
|
||||
# Play the current media in the playlist.
|
||||
"""Play the current media in the playlist."""
|
||||
if self.playlist:
|
||||
media = self.playlist[self.current_index] # Get the current media
|
||||
file_name = media.get('file_name', '') # Get the file name
|
||||
@@ -214,11 +240,11 @@ class MediaPlayer(Screen):
|
||||
Logger.warning("Video duration is unknown. Using default duration")
|
||||
|
||||
def show_image(self, file_path, duration):
|
||||
# Display an image with a fade-in effect.
|
||||
"""Display an image with a fade-in effect."""
|
||||
Logger.info(f"Showing image: {file_path}")
|
||||
if not os.path.exists(file_path):
|
||||
Logger.error(f"Image file not found: {file_path}")
|
||||
#return # Ensure this return is properly indented within the method
|
||||
return
|
||||
|
||||
# Set the image source
|
||||
self.image_display.source = file_path
|
||||
@@ -320,14 +346,28 @@ class MediaPlayer(Screen):
|
||||
self.ids.play_pause_button.background_normal = './Resurse/play.png'
|
||||
|
||||
def check_playlist_updates(self, dt):
|
||||
#Check for updates to the playlist."""
|
||||
new_playlist = load_playlist() # Load the new playlist
|
||||
if new_playlist != self.playlist: # Compare the new playlist with the current one
|
||||
Logger.info("Playlist updated. Changes detected.")
|
||||
self.updated_playlist = new_playlist # Store the updated playlist
|
||||
self.is_playlist_update_pending = True # Mark the update as pending
|
||||
"""Check for updates to the playlist."""
|
||||
Logger.info("Checking for playlist updates...")
|
||||
|
||||
# Fetch the server playlist
|
||||
server_playlist_data = fetch_server_playlist() # Fetch the playlist from the server
|
||||
server_playlist = server_playlist_data.get('playlist', [])
|
||||
server_version = server_playlist_data.get('version', 0)
|
||||
|
||||
# Load the local playlist
|
||||
local_playlist_data = load_local_playlist() # Load the local playlist
|
||||
local_version = local_playlist_data.get('version', 0) if local_playlist_data else 0
|
||||
|
||||
# Compare versions
|
||||
if server_version != local_version: # If versions differ
|
||||
Logger.info(f"Playlist version mismatch detected. Local version: {local_version}, Server version: {server_version}")
|
||||
|
||||
# Update the local playlist and download new media files
|
||||
download_media_files(server_playlist) # Download media files from the server
|
||||
self.playlist = load_local_playlist() # Reload the updated local playlist
|
||||
Logger.info("Playlist updated successfully.")
|
||||
else:
|
||||
Logger.info("Playlist update skipped. No changes detected.")
|
||||
Logger.info("Playlist versions match. No update needed.")
|
||||
|
||||
class SettingsScreen(Screen):
|
||||
"""Settings screen for configuring the app."""
|
||||
|
||||
@@ -2,9 +2,12 @@ import os
|
||||
import json
|
||||
import requests
|
||||
from kivy.logger import Logger
|
||||
import bcrypt
|
||||
import time
|
||||
|
||||
CONFIG_FILE = './Resurse/app_config.txt'
|
||||
LOCAL_PLAYLIST_FILE = './static/local_playlist.json' # Path to the local playlist file
|
||||
|
||||
def load_config():
|
||||
"""Load configuration from app_config.txt."""
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
@@ -14,49 +17,55 @@ def load_config():
|
||||
return json.load(file)
|
||||
except json.JSONDecodeError as e:
|
||||
Logger.error(f"python_functions: Failed to parse configuration file. Error: {e}")
|
||||
return {
|
||||
"screen_orientation": "Landscape",
|
||||
"screen_name": "",
|
||||
"quickconnect_key": "",
|
||||
"server_ip": "",
|
||||
"port": ""
|
||||
}
|
||||
return {}
|
||||
else:
|
||||
Logger.error(f"python_functions: Configuration file {CONFIG_FILE} not found.")
|
||||
return {
|
||||
"screen_orientation": "Landscape",
|
||||
"screen_name": "",
|
||||
"quickconnect_key": "",
|
||||
"server_ip": "",
|
||||
"port": ""
|
||||
}
|
||||
return {}
|
||||
|
||||
# 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"
|
||||
port = config_data.get("port", "")
|
||||
|
||||
Logger.info(f"python_functions: Configuration loaded: server={server}, host={host}, quick={quick}, port={port}")
|
||||
Logger.info(f"python_functions: Configuration status: {config_status}")
|
||||
|
||||
def load_local_playlist():
|
||||
"""Load the playlist from local storage."""
|
||||
if os.path.exists(LOCAL_PLAYLIST_FILE):
|
||||
try:
|
||||
with open(LOCAL_PLAYLIST_FILE, 'r') as local_file:
|
||||
local_playlist = json.load(local_file)
|
||||
Logger.info(f"python_functions: Local playlist loaded: {local_playlist}")
|
||||
if isinstance(local_playlist, dict) and 'playlist' in local_playlist and 'version' in local_playlist:
|
||||
return local_playlist.get('playlist', [])
|
||||
else:
|
||||
Logger.error("python_functions: Invalid local playlist structure.")
|
||||
return []
|
||||
except json.JSONDecodeError as e:
|
||||
Logger.error(f"python_functions: Failed to parse local playlist file. Error: {e}")
|
||||
return []
|
||||
else:
|
||||
Logger.warning("python_functions: Local playlist file not found.")
|
||||
return []
|
||||
|
||||
def load_playlist():
|
||||
"""Load playlist from the server or local storage."""
|
||||
# Check if the local playlist exists and is valid
|
||||
local_playlist = load_local_playlist()
|
||||
if local_playlist:
|
||||
Logger.info("python_functions: Using local playlist.")
|
||||
return local_playlist # Skip server download if local playlist is valid
|
||||
def save_local_playlist(playlist):
|
||||
"""Save the updated playlist locally."""
|
||||
if not playlist or 'playlist' not in playlist:
|
||||
Logger.error("python_functions: Invalid playlist data. Cannot save local playlist.")
|
||||
return
|
||||
|
||||
try:
|
||||
Logger.info("python_functions: Attempting to load playlist from server...")
|
||||
with open(LOCAL_PLAYLIST_FILE, 'w') as local_file:
|
||||
json.dump(playlist, local_file)
|
||||
Logger.info("python_functions: Updated local playlist with server data.")
|
||||
except IOError as e:
|
||||
Logger.error(f"python_functions: Failed to save local playlist: {e}")
|
||||
|
||||
def fetch_server_playlist():
|
||||
"""Fetch the updated playlist from the server."""
|
||||
try:
|
||||
server_ip = f'{server}:{port}' # Construct the server IP with port
|
||||
url = f'http://{server_ip}/api/playlists'
|
||||
params = {
|
||||
@@ -65,104 +74,83 @@ def load_playlist():
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
# Print the raw response content and status code for debugging
|
||||
Logger.debug(f"python_functions: Status Code: {response.status_code}")
|
||||
Logger.debug(f"python_functions: Response Content: {response.text}")
|
||||
|
||||
# Check if the request was successful
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
# Parse the JSON response
|
||||
server_data = response.json()
|
||||
playlist = server_data.get('playlist', [])
|
||||
response_data = response.json()
|
||||
playlist = response_data.get('playlist', [])
|
||||
version = response_data.get('playlist_version', None)
|
||||
hashed_quickconnect = response_data.get('hashed_quickconnect', None)
|
||||
|
||||
# Validate the playlist structure
|
||||
if isinstance(playlist, list) and all(isinstance(item, dict) for item in playlist):
|
||||
Logger.info("python_functions: Playlist loaded successfully.")
|
||||
Logger.debug(f"python_functions: Loaded playlist: {playlist}")
|
||||
|
||||
# Save the playlist locally
|
||||
with open(LOCAL_PLAYLIST_FILE, 'w') as local_file:
|
||||
json.dump(playlist, local_file)
|
||||
Logger.info("python_functions: Updated local playlist with server data.")
|
||||
|
||||
return playlist
|
||||
if version is not None and hashed_quickconnect is not None:
|
||||
if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')):
|
||||
Logger.info("python_functions: Fetched updated playlist from server.")
|
||||
return {'playlist': playlist, 'version': version}
|
||||
else:
|
||||
Logger.error("python_functions: Invalid playlist structure from server.")
|
||||
except json.JSONDecodeError as e:
|
||||
Logger.error(f"python_functions: Failed to parse JSON response: {e}")
|
||||
Logger.error("python_functions: Quickconnect code validation failed.")
|
||||
else:
|
||||
Logger.error("python_functions: Failed to retrieve playlist or hashed quickconnect from the response.")
|
||||
else:
|
||||
Logger.error(f"python_functions: Failed to retrieve playlist. Status Code: {response.status_code}")
|
||||
Logger.error(f"python_functions: Failed to fetch playlist. Status Code: {response.status_code}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
Logger.error(f"python_functions: Failed to load playlist: {e}")
|
||||
Logger.error(f"python_functions: Failed to fetch playlist: {e}")
|
||||
|
||||
# Fallback to local playlist
|
||||
return local_playlist
|
||||
return {'playlist': [], 'version': 0}
|
||||
|
||||
|
||||
def load_local_playlist():
|
||||
"""Load the playlist from local storage."""
|
||||
if os.path.exists(LOCAL_PLAYLIST_FILE):
|
||||
try:
|
||||
with open(LOCAL_PLAYLIST_FILE, 'r') as local_file:
|
||||
local_playlist = json.load(local_file)
|
||||
|
||||
# Validate the structure of the local playlist
|
||||
if isinstance(local_playlist, list) and all(isinstance(item, dict) for item in local_playlist):
|
||||
Logger.info("python_functions: Loaded and validated local playlist.")
|
||||
return local_playlist
|
||||
else:
|
||||
Logger.error("python_functions: Invalid local playlist structure.")
|
||||
return []
|
||||
except json.JSONDecodeError:
|
||||
Logger.error("python_functions: Failed to parse local playlist file.")
|
||||
return []
|
||||
else:
|
||||
Logger.warning("python_functions: Local playlist file not found.")
|
||||
return []
|
||||
|
||||
def download_media_files(playlist):
|
||||
"""Download media files from the playlist."""
|
||||
def download_media_files(playlist, version):
|
||||
"""Download media files from the server and update the local playlist."""
|
||||
Logger.info("python_functions: Starting media file download...")
|
||||
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') # Path to the local folder
|
||||
if not os.path.exists(base_dir):
|
||||
os.makedirs(base_dir)
|
||||
Logger.info(f"python_functions: Created directory {base_dir} for media files.")
|
||||
|
||||
updated_playlist = [] # List to store updated media entries
|
||||
|
||||
for media in playlist:
|
||||
file_name = media.get('file_name', '')
|
||||
file_url = media.get('url', '')
|
||||
duration = media.get('duration', 10) # Default duration if not provided
|
||||
file_path = os.path.join(base_dir, file_name)
|
||||
|
||||
try:
|
||||
response = requests.get(file_url)
|
||||
if response.status_code == 200:
|
||||
with open(file_path, 'wb') as file:
|
||||
file.write(response.content)
|
||||
Logger.info(f"python_functions: Downloaded {file_name} to {file_path}")
|
||||
else:
|
||||
Logger.error(f"python_functions: Failed to download {file_name}: {response.status_code}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
Logger.error(f"python_functions: Failed to download {file_name}: {e}")
|
||||
Logger.debug(f"python_functions: Preparing to download {file_name} from {file_url}...")
|
||||
|
||||
if os.path.exists(file_path):
|
||||
Logger.info(f"python_functions: File {file_name} already exists. Skipping download.")
|
||||
else:
|
||||
try:
|
||||
response = requests.get(file_url, timeout=10)
|
||||
if response.status_code == 200:
|
||||
with open(file_path, 'wb') as file:
|
||||
file.write(response.content)
|
||||
Logger.info(f"python_functions: Successfully downloaded {file_name} to {file_path}")
|
||||
else:
|
||||
Logger.error(f"python_functions: Failed to download {file_name}. Status Code: {response.status_code}")
|
||||
continue
|
||||
except requests.exceptions.RequestException as e:
|
||||
Logger.error(f"python_functions: Error downloading {file_name}: {e}")
|
||||
continue
|
||||
|
||||
updated_media = {
|
||||
'file_name': file_name,
|
||||
'url': file_path, # Update URL to local path
|
||||
'duration': duration
|
||||
}
|
||||
updated_playlist.append(updated_media)
|
||||
|
||||
# Save the updated playlist locally
|
||||
save_local_playlist({'playlist': updated_playlist, 'version': version})
|
||||
|
||||
def clean_unused_files(playlist):
|
||||
"""Remove unused media files from the resource folder."""
|
||||
Logger.info("python_functions: Cleaning unused media files...")
|
||||
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')
|
||||
if not os.path.exists(base_dir):
|
||||
Logger.debug(f"python_functions: 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:
|
||||
|
||||
248
src/python_functions2.py
Normal file
@@ -0,0 +1,248 @@
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
from kivy.logger import Logger
|
||||
import bcrypt
|
||||
import time
|
||||
|
||||
CONFIG_FILE = './Resurse/app_config.txt'
|
||||
LOCAL_PLAYLIST_FILE = './static/local_playlist.json' # Path to the local playlist file
|
||||
def load_config():
|
||||
"""Load configuration from app_config.txt."""
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
try:
|
||||
with open(CONFIG_FILE, 'r') as file:
|
||||
Logger.info("python_functions: Configuration file loaded successfully.")
|
||||
return json.load(file)
|
||||
except json.JSONDecodeError as e:
|
||||
Logger.error(f"python_functions: Failed to parse configuration file. Error: {e}")
|
||||
return {
|
||||
"screen_orientation": "Landscape",
|
||||
"screen_name": "",
|
||||
"quickconnect_key": "",
|
||||
"server_ip": "",
|
||||
"port": ""
|
||||
}
|
||||
else:
|
||||
Logger.error(f"python_functions: 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", "")
|
||||
print(server, host, quick, port)
|
||||
# Determine the configuration status
|
||||
if server and host and quick and port:
|
||||
config_status = "ok"
|
||||
else:
|
||||
config_status = "not_ok"
|
||||
|
||||
Logger.info(f"python_functions: Configuration loaded: server={server}, host={host}, quick={quick}, port={port}")
|
||||
Logger.info(f"python_functions: Configuration status: {config_status}")
|
||||
|
||||
|
||||
def load_playlist():
|
||||
"""Load playlist from the server or local storage and periodically check for updates."""
|
||||
local_playlist = load_local_playlist()
|
||||
local_version = local_playlist.get('version', 0) if local_playlist else 0
|
||||
|
||||
while True:
|
||||
try:
|
||||
Logger.info("python_functions: Checking playlist version on the server...")
|
||||
server_ip = f'{server}:{port}' # Construct the server IP with port
|
||||
url = f'http://{server_ip}/api/playlist_version'
|
||||
params = {
|
||||
'hostname': host,
|
||||
'quickconnect_code': quick
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
response_data = response.json()
|
||||
server_version = response_data.get('playlist_version', None)
|
||||
hashed_quickconnect = response_data.get('hashed_quickconnect', None)
|
||||
|
||||
if server_version is not None and hashed_quickconnect is not None:
|
||||
# Validate the quickconnect code using bcrypt
|
||||
if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')):
|
||||
Logger.info(f"python_functions: Server playlist version: {server_version}, Local playlist version: {local_version}")
|
||||
|
||||
if server_version != local_version:
|
||||
Logger.info("python_functions: Playlist versions differ. Updating local playlist...")
|
||||
updated_playlist = fetch_server_playlist()
|
||||
if updated_playlist and 'playlist' in updated_playlist:
|
||||
save_local_playlist(updated_playlist) # Update local playlist
|
||||
local_version = server_version # Update local version
|
||||
else:
|
||||
Logger.error("python_functions: Failed to update local playlist. Using existing playlist.")
|
||||
else:
|
||||
Logger.info("python_functions: Playlist versions match. No update needed.")
|
||||
else:
|
||||
Logger.error("python_functions: Quickconnect code validation failed.")
|
||||
else:
|
||||
Logger.error("python_functions: Failed to retrieve playlist version or hashed quickconnect from the response.")
|
||||
else:
|
||||
Logger.error(f"python_functions: Failed to retrieve playlist version. Status Code: {response.status_code}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
Logger.error(f"python_functions: Failed to check playlist version: {e}")
|
||||
|
||||
# Wait for 5 minutes before checking again
|
||||
time.sleep(300)
|
||||
|
||||
def load_local_playlist():
|
||||
"""Load the playlist from local storage."""
|
||||
if os.path.exists(LOCAL_PLAYLIST_FILE):
|
||||
try:
|
||||
with open(LOCAL_PLAYLIST_FILE, 'r') as local_file:
|
||||
local_playlist = json.load(local_file)
|
||||
|
||||
# Validate the structure of the local playlist
|
||||
if isinstance(local_playlist, dict) and 'playlist' in local_playlist and 'version' in local_playlist:
|
||||
Logger.info("python_functions: Loaded and validated local playlist.")
|
||||
return local_playlist
|
||||
else:
|
||||
Logger.error("python_functions: Invalid local playlist structure.")
|
||||
return None
|
||||
except json.JSONDecodeError:
|
||||
Logger.error("python_functions: Failed to parse local playlist file.")
|
||||
return None
|
||||
else:
|
||||
Logger.warning("python_functions: Local playlist file not found.")
|
||||
return None
|
||||
|
||||
def download_media_files(playlist):
|
||||
"""Download media files from the server and update the local playlist."""
|
||||
Logger.info("python_functions: Starting media file download...")
|
||||
base_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') # Path to the local folder
|
||||
if not os.path.exists(base_dir):
|
||||
os.makedirs(base_dir)
|
||||
Logger.info(f"python_functions: Created directory {base_dir} for media files.")
|
||||
|
||||
updated_playlist = [] # List to store updated media entries
|
||||
|
||||
for media in playlist:
|
||||
file_name = media.get('file_name', '')
|
||||
file_url = media.get('url', '')
|
||||
duration = media.get('duration', 10) # Default duration if not provided
|
||||
file_path = os.path.join(base_dir, file_name)
|
||||
|
||||
Logger.debug(f"python_functions: Preparing to download {file_name} from {file_url}...")
|
||||
|
||||
if os.path.exists(file_path):
|
||||
Logger.info(f"python_functions: File {file_name} already exists. Skipping download.")
|
||||
else:
|
||||
try:
|
||||
response = requests.get(file_url, timeout=10) # Add timeout for better error handling
|
||||
if response.status_code == 200:
|
||||
with open(file_path, 'wb') as file:
|
||||
file.write(response.content)
|
||||
Logger.info(f"python_functions: Successfully downloaded {file_name} to {file_path}")
|
||||
else:
|
||||
Logger.error(f"python_functions: Failed to download {file_name}. Status Code: {response.status_code}")
|
||||
continue
|
||||
except requests.exceptions.RequestException as e:
|
||||
Logger.error(f"python_functions: Error downloading {file_name}: {e}")
|
||||
continue
|
||||
|
||||
# Update the media entry with the local file path and duration
|
||||
updated_media = {
|
||||
'file_name': file_name,
|
||||
'url': file_path, # Update URL to local path
|
||||
'duration': duration
|
||||
}
|
||||
updated_playlist.append(updated_media)
|
||||
|
||||
# Save the updated playlist locally
|
||||
save_local_playlist({'playlist': updated_playlist, 'version': playlist.get('version', 0)})
|
||||
|
||||
def clean_unused_files(playlist):
|
||||
"""Remove unused media files from the resource folder."""
|
||||
Logger.info("python_functions: Cleaning unused media files...")
|
||||
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"python_functions: 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.info(f"python_functions: Deleted unused file: {file_path}")
|
||||
except OSError as e:
|
||||
Logger.error(f"python_functions: Failed to delete {file_path}: {e}")
|
||||
|
||||
def fetch_server_playlist():
|
||||
"""Fetch the updated playlist from the server."""
|
||||
try:
|
||||
server_ip = f'{server}:{port}' # Construct the server IP with port
|
||||
url = f'http://{server_ip}/api/playlists'
|
||||
params = {
|
||||
'hostname': host,
|
||||
'quickconnect_code': quick
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
response_data = response.json()
|
||||
playlist = response_data.get('playlist', [])
|
||||
version = response_data.get('playlist_version', None)
|
||||
hashed_quickconnect = response_data.get('hashed_quickconnect', None)
|
||||
|
||||
if version is not None and hashed_quickconnect is not None:
|
||||
# Validate the quickconnect code using bcrypt
|
||||
if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')):
|
||||
Logger.info("python_functions: Fetched updated playlist from server.")
|
||||
return {'playlist': playlist, 'version': version}
|
||||
else:
|
||||
Logger.error("python_functions: Quickconnect code validation failed.")
|
||||
else:
|
||||
Logger.error("python_functions: Failed to retrieve playlist or hashed quickconnect from the response.")
|
||||
else:
|
||||
Logger.error(f"python_functions: Failed to fetch playlist. Status Code: {response.status_code}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
Logger.error(f"python_functions: Failed to fetch playlist: {e}")
|
||||
|
||||
# Return an empty playlist if fetching fails
|
||||
return {'playlist': [], 'version': 0}
|
||||
|
||||
def save_local_playlist(playlist):
|
||||
"""Save the updated playlist locally."""
|
||||
if not playlist or 'playlist' not in playlist:
|
||||
Logger.error("python_functions: Invalid playlist data. Cannot save local playlist.")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(LOCAL_PLAYLIST_FILE, 'w') as local_file:
|
||||
json.dump(playlist, local_file)
|
||||
Logger.info("python_functions: Updated local playlist with server data.")
|
||||
except IOError as e:
|
||||
Logger.error(f"python_functions: Failed to save local playlist: {e}")
|
||||
|
||||
def check_playlist_updates(self, dt):
|
||||
"""Check for updates to the playlist."""
|
||||
new_playlist = load_playlist() # Load the new playlist
|
||||
if new_playlist != self.playlist: # Compare the new playlist with the current one
|
||||
Logger.info("Playlist updated. Changes detected.")
|
||||
self.updated_playlist = new_playlist # Store the updated playlist
|
||||
self.is_playlist_update_pending = True # Mark the update as pending
|
||||
else:
|
||||
Logger.info("Playlist update skipped. No changes detected.")
|
||||
@@ -1 +1 @@
|
||||
[{"duration": 20, "file_name": "IMG-20250526-WA0003.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG-20250526-WA0003.jpg"}, {"duration": 20, "file_name": "IMG-20250602-WA0011.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG-20250602-WA0011.jpg"}, {"duration": 20, "file_name": "IMG_20250601_192845.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG_20250601_192845.jpg"}, {"duration": 20, "file_name": "IMG_20250601_185017.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG_20250601_185017.jpg"}, {"duration": 20, "file_name": "IMG_20250601_185019.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG_20250601_185019.jpg"}, {"duration": 20, "file_name": "IMG_20250601_180727.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG_20250601_180727.jpg"}, {"duration": 20, "file_name": "IMG_20250601_174724.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG_20250601_174724.jpg"}, {"duration": 20, "file_name": "IMG-20250531-WA0070.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG-20250531-WA0070.jpg"}, {"duration": 20, "file_name": "IMG-20250604-WA0006.jpg", "url": "http://digi-signage.moto-adv.com/media/IMG-20250604-WA0006.jpg"}]
|
||||
{"playlist": [{"file_name": "edit_pencil.png", "url": "/home/pi/Desktop/signage-player/src/static/resurse/edit_pencil.png", "duration": 20}, {"file_name": "delete.png", "url": "/home/pi/Desktop/signage-player/src/static/resurse/delete.png", "duration": 20}], "version": 2}
|
||||
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 842 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 637 KiB |
|
Before Width: | Height: | Size: 8.8 MiB |
|
Before Width: | Height: | Size: 10 MiB |
|
Before Width: | Height: | Size: 8.2 MiB |
|
Before Width: | Height: | Size: 7.2 MiB |
|
Before Width: | Height: | Size: 10 MiB |
BIN
src/static/resurse/delete.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/static/resurse/edit_pencil.png
Normal file
|
After Width: | Height: | Size: 77 KiB |