ok
This commit is contained in:
parent
046666b524
commit
b7231a35a5
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@ var/run/*
|
||||
.env
|
||||
venv/
|
||||
node_modules
|
||||
tmp.py
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -9,14 +9,15 @@ jQuery(document).ready(function ($) {
|
||||
}).data('input');
|
||||
|
||||
$form.find('.content-object-input').each(function() {
|
||||
const active = $(this).attr('data-input-type') === inputType;
|
||||
|
||||
if ($(this).is('input[type=file]')) {
|
||||
$(this).prop('disabled', !active).prop('required', active);
|
||||
$(this).parents('label:eq(0)').toggleClass('hidden', !active);
|
||||
} else {
|
||||
$(this).prop('disabled', !active).prop('required', active).toggleClass('hidden', !active);
|
||||
}
|
||||
const $input = $(this);
|
||||
const active = $input.attr('data-input-type') === inputType;
|
||||
const $holder = $input.parents('.object-holder:eq(0)');
|
||||
$holder.find('input, select, textarea').prop('disabled', !active).prop('required', active).toggleClass('hidden', !active);
|
||||
$holder.toggleClass('hidden', !active);
|
||||
console.log(active)
|
||||
console.log($input)
|
||||
if (active)
|
||||
console.log($holder)
|
||||
});
|
||||
|
||||
const optionAttributes = $selectedOption.get(0).attributes;
|
||||
|
||||
@ -35,6 +35,28 @@ form {
|
||||
flex: 1;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.object-holder {
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
label {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
flex: 1;
|
||||
|
||||
@ -226,6 +226,7 @@
|
||||
"basic_month_10": "October",
|
||||
"basic_month_11": "November",
|
||||
"basic_month_12": "December",
|
||||
"common_bad_directory_path": "Directory does not exist in the specified path",
|
||||
"common_bad_file_type": "Bad file type uploaded",
|
||||
"common_restart_needed": "Please restart obscreen studio (or restart the device) for the changes to take effect",
|
||||
"common_pick_element": "Pick an element",
|
||||
@ -282,6 +283,9 @@
|
||||
"enum_application_language_french": "French",
|
||||
"enum_application_language_italian": "Italian",
|
||||
"enum_application_language_spanish": "Spanish",
|
||||
"enum_content_type_external_storage": "External Storage",
|
||||
"enum_content_type_external_storage_object_label": "Choose an external storage drive",
|
||||
"enum_content_type_external_storage_target_path_label": "Write an existing directory path inside your storage device",
|
||||
"enum_content_type_url": "URL",
|
||||
"enum_content_type_video": "Video",
|
||||
"enum_content_type_picture": "Picture",
|
||||
|
||||
@ -227,6 +227,7 @@
|
||||
"basic_month_10": "Octubre",
|
||||
"basic_month_11": "Noviembre",
|
||||
"basic_month_12": "Diciembre",
|
||||
"common_bad_directory_path": "El directorio no existe en la ruta especificada",
|
||||
"common_bad_file_type": "Tipo de archivo incorrecto cargado",
|
||||
"common_restart_needed": "Reinicie obscreen studio (o reinicie el dispositivo) para que los cambios surtan efecto",
|
||||
"common_pick_element": "Elige un elemento",
|
||||
@ -283,6 +284,9 @@
|
||||
"enum_application_language_french": "Francés",
|
||||
"enum_application_language_italian": "Italiano",
|
||||
"enum_application_language_spanish": "Español",
|
||||
"enum_content_type_external_storage": "Almacenamiento externo",
|
||||
"enum_content_type_external_storage_object_label": "Elija una unidad de almacenamiento externa",
|
||||
"enum_content_type_external_storage_target_path_label": "Escribe una ruta de directorio existente dentro de tu dispositivo de almacenamiento",
|
||||
"enum_content_type_url": "URL",
|
||||
"enum_content_type_video": "Video",
|
||||
"enum_content_type_picture": "Imagen",
|
||||
|
||||
@ -228,6 +228,7 @@
|
||||
"basic_month_10": "Octobre",
|
||||
"basic_month_11": "Novembre",
|
||||
"basic_month_12": "Décembre",
|
||||
"common_bad_directory_path": "Le dossier n'existe pas dans le chemin indiqué",
|
||||
"common_bad_file_type": "Type de fichier uploadé incorrect",
|
||||
"common_restart_needed": "Veuillez redémarrer obscreen studio (ou redémarrer l'appareil) pour que les changements soient pris en compte",
|
||||
"common_pick_element": "Choisissez un élément",
|
||||
@ -284,6 +285,9 @@
|
||||
"enum_application_language_french": "Français",
|
||||
"enum_application_language_italian": "Italien",
|
||||
"enum_application_language_spanish": "Espagnol",
|
||||
"enum_content_type_external_storage": "Stockage externe",
|
||||
"enum_content_type_external_storage_object_label": "Choisissez un disque de stockage externe",
|
||||
"enum_content_type_external_storage_target_path_label": "Écrivez un chemin de répertoire existant dans votre périphérique de stockage",
|
||||
"enum_content_type_url": "URL",
|
||||
"enum_content_type_video": "Vidéo",
|
||||
"enum_content_type_picture": "Image",
|
||||
|
||||
@ -227,6 +227,7 @@
|
||||
"basic_month_10": "Ottobre",
|
||||
"basic_month_11": "Novembre",
|
||||
"basic_month_12": "Dicembre",
|
||||
"common_bad_directory_path": "La directory non esiste nel percorso specificato",
|
||||
"common_bad_file_type": "Tipo di file caricato non valido",
|
||||
"common_restart_needed": "Riavvia obscreen studio (o riavvia il dispositivo) affinché le modifiche abbiano effetto",
|
||||
"common_pick_element": "Scegli un elemento",
|
||||
@ -283,6 +284,9 @@
|
||||
"enum_application_language_french": "Francese",
|
||||
"enum_application_language_italian": "Italiano",
|
||||
"enum_application_language_spanish": "Spagnolo",
|
||||
"enum_content_type_external_storage": "Archiviazione esterna",
|
||||
"enum_content_type_external_storage_object_label": "Scegli un'unità di archiviazione esterna",
|
||||
"enum_content_type_external_storage_target_path_label": "Écrivez un chemin de répertoire existant dans votre périphérique de stockage",
|
||||
"enum_content_type_url": "URL",
|
||||
"enum_content_type_video": "Video",
|
||||
"enum_content_type_picture": "Immagine",
|
||||
|
||||
@ -48,6 +48,7 @@ class ContentController(ObController):
|
||||
self._model_store.variable().update_by_name('last_pillmenu_slideshow', 'slideshow_content_list')
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
slides_with_content = self._model_store.slide().get_all_indexed(attribute='content_id', multiple=True)
|
||||
external_storages = self._model_store.external_storage().list_usb_storage_devices()
|
||||
|
||||
return render_template(
|
||||
'slideshow/contents/list.jinja.html',
|
||||
@ -59,6 +60,11 @@ class ContentController(ObController):
|
||||
working_folder_children=self._model_store.folder().get_children(folder=working_folder, entity=FolderEntity.CONTENT, sort='created_at', ascending=False),
|
||||
enum_content_type=ContentType,
|
||||
enum_folder_entity=FolderEntity,
|
||||
external_storages={storage.mount_point: "{} ({} - {}GB)".format(
|
||||
storage.mount_point,
|
||||
storage.logical_name,
|
||||
storage.total_size_in_gigabytes()
|
||||
) for storage in external_storages},
|
||||
)
|
||||
|
||||
def slideshow_content_add(self):
|
||||
@ -67,12 +73,21 @@ class ContentController(ObController):
|
||||
"path": working_folder_path,
|
||||
}
|
||||
|
||||
location = request.form['object'] if 'object' in request.form else None
|
||||
|
||||
if 'storage' in request.form:
|
||||
location = "{}/{}".format(request.form['storage'], location.strip('/'))
|
||||
|
||||
if not os.path.exists(location):
|
||||
route_args["error"] = "common_bad_directory_path"
|
||||
return redirect(url_for('slideshow_content_list', **route_args))
|
||||
|
||||
content = self._model_store.content().add_form_raw(
|
||||
name=request.form['name'],
|
||||
type=str_to_enum(request.form['type'], ContentType),
|
||||
request_files=request.files,
|
||||
upload_dir=self._app.config['UPLOAD_FOLDER'],
|
||||
location=request.form['object'] if 'object' in request.form else None,
|
||||
location=location,
|
||||
folder_id=working_folder.id if working_folder else None
|
||||
)
|
||||
|
||||
@ -87,7 +102,7 @@ class ContentController(ObController):
|
||||
for key in request.files:
|
||||
files = request.files.getlist(key)
|
||||
for file in files:
|
||||
type = ContentType.guess_content_type_file(file)
|
||||
type = ContentType.guess_content_type_file(file.filename)
|
||||
name = file.filename.rsplit('.', 1)[0]
|
||||
|
||||
if type:
|
||||
@ -240,7 +255,9 @@ class ContentController(ObController):
|
||||
var_external_url = self._model_store.variable().get_one_by_name('external_url')
|
||||
location = content.location
|
||||
|
||||
if content.type == ContentType.YOUTUBE:
|
||||
if content.type == ContentType.EXTERNAL_STORAGE:
|
||||
location = "file://{}".format(location)
|
||||
elif content.type == ContentType.YOUTUBE:
|
||||
location = "https://www.youtube.com/watch?v={}".format(content.location)
|
||||
elif len(var_external_url.as_string().strip()) > 0 and content.has_file():
|
||||
location = "{}/{}".format(var_external_url.value, content.location)
|
||||
|
||||
@ -2,10 +2,12 @@ import json
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, List, Dict
|
||||
from flask import Flask, render_template, redirect, request, url_for, send_from_directory, jsonify, abort
|
||||
from pathlib import Path
|
||||
|
||||
from src.model.entity.Slide import Slide
|
||||
from src.model.enum.ContentType import ContentType
|
||||
from src.exceptions.NoFallbackPlaylistException import NoFallbackPlaylistException
|
||||
from src.service.ModelStore import ModelStore
|
||||
from src.interface.ObController import ObController
|
||||
@ -123,39 +125,28 @@ class PlayerController(ObController):
|
||||
playlist_notifications = []
|
||||
|
||||
for slide in slides:
|
||||
if slide['content_id']:
|
||||
if int(slide['content_id']) not in contents:
|
||||
continue
|
||||
|
||||
content = contents[int(slide['content_id'])].to_dict()
|
||||
slide['name'] = content['name']
|
||||
slide['location'] = content['location']
|
||||
slide['type'] = content['type']
|
||||
else:
|
||||
if not slide['content_id']:
|
||||
continue
|
||||
|
||||
has_valid_start_date = 'cron_schedule' in slide and slide['cron_schedule'] and get_safe_cron_descriptor(slide['cron_schedule']) and is_cron_calendar_moment(slide['cron_schedule'])
|
||||
has_valid_end_date = 'cron_schedule_end' in slide and slide['cron_schedule_end'] and get_safe_cron_descriptor(slide['cron_schedule_end']) and is_cron_calendar_moment(slide['cron_schedule_end'])
|
||||
if int(slide['content_id']) not in contents:
|
||||
continue
|
||||
|
||||
if slide['is_notification']:
|
||||
if has_valid_start_date:
|
||||
playlist_notifications.append(slide)
|
||||
else:
|
||||
logging.warn('Slide \'{}\' is a notification but start date is invalid'.format(slide['name']))
|
||||
content = contents[int(slide['content_id'])].to_dict()
|
||||
slide['name'] = content['name']
|
||||
slide['location'] = content['location']
|
||||
slide['type'] = content['type']
|
||||
|
||||
if slide['type'] == ContentType.EXTERNAL_STORAGE.value:
|
||||
mount_point_dir = Path(slide['location'])
|
||||
if mount_point_dir.is_dir():
|
||||
for file in mount_point_dir.iterdir():
|
||||
if file.is_file() and not file.stem.startswith('.'):
|
||||
slide['type'] = ContentType.guess_content_type_file(str(file.resolve())).value
|
||||
slide['location'] = "file://{}".format(str(file.resolve()))
|
||||
slide['name'] = file.stem
|
||||
self._feed_playlist(playlist_loop, playlist_notifications, slide)
|
||||
else:
|
||||
if has_valid_start_date:
|
||||
start_date = get_cron_date_time(slide['cron_schedule'], object=True)
|
||||
if datetime.now() <= start_date:
|
||||
continue
|
||||
|
||||
if has_valid_end_date:
|
||||
end_date = get_cron_date_time(slide['cron_schedule_end'], object=True)
|
||||
if datetime.now() >= end_date:
|
||||
continue
|
||||
|
||||
playlist_loop.append(slide)
|
||||
else:
|
||||
playlist_loop.append(slide)
|
||||
self._feed_playlist(playlist_loop, playlist_notifications, slide)
|
||||
|
||||
playlists = {
|
||||
'playlist_id': playlist.id if playlist else None,
|
||||
@ -167,3 +158,24 @@ class PlayerController(ObController):
|
||||
}
|
||||
|
||||
return playlists
|
||||
|
||||
def _feed_playlist(self, loop: List, notifications: List, slide: Dict) -> None:
|
||||
has_valid_start_date = 'cron_schedule' in slide and slide['cron_schedule'] and get_safe_cron_descriptor(slide['cron_schedule']) and is_cron_calendar_moment(slide['cron_schedule'])
|
||||
has_valid_end_date = 'cron_schedule_end' in slide and slide['cron_schedule_end'] and get_safe_cron_descriptor(slide['cron_schedule_end']) and is_cron_calendar_moment(slide['cron_schedule_end'])
|
||||
|
||||
if slide['is_notification']:
|
||||
if has_valid_start_date:
|
||||
return notifications.append(slide)
|
||||
return logging.warn('Slide \'{}\' is a notification but start date is invalid'.format(slide['name']))
|
||||
|
||||
if has_valid_start_date:
|
||||
start_date = get_cron_date_time(slide['cron_schedule'], object=True)
|
||||
if datetime.now() <= start_date:
|
||||
return
|
||||
|
||||
if has_valid_end_date:
|
||||
end_date = get_cron_date_time(slide['cron_schedule_end'], object=True)
|
||||
if datetime.now() >= end_date:
|
||||
return
|
||||
|
||||
loop.append(slide)
|
||||
|
||||
@ -177,7 +177,7 @@ class ContentManager(ModelManager):
|
||||
if not object or object.filename == '':
|
||||
return None
|
||||
|
||||
guessed_type = ContentType.guess_content_type_file(object)
|
||||
guessed_type = ContentType.guess_content_type_file(object.filename)
|
||||
|
||||
if not guessed_type or guessed_type != type:
|
||||
return None
|
||||
|
||||
208
src/manager/ExternalStorageManager.py
Normal file
208
src/manager/ExternalStorageManager.py
Normal file
@ -0,0 +1,208 @@
|
||||
import os
|
||||
import psutil
|
||||
import platform
|
||||
import logging
|
||||
|
||||
from typing import Dict, Optional, List, Tuple, Union
|
||||
from werkzeug.datastructures import FileStorage
|
||||
|
||||
from src.model.entity.ExternalStorage import ExternalStorage
|
||||
from src.util.utils import get_yt_video_id
|
||||
from src.manager.DatabaseManager import DatabaseManager
|
||||
from src.manager.LangManager import LangManager
|
||||
from src.manager.UserManager import UserManager
|
||||
from src.manager.VariableManager import VariableManager
|
||||
from src.service.ModelManager import ModelManager
|
||||
from src.util.UtilFile import randomize_filename
|
||||
|
||||
|
||||
class ExternalStorageManager(ModelManager):
|
||||
|
||||
TABLE_NAME = "external_storage"
|
||||
TABLE_MODEL = [
|
||||
"uuid CHAR(255)",
|
||||
"total_size INTEGER",
|
||||
"logical_name TEXT",
|
||||
"mount_point TEXT",
|
||||
"content_id INTEGER",
|
||||
"created_by CHAR(255)",
|
||||
"updated_by CHAR(255)",
|
||||
"created_at INTEGER",
|
||||
"updated_at INTEGER"
|
||||
]
|
||||
|
||||
def __init__(self, lang_manager: LangManager, database_manager: DatabaseManager, user_manager: UserManager, variable_manager: VariableManager):
|
||||
super().__init__(lang_manager, database_manager, user_manager, variable_manager)
|
||||
self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL)
|
||||
|
||||
def hydrate_object(self, raw_external_storage: dict, id: int = None) -> ExternalStorage:
|
||||
if id:
|
||||
raw_external_storage['id'] = id
|
||||
|
||||
[raw_external_storage, user_tracker_edits] = self.user_manager.initialize_user_trackers(raw_external_storage)
|
||||
|
||||
if len(user_tracker_edits) > 0:
|
||||
self._db.update_by_id(self.TABLE_NAME, raw_external_storage['id'], user_tracker_edits)
|
||||
|
||||
return ExternalStorage(**raw_external_storage)
|
||||
|
||||
def hydrate_list(self, raw_external_storages: list) -> List[ExternalStorage]:
|
||||
return [self.hydrate_object(raw_external_storage) for raw_external_storage in raw_external_storages]
|
||||
|
||||
def get(self, id: int) -> Optional[ExternalStorage]:
|
||||
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[ExternalStorage]:
|
||||
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
|
||||
|
||||
def get_one_by(self, query) -> Optional[ExternalStorage]:
|
||||
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
|
||||
|
||||
if not object:
|
||||
return None
|
||||
|
||||
return self.hydrate_object(object)
|
||||
|
||||
def get_all(self, sort: Optional[str] = 'created_at', ascending=False) -> List[ExternalStorage]:
|
||||
return self.hydrate_list(self._db.get_all(table_name=self.TABLE_NAME, sort=sort, ascending=ascending))
|
||||
|
||||
def get_all_indexed(self, attribute: str = 'id', multiple=False) -> Dict[str, ExternalStorage]:
|
||||
index = {}
|
||||
|
||||
for item in self.get_external_storages():
|
||||
id = getattr(item, attribute)
|
||||
if multiple:
|
||||
if id not in index:
|
||||
index[id] = []
|
||||
index[id].append(item)
|
||||
else:
|
||||
index[id] = item
|
||||
|
||||
return index
|
||||
|
||||
def forget_for_user(self, user_id: int):
|
||||
external_storages = self.get_by("created_by = '{}' or updated_by = '{}'".format(user_id, user_id))
|
||||
edits_external_storages = self.user_manager.forget_user_for_entity(external_storages, user_id)
|
||||
|
||||
for external_storage_id, edits in edits_external_storages.items():
|
||||
self._db.update_by_id(self.TABLE_NAME, external_storage_id, edits)
|
||||
|
||||
def get_external_storages(self, content_id: Optional[int] = None) -> List[ExternalStorage]:
|
||||
query = " 1=1 "
|
||||
|
||||
if content_id:
|
||||
query = "{} {}".format(query, "AND content_id = {}".format(content_id))
|
||||
|
||||
return self.get_by(query=query)
|
||||
|
||||
def pre_add(self, external_storage: Dict) -> Dict:
|
||||
self.user_manager.track_user_on_create(external_storage)
|
||||
self.user_manager.track_user_on_update(external_storage)
|
||||
return external_storage
|
||||
|
||||
def pre_update(self, external_storage: Dict) -> Dict:
|
||||
self.user_manager.track_user_on_update(external_storage)
|
||||
return external_storage
|
||||
|
||||
def pre_delete(self, external_storage_id: str) -> str:
|
||||
return external_storage_id
|
||||
|
||||
def post_add(self, external_storage_id: str) -> str:
|
||||
return external_storage_id
|
||||
|
||||
def post_update(self, external_storage_id: str) -> str:
|
||||
return external_storage_id
|
||||
|
||||
def post_updates(self):
|
||||
pass
|
||||
|
||||
def post_delete(self, external_storage_id: str) -> str:
|
||||
return external_storage_id
|
||||
|
||||
def update_form(self, id: int, logical_name: Optional[str] = None, mount_point: Optional[str] = None, content_id: Optional[int] = None, total_size: Optional[int] = None) -> ExternalStorage:
|
||||
external_storage = self.get(id)
|
||||
|
||||
if not external_storage:
|
||||
return
|
||||
|
||||
form = {
|
||||
"total_size": total_size if total_size else external_storage.total_size,
|
||||
"logical_name": logical_name if logical_name else external_storage.logical_name,
|
||||
"mount_point": mount_point if mount_point else external_storage.mount_point,
|
||||
"content_id": content_id if content_id else external_storage.content_id,
|
||||
}
|
||||
|
||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update(form))
|
||||
self.post_update(id)
|
||||
return self.get(id)
|
||||
|
||||
def add_form(self, external_storage: Union[ExternalStorage, Dict]) -> None:
|
||||
form = external_storage
|
||||
|
||||
if not isinstance(external_storage, dict):
|
||||
form = external_storage.to_dict()
|
||||
del form['id']
|
||||
|
||||
self._db.add(self.TABLE_NAME, self.pre_add(form))
|
||||
self.post_add(external_storage.id)
|
||||
|
||||
def add_form_raw(self, logical_name: Optional[str] = None, mount_point: Optional[str] = None, content_id: Optional[int] = None, total_size: Optional[int] = None) -> ExternalStorage:
|
||||
external_storage = ExternalStorage(
|
||||
logical_name=logical_name,
|
||||
mount_point=mount_point,
|
||||
content_id=content_id,
|
||||
total_size=total_size,
|
||||
)
|
||||
|
||||
self.add_form(external_storage)
|
||||
return self.get_one_by(query="uuid = '{}'".format(external_storage.uuid))
|
||||
|
||||
def delete(self, id: int) -> None:
|
||||
external_storage = self.get(id)
|
||||
|
||||
if external_storage:
|
||||
self.pre_delete(id)
|
||||
self._db.delete_by_id(self.TABLE_NAME, id)
|
||||
self.post_delete(id)
|
||||
|
||||
def to_dict(self, external_storages: List[ExternalStorage]) -> List[Dict]:
|
||||
return [external_storage.to_dict() for external_storage in external_storages]
|
||||
|
||||
@staticmethod
|
||||
def list_usb_storage_devices() -> List[ExternalStorage]:
|
||||
os_type = platform.system()
|
||||
partitions = psutil.disk_partitions()
|
||||
removable_devices = []
|
||||
for partition in partitions:
|
||||
if 'dontbrowse' in partition.opts:
|
||||
continue
|
||||
|
||||
if os_type == "Windows":
|
||||
if 'removable' in partition.opts:
|
||||
removable_devices.append(partition)
|
||||
else:
|
||||
if '/media' in partition.mountpoint or '/run/media' in partition.mountpoint or '/mnt' in partition.mountpoint or '/Volumes' in partition.mountpoint:
|
||||
removable_devices.append(partition)
|
||||
|
||||
if not removable_devices:
|
||||
return {}
|
||||
|
||||
storages = []
|
||||
|
||||
for device in removable_devices:
|
||||
try:
|
||||
usage = psutil.disk_usage(device.mountpoint)
|
||||
# total_size = usage.total / (1024 ** 3)
|
||||
external_storage = ExternalStorage(
|
||||
logical_name=device.device,
|
||||
mount_point=device.mountpoint,
|
||||
content_id=None,
|
||||
total_size=usage.total,
|
||||
)
|
||||
storages.append(external_storage)
|
||||
except Exception as e:
|
||||
logging.error(f"Could not retrieve size for device {device.device}: {e}")
|
||||
|
||||
return storages
|
||||
|
||||
141
src/model/entity/ExternalStorage.py
Normal file
141
src/model/entity/ExternalStorage.py
Normal file
@ -0,0 +1,141 @@
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
import math
|
||||
|
||||
from typing import Optional, Union
|
||||
|
||||
from src.model.enum.FolderEntity import FolderEntity
|
||||
from src.util.utils import str_to_enum
|
||||
|
||||
|
||||
class ExternalStorage:
|
||||
|
||||
def __init__(self, uuid: str = '', total_size: int = 0, logical_name: str = '', mount_point: str = '', content_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._uuid = uuid if uuid else self.generate_and_set_uuid()
|
||||
self._id = id if id else None
|
||||
self._total_size = total_size
|
||||
self._logical_name = logical_name
|
||||
self._mount_point = mount_point
|
||||
self._content_id = content_id
|
||||
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())
|
||||
self._updated_at = int(updated_at if updated_at else time.time())
|
||||
|
||||
def generate_and_set_uuid(self) -> str:
|
||||
self._uuid = str(uuid.uuid4())
|
||||
|
||||
return self._uuid
|
||||
|
||||
@property
|
||||
def id(self) -> Optional[int]:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def uuid(self) -> str:
|
||||
return self._uuid
|
||||
|
||||
@uuid.setter
|
||||
def uuid(self, value: str):
|
||||
self._uuid = value
|
||||
|
||||
@property
|
||||
def content_id(self) -> Optional[int]:
|
||||
return self._content_id
|
||||
|
||||
@content_id.setter
|
||||
def content_id(self, value: Optional[int]):
|
||||
self._content_id = value
|
||||
|
||||
@property
|
||||
def total_size(self) -> int:
|
||||
return self._total_size
|
||||
|
||||
@total_size.setter
|
||||
def total_size(self, value: int):
|
||||
self._total_size = value
|
||||
|
||||
@property
|
||||
def logical_name(self) -> str:
|
||||
return self._logical_name
|
||||
|
||||
@logical_name.setter
|
||||
def logical_name(self, value: str):
|
||||
self._logical_name = value
|
||||
|
||||
@property
|
||||
def mount_point(self) -> str:
|
||||
return self._mount_point
|
||||
|
||||
@mount_point.setter
|
||||
def mount_point(self, value: str):
|
||||
self._mount_point = value
|
||||
|
||||
@property
|
||||
def created_by(self) -> str:
|
||||
return self._created_by
|
||||
|
||||
@created_by.setter
|
||||
def created_by(self, value: str):
|
||||
self._created_by = value
|
||||
|
||||
@property
|
||||
def updated_by(self) -> str:
|
||||
return self._updated_by
|
||||
|
||||
@updated_by.setter
|
||||
def updated_by(self, value: str):
|
||||
self._updated_by = value
|
||||
|
||||
@property
|
||||
def created_at(self) -> int:
|
||||
return self._created_at
|
||||
|
||||
@created_at.setter
|
||||
def created_at(self, value: int):
|
||||
self._created_at = value
|
||||
|
||||
@property
|
||||
def updated_at(self) -> int:
|
||||
return self._updated_at
|
||||
|
||||
@updated_at.setter
|
||||
def updated_at(self, value: int):
|
||||
self._updated_at = value
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"ExternalStorage(" \
|
||||
f"id='{self.id}',\n" \
|
||||
f"uuid='{self.uuid}',\n" \
|
||||
f"total_size='{self.total_size}',\n" \
|
||||
f"logical_name='{self.logical_name}',\n" \
|
||||
f"mount_point='{self.mount_point}',\n" \
|
||||
f"content_id='{self.content_id}',\n" \
|
||||
f"created_by='{self.created_by}',\n" \
|
||||
f"updated_by='{self.updated_by}',\n" \
|
||||
f"created_at='{self.created_at}',\n" \
|
||||
f"updated_at='{self.updated_at}',\n" \
|
||||
f")"
|
||||
|
||||
def to_json(self) -> str:
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"id": self.id,
|
||||
"uuid": self.uuid,
|
||||
"total_size": self.total_size,
|
||||
"logical_name": self.logical_name,
|
||||
"mount_point": self.mount_point,
|
||||
"content_id": self.content_id,
|
||||
"created_by": self.created_by,
|
||||
"updated_by": self.updated_by,
|
||||
"created_at": self.created_at,
|
||||
"updated_at": self.updated_at,
|
||||
}
|
||||
|
||||
def total_size_in_gigabytes(self) -> str:
|
||||
return f"{self.total_size / (1024 ** 3):.2f}"
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ class ContentInputType(Enum):
|
||||
|
||||
UPLOAD = 'upload'
|
||||
TEXT = 'text'
|
||||
STORAGE = 'storage'
|
||||
|
||||
@staticmethod
|
||||
def is_editable(value: Enum) -> bool:
|
||||
@ -17,18 +18,21 @@ class ContentInputType(Enum):
|
||||
return False
|
||||
elif value == ContentInputType.TEXT:
|
||||
return True
|
||||
elif value == ContentInputType.STORAGE:
|
||||
return False
|
||||
|
||||
|
||||
class ContentType(Enum):
|
||||
|
||||
EXTERNAL_STORAGE = 'external_storage'
|
||||
PICTURE = 'picture'
|
||||
URL = 'url'
|
||||
YOUTUBE = 'youtube'
|
||||
VIDEO = 'video'
|
||||
|
||||
@staticmethod
|
||||
def guess_content_type_file(file):
|
||||
mime_type, _ = mimetypes.guess_type(file.filename)
|
||||
def guess_content_type_file(filename: str):
|
||||
mime_type, _ = mimetypes.guess_type(filename)
|
||||
|
||||
if mime_type in [
|
||||
'image/gif',
|
||||
@ -55,6 +59,8 @@ class ContentType(Enum):
|
||||
return ContentInputType.TEXT
|
||||
elif value == ContentType.URL:
|
||||
return ContentInputType.TEXT
|
||||
elif value == ContentType.EXTERNAL_STORAGE:
|
||||
return ContentInputType.STORAGE
|
||||
|
||||
@staticmethod
|
||||
def get_fa_icon(value: Union[Enum, str]) -> str:
|
||||
@ -69,6 +75,8 @@ class ContentType(Enum):
|
||||
return 'fa-brands fa-youtube'
|
||||
elif value == ContentType.URL:
|
||||
return 'fa-link'
|
||||
elif value == ContentType.EXTERNAL_STORAGE:
|
||||
return 'fa-brands fa-usb'
|
||||
|
||||
return 'fa-file'
|
||||
|
||||
@ -85,5 +93,7 @@ class ContentType(Enum):
|
||||
return 'youtube'
|
||||
elif value == ContentType.URL:
|
||||
return 'danger'
|
||||
elif value == ContentType.EXTERNAL_STORAGE:
|
||||
return 'other'
|
||||
|
||||
return 'neutral'
|
||||
|
||||
@ -12,6 +12,7 @@ from src.manager.LangManager import LangManager
|
||||
from src.manager.DatabaseManager import DatabaseManager
|
||||
from src.manager.ConfigManager import ConfigManager
|
||||
from src.manager.LoggingManager import LoggingManager
|
||||
from src.manager.ExternalStorageManager import ExternalStorageManager
|
||||
|
||||
|
||||
class ModelStore:
|
||||
@ -39,6 +40,7 @@ class ModelStore:
|
||||
self._playlist_manager = PlaylistManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager, variable_manager=self._variable_manager)
|
||||
self._slide_manager = SlideManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager, variable_manager=self._variable_manager)
|
||||
self._content_manager = ContentManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager, variable_manager=self._variable_manager)
|
||||
self._external_storage_manager = ExternalStorageManager(lang_manager=self._lang_manager, database_manager=self._database_manager, user_manager=self._user_manager, variable_manager=self._variable_manager)
|
||||
self._variable_manager.reload()
|
||||
|
||||
def logging(self) -> LoggingManager:
|
||||
@ -47,6 +49,9 @@ class ModelStore:
|
||||
def config(self) -> ConfigManager:
|
||||
return self._config_manager
|
||||
|
||||
def external_storage(self) -> ExternalStorageManager:
|
||||
return self._external_storage_manager
|
||||
|
||||
def variable(self) -> VariableManager:
|
||||
return self._variable_manager
|
||||
|
||||
@ -81,6 +86,7 @@ class ModelStore:
|
||||
return self._get_plugins()
|
||||
|
||||
def on_user_delete(self, user_id: int) -> None:
|
||||
self._external_storage_manager.forget_for_user(user_id)
|
||||
self._playlist_manager.forget_for_user(user_id)
|
||||
self._folder_manager.forget_for_user(user_id)
|
||||
self._node_player_group_manager.forget_for_user(user_id)
|
||||
|
||||
@ -327,7 +327,8 @@
|
||||
}
|
||||
|
||||
const loadPicture = function(element, callbackReady, item) {
|
||||
element.innerHTML = `<img src="/${item.location}" alt="" />`;
|
||||
const hasScheme = item.location.indexOf('://') >= 0;
|
||||
element.innerHTML = `<img src="${hasScheme ? item.location : ('/' + item.location)}" alt="" />`;
|
||||
callbackReady(function() {});
|
||||
};
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
{% block add_js %}
|
||||
<script>
|
||||
var external_storages = {{ json_dumps(external_storages) | safe }};
|
||||
var l = $.extend(l, {
|
||||
'js_common_http_error_occured': '{{ l.common_http_error_occured }}',
|
||||
'js_common_http_error_413': '{{ l.common_http_error_413 }}'
|
||||
|
||||
@ -30,16 +30,30 @@
|
||||
<div class="form-group object-input">
|
||||
<label for="" class="object-label">{{ l.slideshow_content_form_label_object }}</label>
|
||||
<div class="widget">
|
||||
<input type="text" name="object" data-input-type="text" class="content-object-input" />
|
||||
<div class="object-holder hidden">
|
||||
<input type="text" name="object" data-input-type="text" class="content-object-input" />
|
||||
</div>
|
||||
|
||||
<label for="content-add-object-input-upload" class="btn-upload hidden">
|
||||
<input type="file" name="object" data-input-type="upload" class="content-object-input" disabled="disabled" id="content-add-object-input-upload"/>
|
||||
<label for="content-add-object-input-upload" class="btn-upload hidden object-holder">
|
||||
<input type="file" name="object" data-input-type="upload" class="content-object-input" disabled="disabled" id="content-add-object-input-upload" />
|
||||
<span class="btn btn-neutral normal">
|
||||
<i class="fa fa-file-import"></i>
|
||||
{{ l.slideshow_content_form_button_upload }}
|
||||
</span>
|
||||
<input type="text" value="{{ l.slideshow_content_form_button_upload_choosen }}" disabled="disabled" class="disabled" />
|
||||
</label>
|
||||
|
||||
<div class="object-holder hidden">
|
||||
<select name="storage" disabled="disabled">
|
||||
{% for key, value in external_storages.items() %}
|
||||
<option value="{{ key }}">{{ value }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="form-group">
|
||||
<label for="content-add-object-input-storage-target-path">{{ l.enum_content_type_external_storage_target_path_label }}</label>
|
||||
<input type="text" name="object" data-input-type="storage" class="content-object-input" disabled="disabled" id="content-add-object-input-storage-target-path" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user