mirror of
https://github.com/usetrmnl/byos_fastapi.git
synced 2026-04-29 13:44:09 -07:00
feat: preserve user-defined playlists during plugin refresh
Add has_persistent_playlist flag to prevent auto-filling playlists when user has configured specific playlist selections. This protects user preferences during plugin refresh cycles while maintaining auto-fill behavior for fresh installations. Test added to verify playlist persistence across plugin refresh operations.
This commit is contained in:
@@ -442,3 +442,76 @@ def test_preview_sequence_stores_each_frame():
|
||||
assert resp_preview.content == expected_bytes
|
||||
finally:
|
||||
_reset_device_context(device_id)
|
||||
|
||||
|
||||
def test_playlist_persistence_across_plugin_refresh():
|
||||
"""Test that user-defined playlists are preserved during plugin refresh."""
|
||||
# Setup initial state with a user-defined playlist
|
||||
master = _prime_rotation_master()
|
||||
device_id = 'test-persistence'
|
||||
_reset_device_context(device_id)
|
||||
|
||||
# Set a user-defined playlist (only hashB, not all entries)
|
||||
user_playlist = ['hashB']
|
||||
|
||||
# Save to database to simulate existing persistent state
|
||||
state.persist_default_playlist(user_playlist)
|
||||
|
||||
# Reset rotation master to initial state to simulate fresh server start
|
||||
_prime_rotation_master()
|
||||
# Clear selected_ids and has_persistent_playlist to simulate fresh start
|
||||
state.rotation_master()['selected_ids'] = []
|
||||
state.rotation_master()['has_persistent_playlist'] = False
|
||||
|
||||
# Initialize playlists from storage (this should set has_persistent_playlist flag)
|
||||
state.initialize_rotation_playlists_from_storage()
|
||||
|
||||
# Verify the playlist was loaded and flag was set
|
||||
assert master['selected_ids'] == user_playlist
|
||||
assert master.get('has_persistent_playlist') == True
|
||||
|
||||
# Simulate what happens when set_primary_rotation_assets is called during plugin refresh
|
||||
from trmnl_server.plugins.base import PluginOutput
|
||||
from trmnl_server.services import state as state_module
|
||||
from trmnl_server import utils
|
||||
from io import BytesIO
|
||||
|
||||
# Create mock plugin assets
|
||||
dummy_path = utils.asset_path('img', 'dummy.bmp')
|
||||
with open(dummy_path, 'rb') as handle:
|
||||
bmp_bytes = handle.read()
|
||||
png_bytes = utils.convert_bmp_bytes_to_png(BytesIO(bmp_bytes)).getvalue()
|
||||
|
||||
# Write temp files for the assets
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
bmp_path = os.path.join(tmp_dir, 'test.bmp')
|
||||
png_path = os.path.join(tmp_dir, 'test.png')
|
||||
|
||||
with open(bmp_path, 'wb') as f:
|
||||
f.write(bmp_bytes)
|
||||
with open(png_path, 'wb') as f:
|
||||
f.write(png_bytes)
|
||||
|
||||
assets = PluginOutput(
|
||||
monochrome_path=bmp_path,
|
||||
grayscale_path=png_path
|
||||
)
|
||||
|
||||
try:
|
||||
# This should NOT overwrite the user playlist since it already exists and has_persistent_playlist flag is set
|
||||
state_module.set_primary_rotation_assets('TestPlugin', assets)
|
||||
|
||||
# Verify the user playlist is preserved
|
||||
assert master['selected_ids'] == user_playlist, f"Expected {user_playlist}, got {master['selected_ids']}"
|
||||
assert len(master['meta']) > 0, "Meta entries should be populated"
|
||||
|
||||
# Verify the playlist wasn't auto-filled with all available entries
|
||||
available_ids = [entry.get('id') for entry in master['meta'] if entry.get('id')]
|
||||
assert len(available_ids) > len(user_playlist), "Should have multiple available entries"
|
||||
assert master['selected_ids'] != available_ids, "Should not auto-fill with all available entries"
|
||||
|
||||
finally:
|
||||
_reset_device_context(device_id)
|
||||
|
||||
@@ -187,7 +187,8 @@ global_state: Dict[str, Any] = {
|
||||
'hashes': [],
|
||||
'meta': [],
|
||||
'selected_ids': [],
|
||||
'version': 0
|
||||
'version': 0,
|
||||
'has_persistent_playlist': False
|
||||
},
|
||||
'devices': {},
|
||||
'device_playlists': {},
|
||||
@@ -413,6 +414,7 @@ def initialize_rotation_playlists_from_storage() -> None:
|
||||
with STATE_LOCK:
|
||||
master = rotation_master()
|
||||
master['selected_ids'] = default_playlist
|
||||
master['has_persistent_playlist'] = True
|
||||
for device_id, selected_ids in models.list_device_playlists():
|
||||
cache_device_playlist(device_id, selected_ids)
|
||||
|
||||
@@ -688,6 +690,7 @@ def set_default_playlist(playlist_ids: List[str]) -> None:
|
||||
with STATE_LOCK:
|
||||
master = rotation_master()
|
||||
master['selected_ids'] = playlist_ids
|
||||
master['has_persistent_playlist'] = False # Reset flag when user explicitly sets playlist
|
||||
master['version'] += 1
|
||||
selection_snapshot = list(master['selected_ids'])
|
||||
persist_default_playlist(selection_snapshot)
|
||||
@@ -916,7 +919,9 @@ def set_primary_rotation_assets(
|
||||
if not had_entries:
|
||||
master['version'] += 1
|
||||
|
||||
if (not master.get('selected_ids')) or auto_fill_enabled:
|
||||
# Only auto-fill if there's no existing playlist AND no persistent playlist was loaded
|
||||
# This prevents overwriting user-defined playlists during plugin refresh
|
||||
if not master.get('selected_ids') and not master.get('has_persistent_playlist'):
|
||||
master['selected_ids'] = [entry.get('id') for entry in meta_list if entry.get('id')]
|
||||
selection_snapshot = list(master['selected_ids'])
|
||||
|
||||
@@ -991,7 +996,9 @@ def append_rotation_assets(
|
||||
)
|
||||
)
|
||||
master['version'] += 1
|
||||
if (not master.get('selected_ids')) or auto_fill_enabled:
|
||||
# Only auto-fill if there's no existing playlist AND no persistent playlist was loaded
|
||||
# This prevents overwriting user-defined playlists during plugin refresh
|
||||
if not master.get('selected_ids') and not master.get('has_persistent_playlist'):
|
||||
master['selected_ids'] = [entry.get('id') for entry in meta_list if entry.get('id')]
|
||||
selection_snapshot = list(master['selected_ids'])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user