rename manager > composer > studio

This commit is contained in:
jr-k 2024-05-23 10:39:15 +02:00
parent 389b6ad717
commit f5af0e34d9
28 changed files with 205 additions and 205 deletions

View File

@ -6,7 +6,7 @@
**⭐️ You liked it ? Give this repository a star, it's free :)**
## About
Use a RaspberryPi (Lite OS) to show a full-screen slideshow (Kiosk-mode)
Use a RaspberryPi (Lite OS) to show a fullscreen slideshow (Kiosk-mode)
[![Docker Pulls](https://badgen.net/docker/pulls/jierka/obscreen?icon=docker&label=docker%20pulls)](https://hub.docker.com/r/jierka/obscreen/)

View File

@ -1,6 +1,6 @@
jQuery(document).ready(function ($) {
const $tableActive = $('table.active-screens');
const $tableInactive = $('table.inactive-screens');
const $tableActive = $('table.active-studios');
const $tableInactive = $('table.inactive-studios');
const $modalsRoot = $('.modals');
const getId = function ($el) {
@ -9,7 +9,7 @@ jQuery(document).ready(function ($) {
const updateTable = function () {
$('table').each(function () {
if ($(this).find('tbody tr.screen-item:visible').length === 0) {
if ($(this).find('tbody tr.studio-item:visible').length === 0) {
$(this).find('tr.empty-tr').removeClass('hidden');
} else {
$(this).find('tr.empty-tr').addClass('hidden');
@ -30,13 +30,13 @@ jQuery(document).ready(function ($) {
const updatePositions = function (table, row) {
const positions = {};
$('.screen-item').each(function (index) {
$('.studio-item').each(function (index) {
positions[getId($(this))] = index;
});
$.ajax({
method: 'POST',
url: '/fleet/screen/position',
url: '/fleet/studio/position',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify(positions),
});
@ -44,14 +44,14 @@ jQuery(document).ready(function ($) {
const main = function () {
$("table").tableDnD({
dragHandle: 'td a.screen-sort',
dragHandle: 'td a.studio-sort',
onDrop: updatePositions
});
};
$(document).on('change', 'input[type=checkbox]', function () {
$.ajax({
url: '/fleet/screen/toggle',
url: '/fleet/studio/toggle',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify({id: getId($(this)), enabled: $(this).is(':checked')}),
method: 'POST',
@ -68,16 +68,16 @@ jQuery(document).ready(function ($) {
updateTable();
});
$(document).on('change', '#screen-add-type', function () {
$(document).on('change', '#studio-add-type', function () {
const value = $(this).val();
const inputType = $(this).find('option').filter(function (i, el) {
return $(el).val() === value;
}).data('input');
$('.screen-add-object-input')
$('.studio-add-object-input')
.addClass('hidden')
.prop('disabled', true)
.filter('#screen-add-object-input-' + inputType)
.filter('#studio-add-object-input-' + inputType)
.removeClass('hidden')
.prop('disabled', false)
;
@ -87,29 +87,29 @@ jQuery(document).ready(function ($) {
hideModal();
});
$(document).on('click', '.screen-add', function () {
showModal('modal-screen-add');
$('.modal-screen-add input:eq(0)').focus().select();
$(document).on('click', '.studio-add', function () {
showModal('modal-studio-add');
$('.modal-studio-add input:eq(0)').focus().select();
});
$(document).on('click', '.screen-edit', function () {
const screen = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
showModal('modal-screen-edit');
$('.modal-screen-edit input:visible:eq(0)').focus().select();
$('#screen-edit-name').val(screen.name);
$('#screen-edit-host').val(screen.host);
$('#screen-edit-port').val(screen.port);
$('#screen-edit-id').val(screen.id);
$(document).on('click', '.studio-edit', function () {
const studio = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
showModal('modal-studio-edit');
$('.modal-studio-edit input:visible:eq(0)').focus().select();
$('#studio-edit-name').val(studio.name);
$('#studio-edit-host').val(studio.host);
$('#studio-edit-port').val(studio.port);
$('#studio-edit-id').val(studio.id);
});
$(document).on('click', '.screen-delete', function () {
if (confirm(l.js_fleet_screen_delete_confirmation)) {
$(document).on('click', '.studio-delete', function () {
if (confirm(l.js_fleet_studio_delete_confirmation)) {
const $tr = $(this).parents('tr:eq(0)');
$tr.remove();
updateTable();
$.ajax({
method: 'DELETE',
url: '/fleet/screen/delete',
url: '/fleet/studio/delete',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify({id: getId($(this))}),
});

View File

@ -73,16 +73,16 @@ python ./obscreen.py
#### Start server forever with systemctl
```bash
cat "$(pwd)/system/obscreen-composer.service" | sed "s#/home/pi#$HOME#g" | sed "s#=pi#=$USER#g" | sudo tee /etc/systemd/system/obscreen-composer.service
cat "$(pwd)/system/obscreen-studio.service" | sed "s#/home/pi#$HOME#g" | sed "s#=pi#=$USER#g" | sudo tee /etc/systemd/system/obscreen-studio.service
sudo systemctl daemon-reload
sudo systemctl enable obscreen-composer.service
sudo systemctl start obscreen-composer.service
sudo systemctl enable obscreen-studio.service
sudo systemctl start obscreen-studio.service
```
#### Troubleshoot
```bash
# Watch logs with following command
sudo journalctl -u obscreen-composer -f
sudo journalctl -u obscreen-studio -f
```
---
## 👌 Usage

View File

@ -87,16 +87,16 @@ python ./obscreen.py
#### Start server forever with systemctl
```bash
cat "$(pwd)/system/obscreen-composer.service" | sed "s#/home/pi#$HOME#g" | sed "s#=pi#=$USER#g" | sudo tee /etc/systemd/system/obscreen-composer.service
cat "$(pwd)/system/obscreen-studio.service" | sed "s#/home/pi#$HOME#g" | sed "s#=pi#=$USER#g" | sudo tee /etc/systemd/system/obscreen-studio.service
sudo systemctl daemon-reload
sudo systemctl enable obscreen-composer.service
sudo systemctl start obscreen-composer.service
sudo systemctl enable obscreen-studio.service
sudo systemctl start obscreen-studio.service
```
#### Troubleshoot
```bash
# Watch logs with following command
sudo journalctl -u obscreen-composer -f
sudo journalctl -u obscreen-studio -f
```
---
## 🏁 Finally

View File

@ -57,26 +57,26 @@
"js_playlist_delete_confirmation": "Are you sure?",
"playlist_delete_has_slides": "Playlist has slides, please remove them before and retry",
"fleet_page_title": "Composers",
"fleet_screen_button_add": "Add a screen",
"fleet_screen_button_fleetview": "Fleet view",
"fleet_screen_panel_active": "Active screens",
"fleet_screen_panel_inactive": "Inactive screens",
"fleet_screen_panel_empty": "Currently, there are no screens. %link% now.",
"fleet_screen_panel_th_name": "Name",
"fleet_screen_panel_th_host": "Host",
"fleet_screen_panel_th_port": "Port",
"fleet_screen_panel_th_enabled": "Enabled",
"fleet_screen_panel_th_activity": "Activity",
"fleet_screen_form_add_title": "Add Screen",
"fleet_screen_form_add_submit": "Add",
"fleet_screen_form_edit_title": "Edit Screen",
"fleet_screen_form_edit_submit": "Save",
"fleet_screen_form_label_name": "Name",
"fleet_screen_form_label_host": "Host",
"fleet_screen_form_label_port": "Port",
"fleet_screen_form_button_cancel": "Cancel",
"js_fleet_screen_delete_confirmation": "Are you sure?",
"fleet_page_title": "Studios",
"fleet_studio_button_add": "Add a studio",
"fleet_studio_button_fleetview": "Fleet view",
"fleet_studio_panel_active": "Active studios",
"fleet_studio_panel_inactive": "Inactive studios",
"fleet_studio_panel_empty": "Currently, there are no studios. %link% now.",
"fleet_studio_panel_th_name": "Name",
"fleet_studio_panel_th_host": "Host",
"fleet_studio_panel_th_port": "Port",
"fleet_studio_panel_th_enabled": "Enabled",
"fleet_studio_panel_th_activity": "Activity",
"fleet_studio_form_add_title": "Add Studio",
"fleet_studio_form_add_submit": "Add",
"fleet_studio_form_edit_title": "Edit Studio",
"fleet_studio_form_edit_submit": "Save",
"fleet_studio_form_label_name": "Name",
"fleet_studio_form_label_host": "Host",
"fleet_studio_form_label_port": "Port",
"fleet_studio_form_button_cancel": "Cancel",
"js_fleet_studio_delete_confirmation": "Are you sure?",
"login_page_title": "Login",
"auth_page_title": "Users",
@ -110,10 +110,10 @@
"settings_variable_form_button_cancel": "Cancel",
"settings_variable_desc_lang": "Server language",
"settings_variable_desc_playlist_enabled": "Enable playlist management",
"settings_variable_desc_fleet_composer_enabled": "Enable fleet composer management",
"settings_variable_desc_fleet_studio_enabled": "Enable fleet studio management",
"settings_variable_desc_auth_enabled": "Enable auth management",
"settings_variable_desc_edition_auth_enabled": "Default user credentials will be admin/admin",
"settings_variable_desc_external_url": "External url (i.e: https://screen-01.company.com or http://10.10.3.100)",
"settings_variable_desc_external_url": "External url (i.e: https://studio-01.company.com or http://10.10.3.100)",
"settings_variable_desc_slide_upload_limit": "Slide upload limit (in megabytes)",
"settings_variable_desc_default_slide_duration": "Intro slide duration (in seconds)",
"settings_variable_desc_polling_interval": "Refresh interval applied for settings to the player (in seconds)",

View File

@ -57,26 +57,26 @@
"js_playlist_delete_confirmation": "Êtes-vous sûr ?",
"playlist_delete_has_slides": "La liste de lecture contient des sldies, supprimez-les avant et réessayez",
"fleet_page_title": "Composeurs",
"fleet_screen_button_add": "Ajouter un écran",
"fleet_screen_button_fleetview": "Vue flotte",
"fleet_screen_panel_active": "Écrans actifs",
"fleet_screen_panel_inactive": "Écrans inactifs",
"fleet_screen_panel_empty": "Actuellement, il n'y a pas d'écrans. %link% maintenant.",
"fleet_screen_panel_th_name": "Nom",
"fleet_screen_panel_th_host": "Hôte",
"fleet_screen_panel_th_port": "Port",
"fleet_screen_panel_th_enabled": "Activé",
"fleet_screen_panel_th_activity": "Options",
"fleet_screen_form_add_title": "Ajout d'un écran",
"fleet_screen_form_add_submit": "Ajouter",
"fleet_screen_form_edit_title": "Modification d'un écran",
"fleet_screen_form_edit_submit": "Enregistrer",
"fleet_screen_form_label_name": "Nom",
"fleet_screen_form_label_host": "Hôte",
"fleet_screen_form_label_port": "Port",
"fleet_screen_form_button_cancel": "Annuler",
"js_fleet_screen_delete_confirmation": "Êtes-vous sûr ?",
"fleet_page_title": "Studios",
"fleet_studio_button_add": "Ajouter un studio",
"fleet_studio_button_fleetview": "Vue flotte",
"fleet_studio_panel_active": "Studios actifs",
"fleet_studio_panel_inactive": "Studios inactifs",
"fleet_studio_panel_empty": "Actuellement, il n'y a pas de studios. %link% maintenant.",
"fleet_studio_panel_th_name": "Nom",
"fleet_studio_panel_th_host": "Hôte",
"fleet_studio_panel_th_port": "Port",
"fleet_studio_panel_th_enabled": "Activé",
"fleet_studio_panel_th_activity": "Options",
"fleet_studio_form_add_title": "Ajout d'un studio",
"fleet_studio_form_add_submit": "Ajouter",
"fleet_studio_form_edit_title": "Modification d'un studio",
"fleet_studio_form_edit_submit": "Enregistrer",
"fleet_studio_form_label_name": "Nom",
"fleet_studio_form_label_host": "Hôte",
"fleet_studio_form_label_port": "Port",
"fleet_studio_form_button_cancel": "Annuler",
"js_fleet_studio_delete_confirmation": "Êtes-vous sûr ?",
"login_page_title": "Connexion",
"auth_page_title": "Utilisateurs",
@ -110,10 +110,10 @@
"settings_variable_form_button_cancel": "Annuler",
"settings_variable_desc_lang": "Langage de l'application",
"settings_variable_desc_playlist_enabled": "Activer la gestion des playlists",
"settings_variable_desc_fleet_composer_enabled": "Activer la gestion de flotte des composeurs",
"settings_variable_desc_fleet_studio_enabled": "Activer la gestion de flotte des studios",
"settings_variable_desc_auth_enabled": "Activer la gestion de l'authentification",
"settings_variable_desc_edition_auth_enabled": "Les identifiants de l'utilisateur par défaut seront admin/admin",
"settings_variable_desc_external_url": "URL externe (i.e: https://screen-01.company.com or http://10.10.3.100)",
"settings_variable_desc_external_url": "URL externe (i.e: https://studio-01.company.com or http://10.10.3.100)",
"settings_variable_desc_slide_upload_limit": "Limite d'upload du fichier d'une slide (en mégaoctets)",
"settings_variable_desc_default_slide_duration": "Durée de la slide d'introduction (en secondes)",
"settings_variable_desc_polling_interval": "Intervalle de rafraîchissement des paramètres à appliquer au lecteur (en secondes)",

View File

@ -7,13 +7,13 @@ from src.model.enum.HookType import HookType
from src.model.hook.HookRegistration import HookRegistration
class FleetmodeScreenRestart(ObPlugin):
class FleetmodeStudioRestart(ObPlugin):
def use_id(self):
return 'fleetmode_screen_restart'
return 'fleetmode_studio_restart'
def use_title(self):
return 'Fleetmode Screen Restart'
return 'Fleetmode Studio Restart'
def use_variables(self) -> List[Variable]:
return [

View File

@ -1,2 +1,2 @@
<button class="normal sysinfo-restart"><i class="fa fa-refresh icon-left"></i>{{ l.fleetmode_screen_restart_button_restart }}</button>
<button class="normal sysinfo-restart"><i class="fa fa-refresh icon-left"></i>{{ l.fleetmode_studio_restart_button_restart }}</button>

View File

@ -2,7 +2,7 @@ import json
from flask import Flask, render_template, redirect, request, url_for, jsonify
from src.service.ModelStore import ModelStore
from src.model.entity.Screen import Screen
from src.model.entity.Studio import Studio
from src.interface.ObController import ObController
@ -10,7 +10,7 @@ class FleetController(ObController):
def guard_fleet(self, f):
def decorated_function(*args, **kwargs):
if not self._model_store.variable().map().get('fleet_composer_enabled').as_bool():
if not self._model_store.variable().map().get('fleet_studio_enabled').as_bool():
return redirect(url_for('manage'))
return f(*args, **kwargs)
@ -18,49 +18,49 @@ class FleetController(ObController):
def register(self):
self._app.add_url_rule('/fleet', 'fleet', self.guard_fleet(self._auth(self.fleet)), methods=['GET'])
self._app.add_url_rule('/fleet/screen/list', 'fleet_screen_list', self.guard_fleet(self._auth(self.fleet_screen_list)), methods=['GET'])
self._app.add_url_rule('/fleet/screen/add', 'fleet_screen_add', self.guard_fleet(self._auth(self.fleet_screen_add)), methods=['POST'])
self._app.add_url_rule('/fleet/screen/edit', 'fleet_screen_edit', self.guard_fleet(self._auth(self.fleet_screen_edit)), methods=['POST'])
self._app.add_url_rule('/fleet/screen/toggle', 'fleet_screen_toggle', self.guard_fleet(self._auth(self.fleet_screen_toggle)), methods=['POST'])
self._app.add_url_rule('/fleet/screen/delete', 'fleet_screen_delete', self.guard_fleet(self._auth(self.fleet_screen_delete)), methods=['DELETE'])
self._app.add_url_rule('/fleet/screen/position', 'fleet_screen_position', self.guard_fleet(self._auth(self.fleet_screen_position)), methods=['POST'])
self._app.add_url_rule('/fleet/studio/list', 'fleet_studio_list', self.guard_fleet(self._auth(self.fleet_studio_list)), methods=['GET'])
self._app.add_url_rule('/fleet/studio/add', 'fleet_studio_add', self.guard_fleet(self._auth(self.fleet_studio_add)), methods=['POST'])
self._app.add_url_rule('/fleet/studio/edit', 'fleet_studio_edit', self.guard_fleet(self._auth(self.fleet_studio_edit)), methods=['POST'])
self._app.add_url_rule('/fleet/studio/toggle', 'fleet_studio_toggle', self.guard_fleet(self._auth(self.fleet_studio_toggle)), methods=['POST'])
self._app.add_url_rule('/fleet/studio/delete', 'fleet_studio_delete', self.guard_fleet(self._auth(self.fleet_studio_delete)), methods=['DELETE'])
self._app.add_url_rule('/fleet/studio/position', 'fleet_studio_position', self.guard_fleet(self._auth(self.fleet_studio_position)), methods=['POST'])
def fleet(self):
return render_template(
'fleet/fleet.jinja.html',
screens=self._model_store.screen().get_enabled_screens(),
studios=self._model_store.studio().get_enabled_studios(),
)
def fleet_screen_list(self):
def fleet_studio_list(self):
return render_template(
'fleet/list.jinja.html',
enabled_screens=self._model_store.screen().get_enabled_screens(),
disabled_screens=self._model_store.screen().get_disabled_screens(),
enabled_studios=self._model_store.studio().get_enabled_studios(),
disabled_studios=self._model_store.studio().get_disabled_studios(),
)
def fleet_screen_add(self):
self._model_store.screen().add_form(Screen(
def fleet_studio_add(self):
self._model_store.studio().add_form(Studio(
name=request.form['name'],
host=request.form['host'],
port=request.form['port'],
))
return redirect(url_for('fleet_screen_list'))
return redirect(url_for('fleet_studio_list'))
def fleet_screen_edit(self):
self._model_store.screen().update_form(request.form['id'], request.form['name'], request.form['host'], request.form['port'])
return redirect(url_for('fleet_screen_list'))
def fleet_studio_edit(self):
self._model_store.studio().update_form(request.form['id'], request.form['name'], request.form['host'], request.form['port'])
return redirect(url_for('fleet_studio_list'))
def fleet_screen_toggle(self):
def fleet_studio_toggle(self):
data = request.get_json()
self._model_store.screen().update_enabled(data.get('id'), data.get('enabled'))
self._model_store.studio().update_enabled(data.get('id'), data.get('enabled'))
return jsonify({'status': 'ok'})
def fleet_screen_delete(self):
def fleet_studio_delete(self):
data = request.get_json()
self._model_store.screen().delete(data.get('id'))
self._model_store.studio().delete(data.get('id'))
return jsonify({'status': 'ok'})
def fleet_screen_position(self):
def fleet_studio_position(self):
data = request.get_json()
self._model_store.screen().update_positions(data)
self._model_store.studio().update_positions(data)
return jsonify({'status': 'ok'})

View File

@ -33,7 +33,7 @@ class SettingsController(ObController):
if variable.name == 'slide_upload_limit':
self.reload_web_server()
if variable.name == 'fleet_composer_enabled':
if variable.name == 'fleet_studio_enabled':
self.reload_web_server()
if variable.name == 'auth_enabled':

View File

@ -63,7 +63,7 @@ class SysinfoController(ObController):
os.execl(python, python, *sys.argv)
else:
try:
subprocess.run(["sudo", "systemctl", "restart", 'obscreen-composer'], check=True, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(["sudo", "systemctl", "restart", 'obscreen-studio'], check=True, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pass
except subprocess.TimeoutExpired:
pass

View File

@ -1,15 +1,15 @@
from typing import Dict, Optional, List, Tuple, Union
from src.model.entity.Screen import Screen
from src.model.entity.Studio import Studio
from src.manager.DatabaseManager import DatabaseManager
from src.manager.LangManager import LangManager
from src.manager.UserManager import UserManager
from src.service.ModelManager import ModelManager
class ScreenManager(ModelManager):
class StudioManager(ModelManager):
TABLE_NAME = "fleet"
TABLE_NAME = "fleet_studio"
TABLE_MODEL = [
"name CHAR(255)",
"enabled INTEGER DEFAULT 0",
@ -22,23 +22,23 @@ class ScreenManager(ModelManager):
super().__init__(lang_manager, database_manager, user_manager)
self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL)
def hydrate_object(self, raw_screen: dict, id: Optional[int] = None) -> Screen:
def hydrate_object(self, raw_studio: dict, id: Optional[int] = None) -> Studio:
if id:
raw_screen['id'] = id
raw_studio['id'] = id
return Screen(**raw_screen)
return Studio(**raw_studio)
def hydrate_list(self, raw_screens: list) -> List[Screen]:
return [self.hydrate_object(raw_screen) for raw_screen in raw_screens]
def hydrate_list(self, raw_studios: list) -> List[Studio]:
return [self.hydrate_object(raw_studio) for raw_studio in raw_studios]
def get(self, id: int) -> Optional[Screen]:
def get(self, id: int) -> Optional[Studio]:
object = self._db.get_by_id(self.TABLE_NAME, id)
return self.hydrate_object(object, id) if object else None
def get_by(self, query, sort: Optional[str] = None) -> List[Screen]:
def get_by(self, query, sort: Optional[str] = None) -> List[Studio]:
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
def get_one_by(self, query) -> Optional[Screen]:
def get_one_by(self, query) -> Optional[Studio]:
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
if not object:
@ -46,30 +46,30 @@ class ScreenManager(ModelManager):
return self.hydrate_object(object)
def get_all(self, sort: bool = False) -> List[Screen]:
def get_all(self, sort: bool = False) -> List[Studio]:
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, "position" if sort else None))
def get_enabled_screens(self) -> List[Screen]:
def get_enabled_studios(self) -> List[Studio]:
return self.get_by(query="enabled = 1", sort="position")
def get_disabled_screens(self) -> List[Screen]:
def get_disabled_studios(self) -> List[Studio]:
return self.get_by(query="enabled = 0", sort="position")
def update_enabled(self, id: int, enabled: bool) -> None:
self._db.update_by_id(self.TABLE_NAME, id, {"enabled": enabled, "position": 999})
def update_positions(self, positions: list) -> None:
for screen_id, screen_position in positions.items():
self._db.update_by_id(self.TABLE_NAME, screen_id, {"position": screen_position})
for studio_id, studio_position in positions.items():
self._db.update_by_id(self.TABLE_NAME, studio_id, {"position": studio_position})
def update_form(self, id: int, name: str, host: str, port: int) -> None:
self._db.update_by_id(self.TABLE_NAME, id, {"name": name, "host": host, "port": port})
def add_form(self, screen: Union[Screen, Dict]) -> None:
form = screen
def add_form(self, studio: Union[Studio, Dict]) -> None:
form = studio
if not isinstance(screen, dict):
form = screen.to_dict()
if not isinstance(studio, dict):
form = studio.to_dict()
del form['id']
self._db.add(self.TABLE_NAME, form)
@ -77,5 +77,5 @@ class ScreenManager(ModelManager):
def delete(self, id: int) -> None:
self._db.delete_by_id(self.TABLE_NAME, id)
def to_dict(self, screens: List[Screen]) -> List[Dict]:
return [screen.to_dict() for screen in screens]
def to_dict(self, studios: List[Studio]) -> List[Dict]:
return [studio.to_dict() for studio in studios]

View File

@ -104,7 +104,7 @@ class VariableManager(ModelManager):
### General
{"name": "lang", "section": self.t(VariableSection.GENERAL), "value": "en", "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_lang'), "selectables": self.t(ApplicationLanguage), "refresh_player": False},
{"name": "auth_enabled", "section": self.t(VariableSection.GENERAL), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_auth_enabled'), "description_edition": self.t('settings_variable_desc_edition_auth_enabled'), "refresh_player": False},
{"name": "fleet_composer_enabled", "section": self.t(VariableSection.GENERAL), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_fleet_composer_enabled'), "refresh_player": False},
{"name": "fleet_studio_enabled", "section": self.t(VariableSection.GENERAL), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_fleet_studio_enabled'), "refresh_player": False},
{"name": "playlist_enabled", "section": self.t(VariableSection.GENERAL), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_playlist_enabled'), "refresh_player": False},
{"name": "external_url", "section": self.t(VariableSection.GENERAL), "value": "", "type": VariableType.STRING, "editable": True, "description": self.t('settings_variable_desc_external_url'), "refresh_player": False},
{"name": "slide_upload_limit", "section": self.t(VariableSection.GENERAL), "value": 32, "unit": VariableUnit.MEGABYTE, "type": VariableType.INT, "editable": True, "description": self.t('settings_variable_desc_slide_upload_limit'), "refresh_player": False},

View File

@ -3,7 +3,7 @@ import json
from typing import Optional, Union
class Screen:
class Studio:
def __init__(self, host: str = '', port: int = 5000, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[int] = None):
self._id = id if id else None
@ -58,7 +58,7 @@ class Screen:
self._position = value
def __str__(self) -> str:
return f"Screen(" \
return f"Studio(" \
f"id='{self.id}',\n" \
f"name='{self.name}',\n" \
f"enabled='{self.enabled}',\n" \

View File

@ -1,6 +1,6 @@
from src.manager.PlaylistManager import PlaylistManager
from src.manager.SlideManager import SlideManager
from src.manager.ScreenManager import ScreenManager
from src.manager.StudioManager import StudioManager
from src.manager.UserManager import UserManager
from src.manager.VariableManager import VariableManager
from src.manager.LangManager import LangManager
@ -26,7 +26,7 @@ class ModelStore:
self._logging_manager = LoggingManager(config_manager=self._config_manager)
# Model
self._screen_manager = ScreenManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager)
self._studio_manager = StudioManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager)
self._playlist_manager = PlaylistManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager)
self._slide_manager = SlideManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager)
self._variable_manager.reload()
@ -49,8 +49,8 @@ class ModelStore:
def playlist(self) -> PlaylistManager:
return self._playlist_manager
def screen(self) -> ScreenManager:
return self._screen_manager
def studio(self) -> StudioManager:
return self._studio_manager
def lang(self) -> LangManager:
return self._lang_manager

View File

@ -26,7 +26,7 @@ class TemplateRenderer:
globals = dict(
STATIC_PREFIX="/{}/{}/".format(WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_ASSETS),
SECRET_KEY=self._model_store.config().map().get('secret_key'),
FLEET_COMPOSER_ENABLED=self._model_store.variable().map().get('fleet_composer_enabled').as_bool(),
FLEET_STUDIO_ENABLED=self._model_store.variable().map().get('fleet_studio_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(),
track_created=self._model_store.user().track_user_created,

View File

@ -1,5 +1,5 @@
[Unit]
Description=Obscreen Composer
Description=Obscreen Studio
After=network.target
[Service]

View File

@ -63,10 +63,10 @@
</a>
</li>
{% endif %}
{% if FLEET_COMPOSER_ENABLED %}
<li class="{{ 'active' if request.url_rule.endpoint == 'fleet_screen_list' }}">
<a href="{{ url_for('fleet_screen_list') }}">
<i class="fa fa-tv"></i> {{ l.fleet_page_title }}
{% if FLEET_STUDIO_ENABLED %}
<li class="{{ 'active' if request.url_rule.endpoint == 'fleet_studio_list' }}">
<a href="{{ url_for('fleet_studio_list') }}">
<i class="fa fa-server"></i> {{ l.fleet_page_title }}
</a>
</li>
{% endif %}
@ -124,7 +124,7 @@
var l = {
'js_playlist_delete_confirmation': '{{ l.js_playlist_delete_confirmation }}',
'js_slideshow_slide_delete_confirmation': '{{ l.js_slideshow_slide_delete_confirmation }}',
'js_fleet_screen_delete_confirmation': '{{ l.js_fleet_screen_delete_confirmation }}',
'js_fleet_studio_delete_confirmation': '{{ l.js_fleet_studio_delete_confirmation }}',
'js_auth_user_delete_confirmation': '{{ l.js_auth_user_delete_confirmation }}',
'js_sysinfo_restart_confirmation': '{{ l.js_sysinfo_restart_confirmation }}',
'js_sysinfo_restart_loading': '{{ l.js_sysinfo_restart_loading }}'

View File

@ -1,55 +1,55 @@
<table class="{{ tclass }}-screens">
<table class="{{ tclass }}-studios">
<thead>
<tr>
<th>{{ l.fleet_screen_panel_th_name }}</th>
<th class="tac">{{ l.fleet_screen_panel_th_host }}</th>
<th class="tac">{{ l.fleet_screen_panel_th_port }}</th>
<th class="tac">{{ l.fleet_screen_panel_th_enabled }}</th>
<th class="tac">{{ l.fleet_screen_panel_th_activity }}</th>
<th>{{ l.fleet_studio_panel_th_name }}</th>
<th class="tac">{{ l.fleet_studio_panel_th_host }}</th>
<th class="tac">{{ l.fleet_studio_panel_th_port }}</th>
<th class="tac">{{ l.fleet_studio_panel_th_enabled }}</th>
<th class="tac">{{ l.fleet_studio_panel_th_activity }}</th>
</tr>
</thead>
<tbody>
<tr class="empty-tr {% if screens|length != 0 %}hidden{% endif %}">
<tr class="empty-tr {% if studios|length != 0 %}hidden{% endif %}">
<td colspan="4">
{{ l.fleet_screen_panel_empty|replace(
{{ l.fleet_studio_panel_empty|replace(
'%link%',
('<a href="javascript:void(0);" class="item-add screen-add">'~l.fleet_screen_button_add~'</a>')|safe
('<a href="javascript:void(0);" class="item-add studio-add">'~l.fleet_studio_button_add~'</a>')|safe
) }}
</td>
</tr>
{% for screen in screens %}
<tr class="screen-item" data-level="{{ screen.id }}" data-entity="{{ screen.to_json() }}">
{% for studio in studios %}
<tr class="studio-item" data-level="{{ studio.id }}" data-entity="{{ studio.to_json() }}">
<td class="infos">
<div class="inner">
<a href="javascript:void(0);" class="item-sort screen-sort">
<a href="javascript:void(0);" class="item-sort studio-sort">
<i class="fa fa-sort icon-left"></i>
</a>
<div class="badge"><i class="fa fa-key icon-left"></i> {{ screen.id }}</div>
<div class="badge"><i class="fa fa-key icon-left"></i> {{ studio.id }}</div>
<i class="fa fa-tv icon-left"></i>
{{ screen.name }}
<i class="fa fa-server icon-left"></i>
{{ studio.name }}
</div>
</td>
<td class="tac">
{{ screen.host }}
{{ studio.host }}
</td>
<td class="tac">
{{ screen.port }}
{{ studio.port }}
</td>
<td class="tac">
<label class="pure-material-switch">
<input type="checkbox" {% if screen.enabled %}checked="checked"{% endif %}><span></span>
<input type="checkbox" {% if studio.enabled %}checked="checked"{% endif %}><span></span>
</label>
</td>
<td class="actions tac">
<a href="javascript:void(0);" class="item-edit screen-edit">
<a href="javascript:void(0);" class="item-edit studio-edit">
<i class="fa fa-pencil"></i>
</a>
<a href="http://{{ screen.host }}:{{ screen.port }}" class="item-download screen-download" target="_blank">
<a href="http://{{ studio.host }}:{{ studio.port }}" class="item-download studio-download" target="_blank">
<i class="fa fa-eye"></i>
</a>
<a href="javascript:void(0);" class="item-delete screen-delete">
<a href="javascript:void(0);" class="item-delete studio-delete">
<i class="fa fa-trash"></i>
</a>
</td>

View File

@ -9,39 +9,39 @@
ul li a {flex:1;display: flex;flex-direction: row;justify-content: center;align-items: center;padding: 20px 5px;color:#0eef5f;font-weight: bold;text-decoration: none;}
ul li.active a { background: #0eef5f;color:white; }
main {display: flex;flex:1;}
main .screen-frame {display: flex; flex:1;}
main .screen-frame iframe {display: flex; flex: 1;}
main .studio-frame {display: flex; flex:1;}
main .studio-frame iframe {display: flex; flex: 1;}
.hidden {display: none !important;}
</style>
</head>
<body>
<ul>
{% for screen in screens %}
{% for studio in studios %}
<li class="{% if loop.first %}active{% endif %}">
<a href="javascript:void(0);" class="screen-switch" data-id="{{ screen.id }}" onclick="tab('{{ screen.id }}')">
{{ screen.name }}
<a href="javascript:void(0);" class="studio-switch" data-id="{{ studio.id }}" onclick="tab('{{ studio.id }}')">
{{ studio.name }}
</a>
</li>
{% endfor %}
</ul>
<main>
{% for screen in screens %}
<div class="screen-frame {% if not loop.first %}hidden{% endif %}" data-id="{{ screen.id }}">
<iframe src="http://{{ screen.host }}:{{ screen.port }}/slideshow?fleet_mode=1" frameborder="0" allowtransparency=""></iframe>
{% for studio in studios %}
<div class="studio-frame {% if not loop.first %}hidden{% endif %}" data-id="{{ studio.id }}">
<iframe src="http://{{ studio.host }}:{{ studio.port }}/slideshow?fleet_mode=1" frameborder="0" allowtransparency=""></iframe>
</div>
{% endfor %}
</main>
<script type="text/javascript">
const tab = function(id) {
const $switches = document.getElementsByClassName('screen-switch');
const $frames = document.getElementsByClassName('screen-frame');
const $switches = document.getElementsByClassName('studio-switch');
const $frames = document.getElementsByClassName('studio-frame');
for (let i = 0; i < $switches.length; i++) {
const $switch = $switches[i];
$switch.parentElement.className = $switch.dataset.id === id ? 'active' : '';
}
for (let j = 0; j < $frames.length; j++) {
const $frame = $frames[j];
$frame.className = $frame.dataset.id === id ? 'screen-frame' : 'screen-frame hidden';
$frame.className = $frame.dataset.id === id ? 'studio-frame' : 'studio-frame hidden';
}
document.location.hash = id;
};

View File

@ -10,7 +10,7 @@
{% block add_js %}
<script src="{{ STATIC_PREFIX }}js/lib/tablednd-fixed.js"></script>
<script src="{{ STATIC_PREFIX }}js/fleet/screens.js"></script>
<script src="{{ STATIC_PREFIX }}js/fleet/studios.js"></script>
{{ HOOK(H_FLEET_JAVASCRIPT) }}
{% endblock %}
@ -20,25 +20,25 @@
<div class="toolbar-actions">
{{ HOOK(H_FLEET_TOOLBAR_ACTIONS_START) }}
<a class="btn normal" href="{{ url_for('fleet') }}" target="_blank"><i class="fa fa-table icon-left"></i>{{ l.fleet_screen_button_fleetview }}</a>
<button class="purple screen-add item-add"><i class="fa fa-plus icon-left"></i>{{ l.fleet_screen_button_add }}</button>
<a class="btn normal" href="{{ url_for('fleet') }}" target="_blank"><i class="fa fa-table icon-left"></i>{{ l.fleet_studio_button_fleetview }}</a>
<button class="purple studio-add item-add"><i class="fa fa-plus icon-left"></i>{{ l.fleet_studio_button_add }}</button>
{{ HOOK(H_FLEET_TOOLBAR_ACTIONS_END) }}
</div>
</div>
<div class="panel">
<div class="panel-body">
<h3>{{ l.fleet_screen_panel_active }}</h3>
<h3>{{ l.fleet_studio_panel_active }}</h3>
{% with tclass='active', screens=enabled_screens %}
{% with tclass='active', studios=enabled_studios %}
{% include 'fleet/component/table.jinja.html' %}
{% endwith %}
</div>
</div>
<div class="panel panel-inactive">
<div class="panel-body">
<h3>{{ l.fleet_screen_panel_inactive }}</h3>
<h3>{{ l.fleet_studio_panel_inactive }}</h3>
{% with tclass='inactive', screens=disabled_screens %}
{% with tclass='inactive', studios=disabled_studios %}
{% include 'fleet/component/table.jinja.html' %}
{% endwith %}
</div>

View File

@ -1,36 +1,36 @@
<div class="modal modal-screen-add">
<div class="modal modal-studio-add">
<h2>
{{ l.fleet_screen_form_add_title }}
{{ l.fleet_studio_form_add_title }}
</h2>
<form action="/fleet/screen/add" method="POST" enctype="multipart/form-data">
<form action="/fleet/studio/add" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="screen-add-name">{{ l.fleet_screen_form_label_name }}</label>
<label for="studio-add-name">{{ l.fleet_studio_form_label_name }}</label>
<div class="widget">
<input name="name" type="text" id="screen-add-name" required="required" />
<input name="name" type="text" id="studio-add-name" required="required" />
</div>
</div>
<div class="form-group">
<label for="screen-add-host">{{ l.fleet_screen_form_label_host }}</label>
<label for="studio-add-host">{{ l.fleet_studio_form_label_host }}</label>
<div class="widget">
<input type="text" name="host" id="screen-add-host" required="required" />
<input type="text" name="host" id="studio-add-host" required="required" />
</div>
</div>
<div class="form-group">
<label for="screen-add-port">{{ l.fleet_screen_form_label_port }}</label>
<label for="studio-add-port">{{ l.fleet_studio_form_label_port }}</label>
<div class="widget">
<input type="number" name="port" id="screen-add-port" required="required" />
<input type="number" name="port" id="studio-add-port" required="required" />
</div>
</div>
<div class="actions">
<button type="button" class="btn-normal modal-close">
{{ l.fleet_screen_form_button_cancel }}
{{ l.fleet_studio_form_button_cancel }}
</button>
<button type="submit" class="green">
<i class="fa fa-plus icon-left"></i> {{ l.fleet_screen_form_add_submit }}
<i class="fa fa-plus icon-left"></i> {{ l.fleet_studio_form_add_submit }}
</button>
</div>
</form>

View File

@ -1,38 +1,38 @@
<div class="modal modal-screen-edit hidden">
<div class="modal modal-studio-edit hidden">
<h2>
{{ l.fleet_screen_form_edit_title }}
{{ l.fleet_studio_form_edit_title }}
</h2>
<form action="/fleet/screen/edit" method="POST">
<input type="hidden" name="id" id="screen-edit-id" />
<form action="/fleet/studio/edit" method="POST">
<input type="hidden" name="id" id="studio-edit-id" />
<div class="form-group">
<label for="screen-edit-name">{{ l.fleet_screen_form_label_name }}</label>
<label for="studio-edit-name">{{ l.fleet_studio_form_label_name }}</label>
<div class="widget">
<input type="text" name="name" id="screen-edit-name" required="required" />
<input type="text" name="name" id="studio-edit-name" required="required" />
</div>
</div>
<div class="form-group">
<label for="screen-edit-host">{{ l.fleet_screen_form_label_host }}</label>
<label for="studio-edit-host">{{ l.fleet_studio_form_label_host }}</label>
<div class="widget">
<input type="text" name="host" id="screen-edit-host" required="required" />
<input type="text" name="host" id="studio-edit-host" required="required" />
</div>
</div>
<div class="form-group">
<label for="screen-edit-port">{{ l.fleet_screen_form_label_port }}</label>
<label for="studio-edit-port">{{ l.fleet_studio_form_label_port }}</label>
<div class="widget">
<input type="number" name="port" id="screen-edit-port" required="required" />
<input type="number" name="port" id="studio-edit-port" required="required" />
</div>
</div>
<div class="actions">
<button type="button" class="btn-normal modal-close">
{{ l.fleet_screen_form_button_cancel }}
{{ l.fleet_studio_form_button_cancel }}
</button>
<button type="submit" class="green">
<i class="fa fa-save icon-left"></i>{{ l.fleet_screen_form_edit_submit }}
<i class="fa fa-save icon-left"></i>{{ l.fleet_studio_form_edit_submit }}
</button>
</div>
</form>