node player groups wip

This commit is contained in:
jr-k 2024-07-16 14:31:57 +02:00
parent 84da36d953
commit 6c37e1ae25
23 changed files with 227 additions and 165 deletions

File diff suppressed because one or more lines are too long

View File

@ -58,84 +58,74 @@
margin: 0 0 20px 0;
}
//.playlist-holder {
// margin: 20px 20px 20px 10px;
// flex: 1;
//
// .form-holder {
// margin: 20px 0 0 0;
//
// form {
// max-width: initial;
// }
//
// .form-group {
// flex-grow: 0;
// margin-bottom: 5px;
// }
// }
//
//
// .preview-holder {
// .form-group {
// flex-grow: 0;
// margin-bottom: 0;
// }
// }
//
// h4 {
// font-size: 14px;
// display: flex;
// flex-direction: row;
// justify-content: flex-start;
// align-items: center;
// align-self: stretch;
// color: white;
// padding-bottom: 10px;
// text-decoration: none;
//
// &.divide {
// border-top: 1px solid #222;
// margin-top: 20px;
// padding-top: 20px;
// }
// }
//
// .qrcode-pic {
// margin-top: 10px;
//
// img {
// border: 1px dashed #555;
// padding: 5px;
// border-radius: $baseRadius;
// }
// }
//
// .preview {
// background: black;
// border: 1px solid rgba($white, .3);
// border-radius: $baseRadius;
// justify-content: center;
// align-items: center;
// align-self: stretch;
// display: flex;
// margin: 10px 0 20px 0;
// height: 300px;
//
// iframe {
// flex: 1;
// align-self: stretch;
// }
// }
//}
.node-player-group-holder {
margin: 20px 20px 20px 10px;
flex: 1;
//.slides-holder {
// align-self: stretch;
// border-right: 1px solid #222;
// margin: 20px 10px 20px 20px;
// padding-right: 20px;
// flex: 1.3;
//}
.form-holder {
margin: 20px 0 0 0;
form {
max-width: initial;
}
.form-group {
flex-grow: 0;
margin-bottom: 15px;
}
}
.preview-holder {
.form-group {
flex-grow: 0;
margin-bottom: 0;
}
}
h4 {
font-size: 14px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
align-self: stretch;
color: white;
padding-bottom: 10px;
text-decoration: none;
&.divide {
border-top: 1px solid #222;
margin-top: 20px;
padding-top: 20px;
}
}
.preview {
background: black;
border: 1px solid rgba($white, .3);
border-radius: $baseRadius;
justify-content: center;
align-items: center;
align-self: stretch;
display: flex;
margin: 10px 0 20px 0;
height: 300px;
iframe {
flex: 1;
align-self: stretch;
}
}
}
.players-holder {
align-self: stretch;
border-right: 1px solid #222;
margin: 20px 10px 20px 20px;
padding-right: 20px;
flex: 1.3;
}
}
}

View File

@ -181,8 +181,8 @@
"settings_variable_desc_ro_refresh_player_request": "Last player refresh request datetime",
"sysinfo_page_title": "System infos",
"sysinfo_panel_button_restart": "Restart",
"sysinfo_panel_table_section_system": "System",
"sysinfo_panel_table_section_application": "Application",
"sysinfo_panel_table_section_system": "System & Application",
"sysinfo_panel_table_section_server": "Server",
"sysinfo_panel_title": "Infos",
"sysinfo_panel_th_attribute": "Attribute",
"sysinfo_panel_th_value": "Value",

View File

@ -181,8 +181,8 @@
"settings_variable_desc_ro_refresh_player_request": "Fecha y hora de la última solicitud de actualización del reproductor",
"sysinfo_page_title": "Información del sistema",
"sysinfo_panel_button_restart": "Reiniciar",
"sysinfo_panel_table_section_system": "Sistema",
"sysinfo_panel_table_section_application": "Aplicación",
"sysinfo_panel_table_section_system": "Sistema & Aplicación",
"sysinfo_panel_table_section_server": "Servidor",
"sysinfo_panel_title": "Información",
"sysinfo_panel_th_attribute": "Atributo",
"sysinfo_panel_th_value": "Valor",

View File

@ -182,8 +182,8 @@
"settings_variable_desc_ro_refresh_player_request": "Date de dernière demande de rafraîchissement du lecteur",
"sysinfo_page_title": "Système",
"sysinfo_panel_button_restart": "Redémarrer",
"sysinfo_panel_table_section_system": "Système",
"sysinfo_panel_table_section_application": "Application",
"sysinfo_panel_table_section_system": "Système & Application",
"sysinfo_panel_table_section_server": "Serveur",
"sysinfo_panel_title": "Informations",
"sysinfo_panel_th_attribute": "Attribut",
"sysinfo_panel_th_value": "Valeur",

View File

@ -181,8 +181,8 @@
"settings_variable_desc_ro_refresh_player_request": "Data e ora della richiesta di aggiornamento dell monitor",
"sysinfo_page_title": "Informazione sistema",
"sysinfo_panel_button_restart": "Riavvia",
"sysinfo_panel_table_section_system": "Sistema",
"sysinfo_panel_table_section_application": "Applicazione",
"sysinfo_panel_table_section_system": "Sistema & Applicazione",
"sysinfo_panel_table_section_server": "Server",
"sysinfo_panel_title": "Informazioni",
"sysinfo_panel_th_attribute": "Attributi",
"sysinfo_panel_th_value": "Valore",

View File

@ -30,6 +30,7 @@ class ContentController(ObController):
self._app.add_url_rule('/slideshow/content/show/<content_id>', 'slideshow_content_show', self._auth(self.slideshow_content_show), methods=['GET'])
def slideshow_content_list(self):
self._model_store.variable().update_by_name('last_pillmenu_slideshow', 'slideshow_content_list')
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_content').as_string()
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.CONTENT)

View File

@ -33,6 +33,7 @@ class FleetNodePlayerController(ObController):
self._app.add_url_rule('/fleet/node-player/delete-folder', 'fleet_node_player_folder_delete', self._auth(self.fleet_node_player_folder_delete), methods=['GET'])
def fleet_node_player_list(self):
self._model_store.variable().update_by_name('last_pillmenu_fleet', 'fleet_node_player_list')
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_node_player').as_string()
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.NODE_PLAYER)
return render_template(

View File

@ -29,6 +29,7 @@ class FleetNodePlayerGroupController(ObController):
return redirect(url_for('fleet_node_player_group_list', player_group_id=0))
def fleet_node_player_group_list(self, player_group_id: int = 0):
self._model_store.variable().update_by_name('last_pillmenu_fleet', 'fleet_node_player_group')
current_player_group = self._model_store.node_player_group().get(player_group_id)
node_player_groups = self._model_store.node_player_group().get_all(sort="created_at", ascending=False)
pcounters = self._model_store.node_player_group().get_player_counters_by_player_groups()

View File

@ -29,6 +29,7 @@ class PlaylistController(ObController):
return redirect(url_for('playlist_list', playlist_id=0))
def playlist_list(self, playlist_id: int = 0):
self._model_store.variable().update_by_name('last_pillmenu_slideshow', 'playlist')
current_playlist = self._model_store.playlist().get(playlist_id)
playlists = self._model_store.playlist().get_all(sort="created_at", ascending=False)
durations = self._model_store.playlist().get_durations_by_playlists()

View File

@ -20,12 +20,16 @@ class SettingsController(ObController):
self._app.add_url_rule('/settings/variable-plugin/edit', 'settings_variable_plugin_edit', self._auth(self.settings_variable_plugin_edit), methods=['POST'])
def settings_variable_list(self):
self._model_store.variable().update_by_name('last_pillmenu_configuration', 'settings_variable_list')
return render_template(
'configuration/settings/list.jinja.html',
variables=self._model_store.variable().get_editable_variables(plugin=False, sort='section'),
)
def settings_variable_plugin_list(self):
self._model_store.variable().update_by_name('last_pillmenu_configuration', 'settings_variable_plugin_list')
return render_template(
'configuration/plugins/list.jinja.html',
plugins=self._model_store.plugins(),

View File

@ -26,12 +26,16 @@ class SysinfoController(ObController):
self._app.add_url_rule('/sysinfo/get/ipaddr', 'sysinfo_get_ipaddr', self._auth(self.sysinfo_get_ipaddr), methods=['GET'])
def logs(self):
self._model_store.variable().update_by_name('last_pillmenu_configuration', 'logs')
return render_template(
'configuration/logs/list.jinja.html',
last_logs=self._model_store.logging().get_last_lines_of_stdout(100),
)
def sysinfo(self):
self._model_store.variable().update_by_name('last_pillmenu_configuration', 'sysinfo_attribute_list')
return render_template(
'configuration/sysinfo/list.jinja.html',
sysinfo=get_all_sysinfo(),

View File

@ -16,7 +16,6 @@ class FolderManager(ModelManager):
TABLE_MODEL = [
"name CHAR(255)",
"parent_id INTEGER",
"depth INTEGER DEFAULT 0",
"entity CHAR(255)",
"created_by CHAR(255)",
"updated_by CHAR(255)",
@ -61,8 +60,20 @@ class FolderManager(ModelManager):
def get_one_by_path(self, path: str, entity: FolderEntity) -> Folder:
parts = path[1:].split('/')
return self.get_one_by(
"name = '{}' and depth = {} and entity = '{}'".format(parts[-1], len(parts) - 1, entity.value))
result = self._database_manager.execute_read_query("""WITH RECURSIVE FolderCTE AS (
SELECT id, name, entity, 1 AS depth FROM folder WHERE parent_id IS NULL
UNION ALL
SELECT f.id, f.name, f.entity, cte.depth + 1 AS depth FROM folder f
INNER JOIN FolderCTE cte ON f.parent_id = cte.id
)
SELECT id FROM FolderCTE WHERE name = '{}' AND depth = {} AND entity = '{}'
""".format(parts[-1], len(parts) - 1, entity.value))
if len(result) > 0:
return self.get(result[0]['id'])
return None
def hydrate_parents(self, folder: Optional[Folder], deep=False) -> Optional[Folder]:
if not folder:
@ -140,8 +151,8 @@ class FolderManager(ModelManager):
if entity_is_folder:
return self._db.execute_write_query(
query="UPDATE {} set parent_id = ?, depth = ? WHERE id = ?".format(self.TABLE_NAME),
params=(folder_id if folder else None, folder.depth + 1 if folder else 1, entity_id)
query="UPDATE {} set parent_id = ? WHERE id = ?".format(self.TABLE_NAME),
params=(folder_id if folder else None, entity_id)
)
table = None
@ -182,12 +193,10 @@ class FolderManager(ModelManager):
working_folder = self.get_one_by_path(path=working_folder_path, entity=entity)
folder_path = "{}/{}".format(working_folder_path, name)
parts = folder_path[1:].split('/')
depth = len(parts) - 1
folder = Folder(
entity=entity,
name=name,
depth=depth,
parent_id=working_folder.id if working_folder else None
)
@ -230,7 +239,6 @@ class FolderManager(ModelManager):
child_dict = {
'id': child.id,
'name': child.name,
'depth': child.depth,
'entity': child.entity,
'created_by': child.created_by,
'updated_by': child.updated_by,

View File

@ -133,6 +133,10 @@ class VariableManager:
# Not editable (System information)
{"name": "start_counter", "value": 0, "type": VariableType.INT, "editable": False, "description": self.t('settings_variable_desc_ro_start_counter')},
{"name": "last_pillmenu_slideshow", "value": "slideshow_content_list", "type": VariableType.STRING, "editable": False, "description": None},
{"name": "last_pillmenu_configuration", "value": "settings_variable_list", "type": VariableType.STRING, "editable": False, "description": None},
{"name": "last_pillmenu_fleet", "value": "fleet_node_player_list", "type": VariableType.STRING, "editable": False, "description": None},
{"name": "last_pillmenu_security", "value": "auth_user_list", "type": VariableType.STRING, "editable": False, "description": None},
{"name": "last_folder_content", "value": FOLDER_ROOT_PATH, "type": VariableType.STRING, "editable": False, "description": self.t('settings_variable_desc_ro_last_folder_content')},
{"name": "last_folder_node_player", "value": FOLDER_ROOT_PATH, "type": VariableType.STRING, "editable": False, "description": self.t('settings_variable_desc_ro_last_folder_node_player')},
{"name": "last_restart", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_editable')},

View File

@ -9,12 +9,11 @@ from src.util.utils import str_to_enum
class Folder:
def __init__(self, entity: Union[FolderEntity, str] = FolderEntity.NODE_PLAYER, name: str = 'Untitled', parent_id: Optional[int] = None, id: Optional[int] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None, depth: Optional[int] = None):
def __init__(self, entity: Union[FolderEntity, str] = FolderEntity.NODE_PLAYER, name: str = 'Untitled', parent_id: Optional[int] = None, id: Optional[int] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
self._id = id if id else None
self._parent_id = parent_id
self._entity = str_to_enum(entity, FolderEntity) if isinstance(entity, str) else entity
self._name = name
self._depth = depth
self._created_by = created_by if created_by else None
self._updated_by = updated_by if updated_by else None
self._created_at = int(created_at if created_at else time.time())
@ -87,14 +86,6 @@ class Folder:
def updated_at(self, value: int):
self._updated_at = value
@property
def depth(self) -> int:
return self._depth
@depth.setter
def depth(self, value: int):
self._depth = value
def __str__(self) -> str:
return f"Folder(" \
f"id='{self.id}',\n" \
@ -105,7 +96,6 @@ class Folder:
f"updated_by='{self.updated_by}',\n" \
f"created_at='{self.created_at}',\n" \
f"updated_at='{self.updated_at}',\n" \
f"depth='{self.depth}',\n" \
f")"
def to_json(self) -> str:
@ -121,7 +111,6 @@ class Folder:
"updated_by": self.updated_by,
"created_at": self.created_at,
"updated_at": self.updated_at,
"depth": self.depth,
}
def is_root(self) -> bool:

View File

@ -6,5 +6,5 @@ FOLDER_ROOT_NAME = 'drive'
class FolderEntity(Enum):
NODE_PLAYER = 'node_player'
NODE_PLAYER = 'content'
CONTENT = 'content'

View File

@ -30,6 +30,10 @@ class TemplateRenderer:
FLEET_PLAYER_ENABLED=self._model_store.variable().map().get('fleet_player_enabled').as_bool(),
AUTH_ENABLED=self._model_store.variable().map().get('auth_enabled').as_bool(),
PLAYLIST_ENABLED=self._model_store.variable().map().get('playlist_enabled').as_bool(),
last_pillmenu_slideshow=self._model_store.variable().map().get('last_pillmenu_slideshow').as_string(),
last_pillmenu_configuration=self._model_store.variable().map().get('last_pillmenu_configuration').as_string(),
last_pillmenu_fleet=self._model_store.variable().map().get('last_pillmenu_fleet').as_string(),
last_pillmenu_security=self._model_store.variable().map().get('last_pillmenu_security').as_string(),
track_created=self._model_store.user().track_user_created,
track_updated=self._model_store.user().track_user_updated,
PORT=self._model_store.config().map().get('port'),

View File

@ -58,6 +58,7 @@
"name": l.dynmenu_content,
"icon": "fa-image",
"position": 0,
"main_pill_route": last_pillmenu_slideshow,
"pills": [
{"name": "Bibliothèque", "route": "slideshow_content_list", "icon": "fa-image"},
{"name": "Playlists", "route": "playlist", "route_alt": "playlist_list", "icon": "fa-play"},
@ -67,6 +68,7 @@
"name": "Configuration",
"icon": "fa-cog",
"position": 3,
"main_pill_route": last_pillmenu_configuration,
"pills": [
{"name": l.settings_page_title, "route": "settings_variable_list", "icon": "fa-cogs"},
{"name": l.settings_plugin_page_title, "route": "settings_variable_plugin_list", "icon": "fa-puzzle-piece"},
@ -82,6 +84,7 @@
"name": "Appareils",
"icon": "fa-tv",
"position": 1,
"main_pill_route": last_pillmenu_fleet,
"pills": [
{"name": l.fleet_node_player_page_title, "route": "fleet_node_player_list", "icon": "fa-tv"},
{"name": l.fleet_node_player_group_page_title, "route": "fleet_node_player_group", "route_alt": "fleet_node_player_group_list", "icon": "fa-layer-group"},
@ -96,6 +99,7 @@
"name": "Sécurité",
"icon": "fa-key",
"position": 2,
"main_pill_route": last_pillmenu_security,
"pills": [
{"name": l.auth_page_title, "route": "auth_user_list", "icon": "fa-user"},
]
@ -130,7 +134,7 @@
{{ HOOK(H_ROOT_NAV_ELEMENT_START) }}
{% for category, menu in dynmenu.items() %}
<li class="{{ 'active' if current_dynmenu and menu.name == current_dynmenu.name }}">
<a href="{{ url_for(menu.pills[0].route) }}">
<a href="{{ url_for(menu.main_pill_route) }}">
<i class="fa {{ menu.icon }}"></i> {{ menu.name }}
</a>
</li>
@ -196,10 +200,10 @@
<div class="dropdown">
<div class="trigger">
<div class="avatar">
{# {{ current_user.username[0] }}#}J
{# {{ current_user.username[0] }}#}J
</div>
<div class="username">
{# {{ current_user.username }}#}Jessym
{# {{ current_user.username }}#}Jessym
</div>
<i class="fa fa-sort-down"></i>
</div>

View File

@ -34,7 +34,7 @@
<tbody>
<tr class="title-item">
<td colspan="2">
<i class="fa fa-server icon-left"></i> {{ l.sysinfo_panel_table_section_system }}
<i class="fa fa-box-open icon-left"></i> {{ l.sysinfo_panel_table_section_system }}
</td>
</tr>
@ -47,6 +47,7 @@
{% for ro_variable in ro_variables %}
{% if ro_variable.description %}
<tr>
<td class="description">{{ ro_variable.description }}</td>
<td class="value">
@ -65,12 +66,13 @@
{% endif %}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
<tbody>
<tr class="title-item">
<td colspan="2">
<i class="fa fa-box-open icon-left"></i> {{ l.sysinfo_panel_table_section_application }}
<i class="fa fa-server icon-left"></i> {{ l.sysinfo_panel_table_section_server }}
</td>
</tr>
{% for env_key, env_value in env_variables.items() %}

View File

@ -0,0 +1,85 @@
<div class="horizontal">
<div class="players-holder vertical">
<h3>
{{ l.playlist_panel_content_management }}
</h3>
<p>
{{ l.playlist_panel_content_management_desc }}
</p>
{# {% with slides=slides %}#}
{# {% include 'slideshow/slides/component/table.jinja.html' %}#}
{# {% endwith %}#}
{##}
{# <div class="actions actions-right">#}
{# <button type="button" class="btn btn-info slide-add">#}
{# <i class="fa fa-plus"></i> {{ l.slideshow_slide_form_add_title }}#}
{# </button>#}
{# <a href="{{ url_for('slideshow_content_list') }}" class="btn btn-neutral" target="_blank">#}
{# <i class="fa fa-upload"></i>#}
{# </a>#}
{# </div>#}
</div>
<div class="node-player-group-holder vertical">
{# <h3>#}
{# {{ l.playlist_panel_about_playlist }}#}
{# </h3>#}
<div class="form-holder">
<form class="form" action="{{ url_for('fleet_node_player_group_save') }}" method="POST">
<input type="hidden" name="id" id="node-player-group-edit-id" value="{{ current_player_group.id }}"/>
<div class="form-group">
<label for="node-player-group-edit-name">{{ l.playlist_form_label_name }}</label>
<div class="widget">
<input type="text" name="name" id="node-player-group-edit-name" required="required" value="{{ current_player_group.name }}"/>
</div>
</div>
<div class="form-group">
<label for="node-player-group-edit-playlist-id">{{ l.fleet_node_player_group_form_label_playlist_id }}</label>
<div class="widget">
<select name="playlist_id" id="node-player-group-edit-playlist-id">
{% for playlist_id, playlist_name in playlists.items() %}
<option value="{{ playlist_id }}" {% if current_player_group.playlist_id == playlist_id %}selected="selected"{% endif %}>
{{ playlist_name }}
</option>
{% endfor %}
</select>
<a href="{{ url_for('playlist_list', playlist_id=current_player_group.playlist_id) }}" class="btn btn-neutral">
<i class="fa-solid fa-up-right-from-square"></i>
</a>
</div>
</div>
<div class="actions actions-right">
<button type="submit" class="btn-info">
<i class="fa fa-save icon-left"></i>
{{ l.common_save }}
</button>
</div>
</form>
</div>
<div class="preview-holder">
{% set preview_url = request.scheme ~ '://' ~ request.headers.get('host') ~ url_for('player_use', playlist_slug_or_id=current_player_group.playlist_id) %}
<h4 class="divide">
Iframe
</h4>
<p>
{{ l.playlist_form_preview_iframe_desc }}
</p>
<div class="preview">
<button type="button" class="btn btn-info btn-naked node-player-group-preview"
data-url="{{ preview_url }}?intro=0&animation=0">
<i class="fa fa-eye icon-left"></i> {{ l.playlist_panel_preview_action }}
</button>
</div>
</div>
</div>
</div>

View File

@ -69,9 +69,9 @@
<div class="page-content">
<div class="inner">
{% if current_player_group %}
{# {% with node_player_groups=node_player_groups %}#}
{# {% include 'fleet/player-group/component/edit.jinja.html' %}#}
{# {% endwith %}#}
{% with node_player_groups=node_player_groups %}
{% include 'fleet/player-group/component/edit.jinja.html' %}
{% endwith %}
{% else %}
<div class="inner-empty empty-flag ">
<i class="fa fa-layer-group"></i>

View File

@ -1,37 +0,0 @@
<div class="modal modal-node-player-group-edit hidden">
<h2>
{{ l.fleet_node_player_group_form_edit_title }}
</h2>
<form class="form" action="/fleet/node-player-group/edit" method="POST">
<input type="hidden" name="id" id="node-player-group-edit-id" />
<div class="form-group">
<label for="node-player-group-edit-name">{{ l.fleet_node_player_group_form_label_name }}</label>
<div class="widget">
<input type="text" name="name" id="node-player-group-edit-name" required="required" />
</div>
</div>
<div class="form-group">
<label for="node-player-group-edit-playlist-id">{{ l.fleet_node_player_group_form_label_playlist_id }}</label>
<div class="widget">
<select name="playlist_id" id="node-player-group-edit-playlist-id">
<option value="">{{ l.common_default_playlist }}</option>
{% for playlist_id, playlist_name in playlists.items() %}
<option value="{{ playlist_id }}">{{ playlist_name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="actions">
<button type="button" class="btn-normal modal-close">
{{ l.fleet_node_player_group_form_button_cancel }}
</button>
<button type="submit" class="green">
<i class="fa fa-save icon-left"></i>{{ l.fleet_node_player_group_form_edit_submit }}
</button>
</div>
</form>
</div>

View File

@ -69,13 +69,13 @@
</form>
</div>
<div class="preview-holder">
{% set preview_url = request.scheme ~ '://' ~ request.headers.get('host') ~ url_for('player_use', playlist_slug_or_id=current_playlist.slug) %}
<h4 class="divide">
URL
</h4>
<p>
{{ l.playlist_form_preview_url_desc }}
</p>
{% set preview_url = request.scheme ~ '://' ~ request.headers.get('host') ~ url_for('player_use', playlist_slug_or_id=current_playlist.slug) %}
<div class="form-group">
<div class="widget">
@ -98,8 +98,6 @@
{{ l.playlist_form_preview_qrcode_desc }}
</p>
<div id="qrcode" class="qrcode-pic" data-qrcode-payload="{{ preview_url }}"></div>
</div>
<h4 class="divide">
Iframe
</h4>
@ -115,6 +113,9 @@
</div>
</div>
</div>
</div>