git updater plugin ok
This commit is contained in:
parent
156d1e10a1
commit
480571512b
19
data/www/js/global.js
Normal file
19
data/www/js/global.js
Normal file
@ -0,0 +1,19 @@
|
||||
jQuery(document).ready(function ($) {
|
||||
|
||||
$(document).on('click', '.protected', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (confirm(l.js_common_are_you_sure)) {
|
||||
if ($(this).is('a')) {
|
||||
if ($(this).attr('target') == '_blank') {
|
||||
window.open($(this).attr('href'));
|
||||
} else {
|
||||
document.location.href = $(this).attr('href');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
});
|
||||
@ -114,6 +114,7 @@ chromium --disable-features=Translate --ignore-certificate-errors --disable-web-
|
||||
- Execute following script
|
||||
```bash
|
||||
cd ~/obscreen
|
||||
git pull
|
||||
source ./venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
sudo systemctl restart obscreen-studio.service
|
||||
|
||||
@ -137,6 +137,7 @@ However, I used this one: `(2,82) = 1920x1080 60Hz 1080p`
|
||||
- Execute following script
|
||||
```bash
|
||||
cd ~/obscreen
|
||||
git pull
|
||||
source ./venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
sudo systemctl restart obscreen-studio.service
|
||||
|
||||
@ -165,6 +165,7 @@
|
||||
|
||||
"common_unknown_ipaddr": "Unknown IP address",
|
||||
"common_empty": "[Empty]",
|
||||
"common_are_you_sure": "Are you sure?",
|
||||
"logout": "Logout",
|
||||
"login_error_not_found": "Bad credentials",
|
||||
"login_error_bad_credentials": "Bad credentials",
|
||||
|
||||
@ -165,6 +165,7 @@
|
||||
|
||||
"common_unknown_ipaddr": "Adresse IP inconnue",
|
||||
"common_empty": "[Vide]",
|
||||
"common_are_you_sure": "Êtes-vous sûr ?",
|
||||
"logout": "Déconnexion",
|
||||
"login_error_not_found": "Identifiants invalides",
|
||||
"login_error_bad_credentials": "Identifiants invalides",
|
||||
|
||||
24
plugins/system/GitUpdater/GitUpdater.py
Normal file
24
plugins/system/GitUpdater/GitUpdater.py
Normal file
@ -0,0 +1,24 @@
|
||||
from src.interface.ObPlugin import ObPlugin
|
||||
|
||||
from typing import List, Dict
|
||||
from src.model.entity.Variable import Variable
|
||||
from src.model.enum.VariableType import VariableType
|
||||
from src.model.enum.HookType import HookType
|
||||
from src.model.hook.HookRegistration import HookRegistration
|
||||
|
||||
|
||||
class GitUpdater(ObPlugin):
|
||||
|
||||
def use_id(self):
|
||||
return 'git_updater'
|
||||
|
||||
def use_title(self):
|
||||
return 'Git Updater'
|
||||
|
||||
def use_variables(self) -> List[Variable]:
|
||||
return []
|
||||
|
||||
def use_hooks_registrations(self) -> List[HookRegistration]:
|
||||
return [
|
||||
super().add_static_hook_registration(hook=HookType.H_SYSINFO_TOOLBAR_ACTIONS_START, priority=10),
|
||||
]
|
||||
20
plugins/system/GitUpdater/controller/GitUpdaterController.py
Normal file
20
plugins/system/GitUpdater/controller/GitUpdaterController.py
Normal file
@ -0,0 +1,20 @@
|
||||
from flask import Flask, redirect, url_for
|
||||
from src.interface.ObController import ObController
|
||||
from src.utils import run_system_command, sudo_run_system_command, get_working_directory
|
||||
from src.Application import Application
|
||||
|
||||
|
||||
class GitUpdaterController(ObController):
|
||||
|
||||
def register(self):
|
||||
self._app.add_url_rule('/git-updater/update/now', 'git_updater_update_now', self._auth(self.update_now), methods=['GET'])
|
||||
|
||||
def update_now(self):
|
||||
sudo_run_system_command(['apt', 'install'] + ''.join('git python3-pip python3-venv libsqlite3-dev'))
|
||||
run_system_command(['git', '-C', get_working_directory(), 'stash'])
|
||||
run_system_command(['git', '-C', get_working_directory(), 'checkout', 'tags/v{}'.format(Application.get_version)])
|
||||
run_system_command(['git', '-C', get_working_directory(), 'pull'])
|
||||
run_system_command(['pip', 'install', '-r', 'requirements.txt'])
|
||||
sudo_run_system_command(['systemctl', 'restart', Application.get_name()])
|
||||
|
||||
return redirect(url_for('sysinfo_attribute_list'))
|
||||
3
plugins/system/GitUpdater/lang/en.json
Normal file
3
plugins/system/GitUpdater/lang/en.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"button_update": "Update"
|
||||
}
|
||||
3
plugins/system/GitUpdater/lang/fr.json
Normal file
3
plugins/system/GitUpdater/lang/fr.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"button_update": "Mettre à jour"
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
<a href="{{ url_for('git_updater_update_now') }}" class="btn sysinfo-update protected"><i class="fa fa-cloud-arrow-down icon-left"></i>{{ l.git_updater_button_update }}</a>
|
||||
|
||||
@ -12,6 +12,7 @@ from src.model.enum.HookType import HookType
|
||||
|
||||
class Application:
|
||||
|
||||
|
||||
def __init__(self, project_dir: str):
|
||||
self._project_dir = project_dir
|
||||
self._stop_event = threading.Event()
|
||||
@ -19,7 +20,7 @@ class Application:
|
||||
self._template_renderer = TemplateRenderer(project_dir=project_dir, model_store=self._model_store, render_hook=self.render_hook)
|
||||
self._web_server = WebServer(project_dir=project_dir, model_store=self._model_store, template_renderer=self._template_renderer)
|
||||
|
||||
logging.info("[Obscreen] Starting...")
|
||||
logging.info("[obscreen] Starting...")
|
||||
self._plugin_store = PluginStore(project_dir=project_dir, model_store=self._model_store, template_renderer=self._template_renderer, web_server=self._web_server)
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
|
||||
@ -35,3 +36,11 @@ class Application:
|
||||
def render_hook(self, hook: HookType) -> str:
|
||||
return self._template_renderer.render_hooks(self._plugin_store.map_hooks()[hook])
|
||||
|
||||
@staticmethod
|
||||
def get_name() -> str:
|
||||
return 'obscreen-studio'
|
||||
|
||||
@staticmethod
|
||||
def get_version() -> str:
|
||||
with open("version.txt", 'r') as file:
|
||||
return file.read()
|
||||
|
||||
@ -38,11 +38,6 @@ class SettingsController(ObController):
|
||||
|
||||
if variable.name == 'auth_enabled':
|
||||
self.reload_web_server()
|
||||
if variable.as_bool():
|
||||
return redirect(url_for(
|
||||
'logout',
|
||||
restart=1
|
||||
))
|
||||
|
||||
if variable.name == 'lang':
|
||||
self._model_store.lang().set_lang(variable.value)
|
||||
|
||||
@ -12,7 +12,7 @@ from src.service.ModelStore import ModelStore
|
||||
|
||||
from src.interface.ObController import ObController
|
||||
from src.utils import get_ip_address, am_i_in_docker
|
||||
from src.service.Sysinfos import get_all_sysinfos
|
||||
from src.service.Sysinfo import get_all_sysinfo
|
||||
|
||||
|
||||
class SysinfoController(ObController):
|
||||
@ -25,7 +25,7 @@ class SysinfoController(ObController):
|
||||
def sysinfo(self):
|
||||
return render_template(
|
||||
'sysinfo/list.jinja.html',
|
||||
sysinfos=get_all_sysinfos(),
|
||||
sysinfo=get_all_sysinfo(),
|
||||
last_logs=self._model_store.logging().get_last_lines_of_stdout(100),
|
||||
ro_variables=self._model_store.variable().get_readonly_variables(),
|
||||
env_variables=self._model_store.config().map()
|
||||
|
||||
@ -9,8 +9,8 @@ from src.interface.ObPlugin import ObPlugin
|
||||
class ObController(abc.ABC):
|
||||
|
||||
def __init__(self, web_server, app, auth_required, model_store: ModelStore, template_renderer: TemplateRenderer, plugin: Optional[ObPlugin] = None):
|
||||
self._app = app
|
||||
self._web_server = web_server
|
||||
self._app = app
|
||||
self._auth = auth_required
|
||||
self._model_store = model_store
|
||||
self._template_renderer = template_renderer
|
||||
|
||||
@ -4,12 +4,14 @@ from enum import Enum
|
||||
class HookType(Enum):
|
||||
|
||||
H_FLEETMODE_SLIDESHOW_TOOLBAR_ACTIONS = 'h_fleetmode_slideshow_toolbar_actions'
|
||||
|
||||
H_SLIDESHOW_TOOLBAR_ACTIONS_START = 'h_slideshow_toolbar_actions_start'
|
||||
H_SLIDESHOW_TOOLBAR_ACTIONS_END = 'h_slideshow_toolbar_actions_end'
|
||||
H_SLIDESHOW_CSS = 'h_slideshow_css'
|
||||
H_SLIDESHOW_JAVASCRIPT = 'h_slideshow_javascript'
|
||||
|
||||
H_SYSINFO_TOOLBAR_ACTIONS_START = 'h_sysinfo_toolbar_actions_start'
|
||||
H_SYSINFO_TOOLBAR_ACTIONS_END = 'h_sysinfo_toolbar_actions_end'
|
||||
|
||||
H_FLEET_TOOLBAR_ACTIONS_START = 'h_fleet_toolbar_actions_start'
|
||||
H_FLEET_TOOLBAR_ACTIONS_END = 'h_fleet_toolbar_actions_end'
|
||||
H_FLEET_CSS = 'h_fleet_css'
|
||||
|
||||
@ -74,7 +74,9 @@ class PluginStore:
|
||||
for name, obj in inspect.getmembers(module):
|
||||
if inspect.isclass(obj) and issubclass(obj, ObController) and obj is not ObController:
|
||||
obj(
|
||||
web_server=self._web_server,
|
||||
app=self._web_server.get_app(),
|
||||
auth_required=self._web_server.auth_required,
|
||||
model_store=self._model_store,
|
||||
template_renderer=self._template_renderer,
|
||||
plugin=plugin
|
||||
@ -156,8 +158,7 @@ class PluginStore:
|
||||
|
||||
def is_plugin_enabled(self, plugin: ObPlugin) -> bool:
|
||||
var = self._model_store.variable().get_one_by_name(plugin.get_plugin_variable_name(self.DEFAULT_PLUGIN_ENABLED_VARIABLE))
|
||||
if var.as_bool:
|
||||
logging.info("[Plugin] {} enabled".format(plugin.use_title()))
|
||||
logging.info("[plugin] {} {}".format("🟢" if var.as_bool() else "⚫️", plugin.use_title()))
|
||||
|
||||
return var.as_bool() if var else False
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import platform
|
||||
import psutil
|
||||
import socket
|
||||
|
||||
from src.utils import convert_size
|
||||
from src.utils import convert_size, get_working_directory
|
||||
|
||||
|
||||
def get_rpi_model():
|
||||
@ -94,14 +94,14 @@ def get_default_log_file():
|
||||
return None
|
||||
|
||||
|
||||
def get_all_sysinfos():
|
||||
def get_all_sysinfo():
|
||||
rpi_model = get_rpi_model()
|
||||
infos = {
|
||||
"sysinfo_rpi_model": rpi_model if rpi_model else 'sysinfo_rpi_model_unknown',
|
||||
"sysinfo_storage_free_space": get_free_space(),
|
||||
"sysinfo_memory_usage": "{}{}".format(get_memory_usage()['percent'], "%"),
|
||||
"sysinfo_os_version": get_os_version(),
|
||||
"sysinfo_install_directory": os.getcwd()
|
||||
"sysinfo_install_directory": get_working_directory()
|
||||
}
|
||||
network_info = get_network_info()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from flask import Flask, send_from_directory, Markup
|
||||
from flask import Flask, send_from_directory, Markup, url_for
|
||||
from typing import List
|
||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||
from src.service.ModelStore import ModelStore
|
||||
@ -57,7 +57,8 @@ class TemplateRenderer:
|
||||
os.path.basename(hook_registration.template)
|
||||
))
|
||||
content.append(template.render(
|
||||
**self.get_view_globals()
|
||||
**self.get_view_globals(),
|
||||
url_for=url_for
|
||||
))
|
||||
elif isinstance(hook_registration, FunctionalHookRegistration):
|
||||
content.append(hook_registration.function())
|
||||
|
||||
@ -43,7 +43,6 @@ class WebServer:
|
||||
self.setup()
|
||||
|
||||
def setup(self) -> None:
|
||||
self._auth_enabled = self._model_store.variable().map().get('auth_enabled').as_bool()
|
||||
self._setup_flask_app()
|
||||
self._setup_web_globals()
|
||||
self._setup_web_errors()
|
||||
@ -82,33 +81,37 @@ class WebServer:
|
||||
self._login_manager.init_app(self._app)
|
||||
self._login_manager.login_view = 'login'
|
||||
|
||||
if self._auth_enabled and self._model_store.user().count_all_enabled() == 0:
|
||||
if self._model_store.variable().map().get('auth_enabled').as_bool() and self._model_store.user().count_all_enabled() == 0:
|
||||
self._model_store.user().add_form(User(username="admin", password="admin", enabled=True))
|
||||
|
||||
@self._login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return self._model_store.user().get(user_id)
|
||||
|
||||
def _setup_web_controllers(self) -> None:
|
||||
def auth_required(f):
|
||||
if not self._auth_enabled:
|
||||
return f
|
||||
def auth_required(self, f):
|
||||
if not self._model_store.variable().map().get('auth_enabled').as_bool():
|
||||
return f
|
||||
|
||||
def decorated_function(*args, **kwargs):
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for('login'))
|
||||
def decorated_function(*args, **kwargs):
|
||||
if not self._model_store.variable().map().get('auth_enabled').as_bool():
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for('login'))
|
||||
return f(*args, **kwargs)
|
||||
|
||||
CoreController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
PlayerController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
SlideshowController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
SettingsController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
SysinfoController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
FleetController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
PlaylistController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
AuthController(self, self._app, auth_required, self._model_store, self._template_renderer)
|
||||
return decorated_function
|
||||
|
||||
def _setup_web_controllers(self) -> None:
|
||||
|
||||
CoreController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
PlayerController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
SlideshowController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
SettingsController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
SysinfoController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
FleetController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
PlaylistController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
AuthController(self, self._app, self.auth_required, self._model_store, self._template_renderer)
|
||||
|
||||
def _setup_web_globals(self) -> None:
|
||||
@self._app.context_processor
|
||||
|
||||
24
src/utils.py
24
src/utils.py
@ -1,6 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
import inspect
|
||||
import logging
|
||||
import subprocess
|
||||
import unicodedata
|
||||
@ -234,3 +235,26 @@ def convert_size(size_bytes):
|
||||
p = math.pow(1024, i)
|
||||
s = round(size_bytes / p, 2)
|
||||
return f"{s} {size_name[i]}"
|
||||
|
||||
|
||||
def get_working_directory():
|
||||
return os.getcwd()
|
||||
|
||||
|
||||
def run_system_command(commands: list, shell: bool = False, stdout=subprocess.PIPE, stderr=subprocess.PIPE):
|
||||
return subprocess.run(commands, shell=shell, stdout=stdout, stderr=stderr)
|
||||
|
||||
|
||||
def sudo_run_system_command(commands: list, shell: bool = False, stdout=subprocess.PIPE, stderr=subprocess.PIPE):
|
||||
if commands[0] != 'sudo':
|
||||
commands.insert(0, 'sudo')
|
||||
return run_system_command(commands, shell=shell, stdout=stdout, stderr=stderr)
|
||||
|
||||
|
||||
def get_function_caller(depth: int = 3) -> str:
|
||||
return inspect.getmodulename(inspect.stack()[depth][1])
|
||||
|
||||
|
||||
def clamp(x: float, minimum: float, maximum: float) -> float:
|
||||
return max(minimum, min(x, maximum))
|
||||
|
||||
|
||||
@ -122,6 +122,7 @@
|
||||
<script>
|
||||
var secret_key = '{{ SECRET_KEY }}';
|
||||
var l = {
|
||||
'js_common_are_you_sure': '{{ l.common_are_you_sure }}',
|
||||
'js_playlist_delete_confirmation': '{{ l.js_playlist_delete_confirmation }}',
|
||||
'js_slideshow_slide_delete_confirmation': '{{ l.js_slideshow_slide_delete_confirmation }}',
|
||||
'js_fleet_studio_delete_confirmation': '{{ l.js_fleet_studio_delete_confirmation }}',
|
||||
@ -131,6 +132,7 @@
|
||||
};
|
||||
</script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/global.js"></script>
|
||||
{{ HOOK(H_ROOT_JAVASCRIPT) }}
|
||||
{% block add_js %}{% endblock %}
|
||||
</body>
|
||||
|
||||
@ -13,7 +13,9 @@
|
||||
<div class="toolbar">
|
||||
<h2>{{ l.sysinfo_page_title }}</h2>
|
||||
<div class="toolbar-actions">
|
||||
{{ HOOK(H_SYSINFO_TOOLBAR_ACTIONS_START) }}
|
||||
<button class="purple sysinfo-restart"><i class="fa fa-refresh icon-left"></i>{{ l.sysinfo_panel_button_restart }}</button>
|
||||
{{ HOOK(H_SYSINFO_TOOLBAR_ACTIONS_END) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
@ -35,10 +37,10 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% for sysinfo_label, sysinfo in sysinfos.items() %}
|
||||
{% for sysinfo_label, sysinfo_value in sysinfo.items() %}
|
||||
<tr>
|
||||
<td>{{ l[sysinfo_label] }}</td>
|
||||
<td>{{ t(sysinfo) }}</td>
|
||||
<td>{{ t(sysinfo_value) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user