visual feedback during updates

This commit is contained in:
j 2016-01-12 20:30:51 +05:30
parent 0f0202a3aa
commit 516397ae44
9 changed files with 274 additions and 25 deletions

12
ctl
View File

@ -54,7 +54,7 @@ hash -r 2>/dev/null
# allow more open files # allow more open files
ulimit -S -n 2048 ulimit -S -n 2048
function remove_loginscript { function remove_autostart {
if [ $SYSTEM == "Darwin" ]; then if [ $SYSTEM == "Darwin" ]; then
launchd_name="com.openmedialibrary.loginscript" launchd_name="com.openmedialibrary.loginscript"
launchd_plist="$HOME/Library/LaunchAgents/${launchd_name}.plist" launchd_plist="$HOME/Library/LaunchAgents/${launchd_name}.plist"
@ -64,15 +64,15 @@ function remove_loginscript {
rm "$launchd_plist" rm "$launchd_plist"
fi fi
fi fi
}
if [ "$1" == "start" ]; then
remove_loginscript
if [ $SYSTEM == "Linux" ]; then if [ $SYSTEM == "Linux" ]; then
if [ -e "$HOME/.config/autostart/openmedialibrary.desktop" ]; then if [ -e "$HOME/.config/autostart/openmedialibrary.desktop" ]; then
rm "$HOME/.config/autostart/openmedialibrary.desktop" rm "$HOME/.config/autostart/openmedialibrary.desktop"
fi fi
fi fi
}
if [ "$1" == "start" ]; then
remove_autostart
cd "$BASE/$NAME" cd "$BASE/$NAME"
if [ -e $PID ]; then if [ -e $PID ]; then
if ps -p `cat "$PID"` > /dev/null; then if ps -p `cat "$PID"` > /dev/null; then
@ -96,7 +96,7 @@ if [ "$1" == "debug" ]; then
exec python3 oml server debug $PID exec python3 oml server debug $PID
fi fi
if [ "$1" == "stop" ]; then if [ "$1" == "stop" ]; then
remove_loginscript remove_autostart
if [ -e $PID ]; then if [ -e $PID ]; then
_PID=`cat $PID` _PID=`cat $PID`
kill $_PID kill $_PID

View File

@ -114,10 +114,10 @@ class Install(Thread):
self.status['installing'] = 'setup' self.status['installing'] = 'setup'
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
os.system('./ctl install_launcher') os.system('./ctl install_launcher')
os.system('./ctl setup')
self.status['progress'] = 1
with open('config/release.json', 'w') as fd: with open('config/release.json', 'w') as fd:
json.dump(release, fd, indent=2) json.dump(release, fd, indent=2)
os.system('./ctl setup')
self.status['progress'] = 1
self.status['done'] = True self.status['done'] = True
def download(self, url, filename): def download(self, url, filename):

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
import os import os
import sys import sys
import signal import signal
@ -23,6 +22,7 @@ import setup
import state import state
import tasks import tasks
import websocket import websocket
import update
import logging import logging
@ -31,13 +31,15 @@ logger = logging.getLogger(__name__)
class MainHandler(OMLHandler): class MainHandler(OMLHandler):
def get(self, path): def get(self, path):
path = os.path.join(settings.static_path, 'html', 'oml.html')
with open(path) as fd:
content = fd.read()
version = settings.MINOR_VERSION.split('-')[0] version = settings.MINOR_VERSION.split('-')[0]
if version == 'git': if version == 'git':
version = int(time.mktime(time.gmtime())) version = int(time.mktime(time.gmtime()))
content = content.replace('oml.js?1', 'oml.js?%s' % version) path = os.path.join(settings.static_path, 'html', 'oml.html')
with open(path) as fd:
content = fd.read()
content = content.replace('.js?1', '.js?%s' % version)
if state.update:
content = content.replace('oml.js', 'oml.update.js')
self.set_header('Content-Type', 'text/html') self.set_header('Content-Type', 'text/html')
self.set_header('Content-Length', str(len(content))) self.set_header('Content-Length', str(len(content)))
self.write(content) self.write(content)
@ -133,11 +135,11 @@ def run():
with open(PID, 'w') as pid: with open(PID, 'w') as pid:
pid.write('%s' % os.getpid()) pid.write('%s' % os.getpid())
state.update = update.update_available()
state.PID = PID state.PID = PID
state.http_server = http_server state.http_server = http_server
state.main = IOLoop.instance() state.main = IOLoop.instance()
state.cache = Cache(ttl=60) state.cache = Cache(ttl=60)
state.tasks = tasks.Tasks()
def start_node(): def start_node():
import downloads import downloads
@ -156,7 +158,13 @@ def run():
else: else:
nodes.publish_node() nodes.publish_node()
state.main.add_callback(publish) state.main.add_callback(publish)
state.main.add_callback(start_node)
if not state.update:
state.tasks = tasks.Tasks()
state.main.add_callback(start_node)
else:
state.tasks = update.Update()
if ':' in settings.server['address']: if ':' in settings.server['address']:
host = '[%s]' % settings.server['address'] host = '[%s]' % settings.server['address']
elif not settings.server['address']: elif not settings.server['address']:

View File

@ -78,7 +78,8 @@ else:
NODE_PROTOCOL="0.4" NODE_PROTOCOL="0.4"
VERSION="%s.%s" % (NODE_PROTOCOL, MINOR_VERSION) VERSION="%s.%s" % (NODE_PROTOCOL, MINOR_VERSION)
USER_AGENT = 'OpenMediaLibrary/%s' % VERSION USER_AGENT = 'OpenMediaLibrary/%s' % VERSION
DEBUG_HTTP = server.get('debug_http', False) DEBUG_HTTP = server.get('debug_http', False)
DB_VERSION = 0

View File

@ -2,11 +2,13 @@ bandwidth = None
host = None host = None
main = None main = None
nodes = False nodes = False
node = False
online = False online = False
tasks = False tasks = False
scraping = False scraping = False
downloads = False downloads = False
tor = False tor = False
update = False
websockets = [] websockets = []
activity = {} activity = {}

View File

@ -6,7 +6,7 @@ from queue import Queue
from threading import Thread from threading import Thread
from websocket import trigger_event from websocket import trigger_event
import state
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -34,8 +34,6 @@ class Tasks(Thread):
item.scan.run_import(data) item.scan.run_import(data)
elif action == 'scan': elif action == 'scan':
item.scan.run_scan() item.scan.run_scan()
elif action == 'update':
trigger_event('error', {'error': 'not implemented'})
else: else:
trigger_event('error', {'error': 'unknown action'}) trigger_event('error', {'error': 'unknown action'})
except: except:

View File

@ -238,7 +238,7 @@ def install_tor():
if get_tor(): if get_tor():
print('found existing tor installation') print('found existing tor installation')
url = torbrowser_url() url = torbrowser_url()
target = os.path.join(settings.base_dir, 'tor') target = os.path.normpath(os.path.join(settings.base_dir, '..', 'tor'))
if url: if url:
print('downloading and installing tor') print('downloading and installing tor')
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):

View File

@ -6,10 +6,12 @@ from contextlib import closing
import json import json
import os import os
import tarfile import tarfile
from threading import Thread
import urllib.request, urllib.error, urllib.parse import urllib.request, urllib.error, urllib.parse
import shutil import shutil
import subprocess import subprocess
import sys import sys
import time
import ed25519 import ed25519
import ox import ox
@ -145,7 +147,6 @@ def install():
shutil.copy(os.path.join(settings.updates_path, 'release.json'), os.path.join(settings.config_path, 'release.json')) shutil.copy(os.path.join(settings.updates_path, 'release.json'), os.path.join(settings.config_path, 'release.json'))
for cmd in [ for cmd in [
['./ctl', 'stop'], ['./ctl', 'stop'],
['./ctl', 'setup'],
['./ctl', 'postupdate', '-o', old_version, '-n', new_version] ['./ctl', 'postupdate', '-o', old_version, '-n', new_version]
]: ]:
subprocess.call(cmd) subprocess.call(cmd)
@ -153,6 +154,26 @@ def install():
return True return True
return True return True
def update_available():
db_version = settings.server.get('db_version', 0)
if db_version < settings.DB_VERSION:
return True
if not os.path.exists(os.path.join(settings.updates_path, 'release.json')):
return False
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return False
with open(os.path.join(settings.updates_path, 'release.json')) as fd:
release = json.load(fd)
old_version = current_version('openmedialibrary')
new_version = release['modules']['openmedialibrary']['version']
return verify(release) and old_version < new_version
def restart_oml(update=False):
if update:
get_latest_release()
subprocess.Popen([os.path.join(settings.base_dir, 'ctl'), 'restart'],
close_fds=True, start_new_session=True)
def get_app_version(app): def get_app_version(app):
plist = app + '/Contents/Info.plist' plist = app + '/Contents/Info.plist'
if os.path.exists(plist): if os.path.exists(plist):
@ -214,9 +235,50 @@ def restart(data):
''' '''
restart (and upgrade if upgrades are available) restart (and upgrade if upgrades are available)
''' '''
if data.get('update'): restart_oml(data.get('update'))
download()
subprocess.Popen([os.path.join(settings.base_dir, 'ctl'), 'restart'],
close_fds=True, start_new_session=True)
return {} return {}
actions.register(restart, cache=False) actions.register(restart, cache=False)
class Update(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.start()
def status(self, status, reload=False):
from websocket import trigger_event
trigger_event('updatestatus', {
'reload': reload,
'status': status,
})
def install(self):
while update_available():
self.status('Downloading new version...')
while not download():
self.status('Download failed... (try again in 10 seconds)')
time.sleep(10)
self.status('Downloading new version...')
self.status('Installing new version...')
# install right now calls stop!
'''
if not install():
self.status('Installation failed...')
'''
restart_oml()
def update_database(self):
db_version = settings.server.get('db_version', 0)
if db_version < settings.DB_VERSION:
self.status('Migrating database...')
time.sleep(1)
settings.server['db_version'] = settings.DB_VERSION
def run(self):
self.status('Checking for Updates...')
self.update_database()
self.install()
self.status('Restarting...', True)
restart_oml()

178
static/js/oml.update.js Normal file
View File

@ -0,0 +1,178 @@
'use strict';
(function() {
var animationInterval,
enableDebugMode = getLocalStorage('oml.enableDebugMode'),
omlVersion = getOMLVersion(),
oxjsPath = '/static/oxjs/' + (enableDebugMode ? 'dev' : 'min'),
terminal,
theme = getLocalStorage('Ox.theme')
&& JSON.parse(localStorage['Ox.theme'])
|| 'oxlight';
loadImages(function(images) {
loadScreen(images);
loadOxJS(loadOML);
});
function getLocalStorage(key) {
try {
return localStorage[key];
} catch(e) {}
}
function getOMLVersion() {
var i, path, scripts = document.getElementsByTagName('script');
for (i = 0; i < scripts.length; i++) {
if(/oml.update.js/.test(scripts[i].src)) {
return scripts[i].src.replace(/.*\?/, '');
}
}
}
function loadImages(callback) {
var images = {};
images.logo = document.createElement('img');
images.logo.onload = function() {
images.logo.style.position = 'absolute';
images.logo.style.left = 0;
images.logo.style.top = 0;
images.logo.style.right = 0;
images.logo.style.bottom = '96px';
images.logo.style.width = '256px';
images.logo.style.height = '256px';
images.logo.style.margin = 'auto';
images.logo.style.MozUserSelect = 'none';
images.logo.style.MSUserSelect = 'none';
images.logo.style.OUserSelect = 'none';
images.logo.style.WebkitUserSelect = 'none';
images.loadingIcon = document.createElement('img');
images.loadingIcon.setAttribute('id', 'loadingIcon');
images.loadingIcon.style.position = 'absolute';
images.loadingIcon.style.left = '16px';
images.loadingIcon.style.top = '256px'
images.loadingIcon.style.right = 0;
images.loadingIcon.style.bottom = 0;
images.loadingIcon.style.width = '32px';
images.loadingIcon.style.height = '32px';
images.loadingIcon.style.margin = 'auto';
images.loadingIcon.style.MozUserSelect = 'none';
images.loadingIcon.style.MSUserSelect = 'none';
images.loadingIcon.style.OUserSelect = 'none';
images.loadingIcon.style.WebkitUserSelect = 'none';
images.loadingIcon.src = oxjsPath
+ '/UI/themes/' + theme + '/svg/symbolLoading.svg';
callback(images);
};
images.logo.src = '/static/png/oml.png';
}
function loadOML(browserSupported) {
window.oml = Ox.App({
name: 'oml',
socket: 'ws://' + document.location.host + '/ws',
url: '/api/'
}).bindEvent({
load: function(data) {
data.browserSupported = browserSupported;
oml.ui = {};
oml.ui.status = Ox.Element()
.css({
position: 'absolute',
left: 0,
right: 0,
bottom: '50px',
textAlign: 'center',
paddingLeft: '16px',
color: '#999999'
}).appendTo(Ox.$('#loadingScreen'));
oml.ui.status.html('Updating Open Media Library...');
},
updatestatus: function(data) {
oml.ui.status.html(data.status);
oml.reload = data.reload;
},
close: function(data) {
},
open: function(data) {
Ox.print('socket open');
if (oml.reload) {
document.location.href = document.location.protocol + '//' + document.location.host;
}
}
});
}
function loadOxJS(callback) {
var head = document.head
|| document.getElementsByTagName('head')[0]
|| document.documentElement,
script = document.createElement('script');
script.onload = function() {
Ox.load({UI: {theme: theme}}, function() {
Ox.formatUpper = function(string) {
return string.toUpperCase();
};
callback();
});
};
script.src = oxjsPath + '/Ox.js?' + omlVersion;
script.type = 'text/javascript';
head.appendChild(script);
}
function loadScreen(images) {
var loadingScreen = document.createElement('div');
loadingScreen.setAttribute('id', 'loadingScreen');
loadingScreen.className = 'OxScreen';
loadingScreen.style.position = 'absolute';
loadingScreen.style.width = '100%';
loadingScreen.style.height = '100%';
loadingScreen.style.backgroundColor = theme == 'oxlight' ? 'rgb(224, 224, 224)'
: theme == 'oxmedium' ? 'rgb(144, 144, 144)' : 'rgb(32, 32, 32)';
loadingScreen.style.zIndex = '1002';
loadingScreen.appendChild(images.logo);
loadingScreen.appendChild(images.loadingIcon);
// FF3.6 document.body can be undefined here
window.onload = function() {
document.body.style.margin = 0;
document.body.appendChild(loadingScreen);
startAnimation();
};
// IE8 does not call onload if already loaded before set
document.body && window.onload();
}
function removeScreen() {
var $loadingScreen = $('#loadingScreen');
$loadingScreen.animate({
opacity: 0
}, 1000, function() {
$loadingScreen.remove();
});
}
function startAnimation() {
var css, deg = 0, loadingIcon = document.getElementById('loadingIcon'),
previousTime = +new Date();
animationInterval = setInterval(function() {
var currentTime = +new Date(),
delta = (currentTime - previousTime) / 1000;
previousTime = currentTime;
deg = Math.round((deg + delta * 360) % 360 / 30) * 30;
css = 'rotate(' + deg + 'deg)';
loadingIcon.style.MozTransform = css;
loadingIcon.style.MSTransform = css;
loadingIcon.style.OTransform = css;
loadingIcon.style.WebkitTransform = css;
loadingIcon.style.transform = css;
}, 83);
}
function stopAnimation() {
clearInterval(animationInterval);
}
}());