Compare commits
24 commits
Author | SHA1 | Date | |
---|---|---|---|
dd8ea22d45 | |||
85eba0bf09 | |||
627a016515 | |||
ad2af2a257 | |||
3bc2b1bd3e | |||
5c6c7e37c7 | |||
7cfe645ab7 | |||
ff236e8828 | |||
34af2b1fab | |||
d83309d4cd | |||
59c2045ac6 | |||
a24d96b098 | |||
03daede441 | |||
9e6ecb5459 | |||
e7ede6ade0 | |||
f4bfe9294b | |||
c69ca372ee | |||
9e00dd09e3 | |||
7cc1504069 | |||
d5ace7aeca | |||
1b1442e664 | |||
18cbf0ec9c | |||
a8aa619217 | |||
ff1c929d4d |
26 changed files with 413 additions and 198 deletions
3
ctl
3
ctl
|
@ -30,9 +30,6 @@ if [ "$action" = "init" ]; then
|
||||||
$SUDO git clone -b $branch https://code.0x2620.org/0x2620/oxjs.git static/oxjs
|
$SUDO git clone -b $branch https://code.0x2620.org/0x2620/oxjs.git static/oxjs
|
||||||
fi
|
fi
|
||||||
$SUDO mkdir -p src
|
$SUDO mkdir -p src
|
||||||
if [ ! -d src/oxtimelines ]; then
|
|
||||||
$SUDO git clone -b $branch https://code.0x2620.org/0x2620/oxtimelines.git src/oxtimelines
|
|
||||||
fi
|
|
||||||
for package in oxtimelines python-ox; do
|
for package in oxtimelines python-ox; do
|
||||||
cd ${BASE}
|
cd ${BASE}
|
||||||
if [ ! -d src/${package} ]; then
|
if [ ! -d src/${package} ]; then
|
||||||
|
|
36
pandora/app/oidc.py
Normal file
36
pandora/app/oidc.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
import mozilla_django_oidc.auth
|
||||||
|
|
||||||
|
from user.utils import prepare_user
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class OIDCAuthenticationBackend(mozilla_django_oidc.auth.OIDCAuthenticationBackend):
|
||||||
|
def create_user(self, claims):
|
||||||
|
user = super(OIDCAuthenticationBackend, self).create_user(claims)
|
||||||
|
username = None
|
||||||
|
for key in ('preferred_username', 'name'):
|
||||||
|
if claims.get(key):
|
||||||
|
username = claims[key]
|
||||||
|
break
|
||||||
|
n = 1
|
||||||
|
if username and username != user.username:
|
||||||
|
uname = username
|
||||||
|
while User.objects.filter(username=uname).exclude(id=user.id).exists():
|
||||||
|
n += 1
|
||||||
|
uname = '%s (%s)' % (username, n)
|
||||||
|
user.username = uname
|
||||||
|
user.save()
|
||||||
|
prepare_user(user)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def update_user(self, user, claims):
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def generate_username(email):
|
||||||
|
return unicodedata.normalize('NFKC', email)[:150]
|
|
@ -184,6 +184,7 @@ def init(request, data):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
config['site']['oidc'] = bool(getattr(settings, 'OIDC_RP_CLIENT_ID', False))
|
||||||
response['data']['site'] = config
|
response['data']['site'] = config
|
||||||
response['data']['user'] = init_user(request.user, request)
|
response['data']['user'] = init_user(request.user, request)
|
||||||
request.session['last_init'] = str(datetime.now())
|
request.session['last_init'] = str(datetime.now())
|
||||||
|
|
|
@ -40,8 +40,12 @@ info_key_map = {
|
||||||
'display_id': 'id',
|
'display_id': 'id',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
YT_DLP = ['yt-dlp']
|
||||||
|
if settings.YT_DLP_EXTRA:
|
||||||
|
YT_DLP += settings.YT_DLP_EXTRA
|
||||||
|
|
||||||
def get_info(url, referer=None):
|
def get_info(url, referer=None):
|
||||||
cmd = ['yt-dlp', '-j', '--all-subs', url]
|
cmd = YT_DLP + ['-j', '--all-subs', url]
|
||||||
if referer:
|
if referer:
|
||||||
cmd += ['--referer', referer]
|
cmd += ['--referer', referer]
|
||||||
p = subprocess.Popen(cmd,
|
p = subprocess.Popen(cmd,
|
||||||
|
@ -93,7 +97,7 @@ def add_subtitles(item, media, tmp):
|
||||||
sub.save()
|
sub.save()
|
||||||
|
|
||||||
def load_formats(url):
|
def load_formats(url):
|
||||||
cmd = ['yt-dlp', '-q', url, '-j', '-F']
|
cmd = YT_DLP + ['-q', url, '-j', '-F']
|
||||||
p = subprocess.Popen(cmd,
|
p = subprocess.Popen(cmd,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE, close_fds=True)
|
stderr=subprocess.PIPE, close_fds=True)
|
||||||
|
@ -112,7 +116,7 @@ def download(item_id, url, referer=None):
|
||||||
if isinstance(tmp, bytes):
|
if isinstance(tmp, bytes):
|
||||||
tmp = tmp.decode('utf-8')
|
tmp = tmp.decode('utf-8')
|
||||||
os.chdir(tmp)
|
os.chdir(tmp)
|
||||||
cmd = ['yt-dlp', '-q', media['url']]
|
cmd = YT_DLP + ['-q', media['url']]
|
||||||
if referer:
|
if referer:
|
||||||
cmd += ['--referer', referer]
|
cmd += ['--referer', referer]
|
||||||
elif 'referer' in media:
|
elif 'referer' in media:
|
||||||
|
|
|
@ -34,6 +34,13 @@ def api(request):
|
||||||
return response
|
return response
|
||||||
if request.META.get('CONTENT_TYPE') == 'application/json':
|
if request.META.get('CONTENT_TYPE') == 'application/json':
|
||||||
r = json.loads(request.body.decode('utf-8'))
|
r = json.loads(request.body.decode('utf-8'))
|
||||||
|
if 'action' not in r:
|
||||||
|
logger.error("invalid api request: %s", r)
|
||||||
|
response = render_to_json_response(json_response(status=400,
|
||||||
|
text='Invalid request'))
|
||||||
|
response['Access-Control-Allow-Origin'] = '*'
|
||||||
|
return response
|
||||||
|
else:
|
||||||
action = r['action']
|
action = r['action']
|
||||||
data = r.get('data', {})
|
data = r.get('data', {})
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -111,6 +111,7 @@ ROOT_URLCONF = 'urls'
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
|
'mozilla_django_oidc',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
|
@ -158,6 +159,27 @@ INSTALLED_APPS = (
|
||||||
)
|
)
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'system.User'
|
AUTH_USER_MODEL = 'system.User'
|
||||||
|
AUTH_PROFILE_MODULE = 'user.UserProfile'
|
||||||
|
AUTH_CHECK_USERNAME = True
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
)
|
||||||
|
|
||||||
|
# OpenID Connect login support
|
||||||
|
LOGIN_REDIRECT_URL = "/grid"
|
||||||
|
LOGOUT_REDIRECT_URL = "/grid"
|
||||||
|
OIDC_USERNAME_ALGO = "app.oidc.generate_username"
|
||||||
|
OIDC_RP_CLIENT_ID = None
|
||||||
|
|
||||||
|
# define those in local_settings to enable OCID based login
|
||||||
|
#OIDC_RP_CLIENT_ID = '<client id>'
|
||||||
|
#OIDC_RP_CLIENT_SECRET = '<client secret>'
|
||||||
|
#OIDC_RP_SIGN_ALGO = "RS256"
|
||||||
|
#OIDC_OP_JWKS_ENDPOINT = "<jwks endpoint>"
|
||||||
|
#OIDC_OP_AUTHORIZATION_ENDPOINT = "<authorization endpoint>"
|
||||||
|
#OIDC_OP_TOKEN_ENDPOINT = "<token endpoint>"
|
||||||
|
#OIDC_OP_USER_ENDPOINT = "<user endpoint>"
|
||||||
|
|
||||||
# Log errors into db
|
# Log errors into db
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
|
@ -193,8 +215,6 @@ CACHES = {
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
|
||||||
AUTH_PROFILE_MODULE = 'user.UserProfile'
|
|
||||||
AUTH_CHECK_USERNAME = True
|
|
||||||
FFMPEG = 'ffmpeg'
|
FFMPEG = 'ffmpeg'
|
||||||
FFPROBE = 'ffprobe'
|
FFPROBE = 'ffprobe'
|
||||||
USE_VP9 = True
|
USE_VP9 = True
|
||||||
|
@ -291,6 +311,8 @@ DATA_UPLOAD_MAX_MEMORY_SIZE = 32 * 1024 * 1024
|
||||||
|
|
||||||
EMPTY_CLIPS = True
|
EMPTY_CLIPS = True
|
||||||
|
|
||||||
|
YT_DLP_EXTRA = []
|
||||||
|
|
||||||
#you can ignore things below this line
|
#you can ignore things below this line
|
||||||
#=========================================================================
|
#=========================================================================
|
||||||
LOCAL_APPS = []
|
LOCAL_APPS = []
|
||||||
|
@ -321,3 +343,7 @@ except NameError:
|
||||||
|
|
||||||
INSTALLED_APPS = tuple(list(INSTALLED_APPS) + LOCAL_APPS)
|
INSTALLED_APPS = tuple(list(INSTALLED_APPS) + LOCAL_APPS)
|
||||||
|
|
||||||
|
if OIDC_RP_CLIENT_ID:
|
||||||
|
AUTHENTICATION_BACKENDS = list(AUTHENTICATION_BACKENDS) + [
|
||||||
|
'app.oidc.OIDCAuthenticationBackend'
|
||||||
|
]
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<meta property="og:site_name" content="{{ settings.SITENAME }}"/>
|
<meta property="og:site_name" content="{{ settings.SITENAME }}"/>
|
||||||
{% compress css file m %}
|
{% compress css file m %}
|
||||||
<link rel="stylesheet" href="{% static 'mobile/css/reset.css' %}"></link>
|
<link rel="stylesheet" href="{% static 'mobile/css/reset.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'mobile/css/style.css' %}"></link>
|
<link rel="stylesheet" href="{% static 'mobile/css/style.css' %}">
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
<meta name="google" value="notranslate"/>
|
<meta name="google" value="notranslate"/>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from django.urls import path, re_path
|
from django.urls import path, re_path, include
|
||||||
from oxdjango.http import HttpFileResponse
|
from oxdjango.http import HttpFileResponse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -33,9 +33,15 @@ import urlalias.views
|
||||||
def serve_static_file(path, location, content_type):
|
def serve_static_file(path, location, content_type):
|
||||||
return HttpFileResponse(location, content_type=content_type)
|
return HttpFileResponse(location, content_type=content_type)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
#path('admin/', admin.site.urls),
|
#path('admin/', admin.site.urls),
|
||||||
|
]
|
||||||
|
if settings.OIDC_RP_CLIENT_ID:
|
||||||
|
urlpatterns += [
|
||||||
|
path('oidc/', include('mozilla_django_oidc.urls')),
|
||||||
|
]
|
||||||
|
urlpatterns += [
|
||||||
re_path(r'^api/locale.(?P<lang>.*).json$', translation.views.locale_json),
|
re_path(r'^api/locale.(?P<lang>.*).json$', translation.views.locale_json),
|
||||||
re_path(r'^api/upload/text/?$', text.views.upload),
|
re_path(r'^api/upload/text/?$', text.views.upload),
|
||||||
re_path(r'^api/upload/document/?$', document.views.upload),
|
re_path(r'^api/upload/document/?$', document.views.upload),
|
||||||
|
|
|
@ -3,6 +3,38 @@ from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_user(user):
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.conf import settings
|
||||||
|
from itemlist.models import List, Position
|
||||||
|
from django.db.models import Max
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
first_user_qs = User.objects.all()
|
||||||
|
if user.id:
|
||||||
|
first_user_qs = first_user_qs.exclude(id=user.id)
|
||||||
|
if first_user_qs.count() == 0:
|
||||||
|
user.is_superuser = True
|
||||||
|
user.is_staff = True
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
for l in settings.CONFIG['personalLists']:
|
||||||
|
list = List(name=l['title'], user=user)
|
||||||
|
for key in ('query', 'public', 'featured'):
|
||||||
|
if key in l:
|
||||||
|
setattr(list, key, l[key])
|
||||||
|
if key == 'query':
|
||||||
|
for c in list.query['conditions']:
|
||||||
|
if c['key'] == 'user':
|
||||||
|
c['value'] = c['value'].format(username=user.username)
|
||||||
|
list.save()
|
||||||
|
pos = Position(list=list, section='personal', user=user)
|
||||||
|
qs = Position.objects.filter(user=user, section='personal')
|
||||||
|
pos.position = (qs.aggregate(Max('position'))['position__max'] or 0) + 1
|
||||||
|
pos.save()
|
||||||
|
|
||||||
|
|
||||||
def get_ip(request):
|
def get_ip(request):
|
||||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||||
ip = request.META['HTTP_X_FORWARDED_FOR'].split(',')[0]
|
ip = request.META['HTTP_X_FORWARDED_FOR'].split(',')[0]
|
||||||
|
|
|
@ -28,7 +28,7 @@ from user.models import Group
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from .decorators import capability_required_json
|
from .decorators import capability_required_json
|
||||||
from .utils import rename_user
|
from .utils import rename_user, prepare_user
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -177,28 +177,10 @@ def signup(request, data):
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
first_user = User.objects.count() == 0
|
|
||||||
user = User(username=data['username'], email=data['email'])
|
user = User(username=data['username'], email=data['email'])
|
||||||
user.set_password(data['password'])
|
user.set_password(data['password'])
|
||||||
#make first user admin
|
|
||||||
user.is_superuser = first_user
|
|
||||||
user.is_staff = first_user
|
|
||||||
user.save()
|
user.save()
|
||||||
#create default user lists:
|
prepare_user(user)
|
||||||
for l in settings.CONFIG['personalLists']:
|
|
||||||
list = List(name=l['title'], user=user)
|
|
||||||
for key in ('query', 'public', 'featured'):
|
|
||||||
if key in l:
|
|
||||||
setattr(list, key, l[key])
|
|
||||||
if key == 'query':
|
|
||||||
for c in list.query['conditions']:
|
|
||||||
if c['key'] == 'user':
|
|
||||||
c['value'] = c['value'].format(username=user.username)
|
|
||||||
list.save()
|
|
||||||
pos = Position(list=list, section='personal', user=user)
|
|
||||||
qs = Position.objects.filter(user=user, section='personal')
|
|
||||||
pos.position = (qs.aggregate(Max('position'))['position__max'] or 0) + 1
|
|
||||||
pos.save()
|
|
||||||
if request.session.session_key:
|
if request.session.session_key:
|
||||||
models.SessionData.objects.filter(session_key=request.session.session_key).update(user=user)
|
models.SessionData.objects.filter(session_key=request.session.session_key).update(user=user)
|
||||||
ui = json.loads(request.session.get('ui', 'null'))
|
ui = json.loads(request.session.get('ui', 'null'))
|
||||||
|
|
|
@ -20,3 +20,4 @@ future
|
||||||
pytz
|
pytz
|
||||||
pypdfium2
|
pypdfium2
|
||||||
Pillow>=10
|
Pillow>=10
|
||||||
|
mozilla-django-oidc==4.0.1
|
||||||
|
|
|
@ -106,6 +106,13 @@ pandora.ui.addFilesDialog = function(options) {
|
||||||
});
|
});
|
||||||
|
|
||||||
var selectItems = [];
|
var selectItems = [];
|
||||||
|
selectItems.push({
|
||||||
|
id: 'one',
|
||||||
|
title: Ox._(
|
||||||
|
options.items.length > 1 ? 'Create new {0} with multiple parts' : 'Create new {0}',
|
||||||
|
[pandora.site.itemName.singular.toLowerCase()]
|
||||||
|
)
|
||||||
|
});
|
||||||
if (pandora.user.ui.item && options.editable) {
|
if (pandora.user.ui.item && options.editable) {
|
||||||
selectItems.push({
|
selectItems.push({
|
||||||
id: 'add',
|
id: 'add',
|
||||||
|
@ -124,13 +131,6 @@ pandora.ui.addFilesDialog = function(options) {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
selectItems.push({
|
|
||||||
id: 'one',
|
|
||||||
title: Ox._(
|
|
||||||
options.items.length > 1 ? 'Create new {0} with multiple parts' : 'Create new {0}',
|
|
||||||
[pandora.site.itemName.singular.toLowerCase()]
|
|
||||||
)
|
|
||||||
});
|
|
||||||
var $select = Ox.Select({
|
var $select = Ox.Select({
|
||||||
items: selectItems,
|
items: selectItems,
|
||||||
width: 256
|
width: 256
|
||||||
|
|
|
@ -100,6 +100,10 @@ pandora.ui.appPanel = function() {
|
||||||
pandora.$ui.siteDialog = pandora.ui.siteDialog(page).open();
|
pandora.$ui.siteDialog = pandora.ui.siteDialog(page).open();
|
||||||
}
|
}
|
||||||
} else if (['signup', 'signin'].indexOf(page) > -1) {
|
} else if (['signup', 'signin'].indexOf(page) > -1) {
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
pandora.oidcLogin()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (pandora.user.level == 'guest') {
|
if (pandora.user.level == 'guest') {
|
||||||
if (pandora.$ui.accountDialog && pandora.$ui.accountDialog.is(':visible')) {
|
if (pandora.$ui.accountDialog && pandora.$ui.accountDialog.is(':visible')) {
|
||||||
pandora.$ui.accountDialog.options(pandora.ui.accountDialogOptions(page));
|
pandora.$ui.accountDialog.options(pandora.ui.accountDialogOptions(page));
|
||||||
|
|
|
@ -424,18 +424,26 @@ pandora.ui.folders = function(section) {
|
||||||
}).bindEvent({
|
}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
var $dialog = pandora.ui.iconDialog({
|
var $dialog = pandora.ui.iconDialog({
|
||||||
buttons: title != Ox._('Featured ' + folderItems) ? [
|
buttons: title != Ox._('Featured ' + folderItems) ? [].concat(
|
||||||
|
pandora.site.site.oidc ? []
|
||||||
|
: [
|
||||||
Ox.Button({title: Ox._('Sign Up...')}).bindEvent({
|
Ox.Button({title: Ox._('Sign Up...')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
pandora.$ui.accountDialog = pandora.ui.accountDialog('signup').open();
|
pandora.$ui.accountDialog = pandora.ui.accountDialog('signup').open();
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
],
|
||||||
|
[
|
||||||
Ox.Button({title: Ox._('Sign In...')}).bindEvent({
|
Ox.Button({title: Ox._('Sign In...')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
pandora.oidcLogin()
|
||||||
|
} else {
|
||||||
pandora.$ui.accountDialog = pandora.ui.accountDialog('signin').open();
|
pandora.$ui.accountDialog = pandora.ui.accountDialog('signin').open();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
{},
|
{},
|
||||||
Ox.Button({title: Ox._('Not Now')}).bindEvent({
|
Ox.Button({title: Ox._('Not Now')}).bindEvent({
|
||||||
|
@ -443,7 +451,8 @@ pandora.ui.folders = function(section) {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
] : [
|
]
|
||||||
|
): [
|
||||||
Ox.Button({title: Ox._('Close')}).bindEvent({
|
Ox.Button({title: Ox._('Close')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
|
|
|
@ -137,6 +137,7 @@ pandora.ui.helpDialog = function() {
|
||||||
|
|
||||||
that.select = function(id) {
|
that.select = function(id) {
|
||||||
var img, $img;
|
var img, $img;
|
||||||
|
if ($text) {
|
||||||
$text.html(text[id || 'help']).scrollTop(0);
|
$text.html(text[id || 'help']).scrollTop(0);
|
||||||
img = $text.find('img');
|
img = $text.find('img');
|
||||||
if (img) {
|
if (img) {
|
||||||
|
@ -155,6 +156,7 @@ pandora.ui.helpDialog = function() {
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
whiteSpace: 'nowrap'
|
whiteSpace: 'nowrap'
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,13 +171,13 @@ pandora.ui.home = function() {
|
||||||
}),
|
}),
|
||||||
$signinButton = Ox.Button({
|
$signinButton = Ox.Button({
|
||||||
title: Ox._('Sign In'),
|
title: Ox._('Sign In'),
|
||||||
width: 74
|
width: pandora.site.site.oidc ? 156 : 74
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 0,
|
left: 0,
|
||||||
top: '112px',
|
top: '112px',
|
||||||
right: '82px',
|
right: pandora.site.site.oidc ? '164px' : '82px',
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
@ -248,7 +248,13 @@ pandora.ui.home = function() {
|
||||||
adjustRatio();
|
adjustRatio();
|
||||||
|
|
||||||
if (pandora.user.level == 'guest') {
|
if (pandora.user.level == 'guest') {
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
$signinButton.options({
|
||||||
|
width: 156
|
||||||
|
})
|
||||||
|
} else {
|
||||||
$signupButton.appendTo(that);
|
$signupButton.appendTo(that);
|
||||||
|
}
|
||||||
$signinButton.appendTo(that);
|
$signinButton.appendTo(that);
|
||||||
} else {
|
} else {
|
||||||
$preferencesButton.appendTo(that);
|
$preferencesButton.appendTo(that);
|
||||||
|
|
|
@ -270,7 +270,7 @@ pandora.ui.list = function() {
|
||||||
item: function(data, sort, size) {
|
item: function(data, sort, size) {
|
||||||
size = 128;
|
size = 128;
|
||||||
var clipsQuery = pandora.getClipsQuery(),
|
var clipsQuery = pandora.getClipsQuery(),
|
||||||
isClipsQuery = !!clipsQuery.conditions.length,
|
isClipsQuery = clipsQuery.conditions.length > 1,
|
||||||
ratio = ui.icons == 'posters'
|
ratio = ui.icons == 'posters'
|
||||||
? (ui.showSitePosters ? pandora.site.posters.ratio : data.posterRatio) : 1,
|
? (ui.showSitePosters ? pandora.site.posters.ratio : data.posterRatio) : 1,
|
||||||
url = pandora.getMediaURL('/' + data.id + '/' + (
|
url = pandora.getMediaURL('/' + data.id + '/' + (
|
||||||
|
@ -352,7 +352,7 @@ pandora.ui.list = function() {
|
||||||
},
|
},
|
||||||
items: function(data, callback) {
|
items: function(data, callback) {
|
||||||
var clipsQuery = pandora.getClipsQuery(),
|
var clipsQuery = pandora.getClipsQuery(),
|
||||||
isClipsQuery = !!clipsQuery.conditions.length;
|
isClipsQuery = clipsQuery.conditions.length > 1;
|
||||||
pandora.api.find(Ox.extend(data, Ox.extend({
|
pandora.api.find(Ox.extend(data, Ox.extend({
|
||||||
query: ui.find
|
query: ui.find
|
||||||
}, isClipsQuery ? {clips: {
|
}, isClipsQuery ? {clips: {
|
||||||
|
|
|
@ -46,7 +46,9 @@ pandora.ui.mainMenu = function() {
|
||||||
{ id: 'tasks', title: Ox._('Tasks...'), disabled: isGuest },
|
{ id: 'tasks', title: Ox._('Tasks...'), disabled: isGuest },
|
||||||
{ id: 'archives', title: Ox._('Archives...'), disabled: /*isGuest*/ true },
|
{ id: 'archives', title: Ox._('Archives...'), disabled: /*isGuest*/ true },
|
||||||
{},
|
{},
|
||||||
{ id: 'signup', title: Ox._('Sign Up...'), disabled: !isGuest },
|
!pandora.site.site.oidc
|
||||||
|
? { id: 'signup', title: Ox._('Sign Up...'), disabled: !isGuest }
|
||||||
|
: [],
|
||||||
isGuest ? { id: 'signin', title: Ox._('Sign In...')}
|
isGuest ? { id: 'signin', title: Ox._('Sign In...')}
|
||||||
: { id: 'signout', title: Ox._('Sign Out...')}
|
: { id: 'signout', title: Ox._('Sign Out...')}
|
||||||
] },
|
] },
|
||||||
|
|
|
@ -2650,6 +2650,11 @@ pandora.logEvent = function(data, event, element) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pandora.oidcLogin = function() {
|
||||||
|
Ox.LoadingScreen().css({zIndex: 100}).addClass('OxScreen').appendTo(document.body).show().start()
|
||||||
|
document.location.href = '/oidc/authenticate/';
|
||||||
|
};
|
||||||
|
|
||||||
pandora.openLicenseDialog = function() {
|
pandora.openLicenseDialog = function() {
|
||||||
if (!Ox.Focus.focusedElementIsInput() && !pandora.hasDialogOrScreen()) {
|
if (!Ox.Focus.focusedElementIsInput() && !pandora.hasDialogOrScreen()) {
|
||||||
pandora.ui.licenseDialog().open().bindEvent({
|
pandora.ui.licenseDialog().open().bindEvent({
|
||||||
|
|
|
@ -201,3 +201,75 @@ ol li {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.seekbar {
|
||||||
|
padding: 12px 22px;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.fullscreen .seekbar {
|
||||||
|
padding: 28px 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar-progress {
|
||||||
|
height: 10px;
|
||||||
|
border: solid 1px #B1B1B1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar-progress [role="progressbar"] {
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
background-color: #B1B1B180;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar-progress [role="progressbar"]:after {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
right: -10px;
|
||||||
|
background-color: #B1B1B1;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar input[type="range"] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
background: transparent;
|
||||||
|
outline: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar input[type="range"]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: block;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar input[type="range"]::-moz-range-thumb {
|
||||||
|
display: block;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar input[type="range"]::-moz-range-track {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seekbar input[type="range"]::-moz-focus-outer {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -153,9 +153,11 @@ window.VideoPlayer = function(options) {
|
||||||
${icon.mute}
|
${icon.mute}
|
||||||
</div>
|
</div>
|
||||||
<div class="position">
|
<div class="position">
|
||||||
<div class="bar">
|
<div class="seekbar">
|
||||||
<div class="progress"></div>
|
<input type="range" value="0" min='0' max='100' step='.25' />
|
||||||
|
<div class="seekbar-progress">
|
||||||
|
<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="38" style="width: 0%;"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="time">
|
<div class="time">
|
||||||
|
@ -223,15 +225,31 @@ window.VideoPlayer = function(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var showControls
|
var showControls
|
||||||
|
function hideControlsLater() {
|
||||||
|
if (showControls) {
|
||||||
|
clearTimeout(showControls)
|
||||||
|
}
|
||||||
|
showControls = setTimeout(() => {
|
||||||
|
if (touching) {
|
||||||
|
hideControlsLater()
|
||||||
|
} else {
|
||||||
|
self.controls.style.opacity = that.paused ? '1' : '0'
|
||||||
|
showControls = null
|
||||||
|
}
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
var toggleControls = event => {
|
var toggleControls = event => {
|
||||||
|
if (event.target.tagName == "INPUT") {
|
||||||
|
if (showControls) {
|
||||||
|
clearTimeout(showControls)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (self.controls.style.opacity == '0') {
|
if (self.controls.style.opacity == '0') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
self.controls.style.opacity = '1'
|
self.controls.style.opacity = '1'
|
||||||
showControls = setTimeout(() => {
|
hideControlsLater()
|
||||||
self.controls.style.opacity = that.paused ? '1' : '0'
|
|
||||||
showControls = null
|
|
||||||
}, 3000)
|
|
||||||
} else {
|
} else {
|
||||||
self.controls.style.opacity = '0'
|
self.controls.style.opacity = '0'
|
||||||
}
|
}
|
||||||
|
@ -241,10 +259,7 @@ window.VideoPlayer = function(options) {
|
||||||
clearTimeout(showControls)
|
clearTimeout(showControls)
|
||||||
}
|
}
|
||||||
self.controls.style.opacity = '1'
|
self.controls.style.opacity = '1'
|
||||||
showControls = setTimeout(() => {
|
hideControlsLater()
|
||||||
self.controls.style.opacity = that.paused ? '1' : '0'
|
|
||||||
showControls = null
|
|
||||||
}, 3000)
|
|
||||||
})
|
})
|
||||||
self.controls.addEventListener("mouseleave", event => {
|
self.controls.addEventListener("mouseleave", event => {
|
||||||
if (showControls) {
|
if (showControls) {
|
||||||
|
@ -253,7 +268,13 @@ window.VideoPlayer = function(options) {
|
||||||
self.controls.style.opacity = that.paused ? '1' : '0'
|
self.controls.style.opacity = that.paused ? '1' : '0'
|
||||||
showControls = null
|
showControls = null
|
||||||
})
|
})
|
||||||
|
self.controls.addEventListener("touchstart", event => {
|
||||||
|
touching = true
|
||||||
|
})
|
||||||
self.controls.addEventListener("touchstart", toggleControls)
|
self.controls.addEventListener("touchstart", toggleControls)
|
||||||
|
self.controls.addEventListener("touchend", event => {
|
||||||
|
touching = false
|
||||||
|
})
|
||||||
self.controls.querySelector('.toggle').addEventListener("click", toggleVideo)
|
self.controls.querySelector('.toggle').addEventListener("click", toggleVideo)
|
||||||
self.controls.querySelector('.volume').addEventListener("click", toggleSound)
|
self.controls.querySelector('.volume').addEventListener("click", toggleSound)
|
||||||
self.controls.querySelector('.fullscreen-btn').addEventListener("click", toggleFullscreen)
|
self.controls.querySelector('.fullscreen-btn').addEventListener("click", toggleFullscreen)
|
||||||
|
@ -310,6 +331,7 @@ window.VideoPlayer = function(options) {
|
||||||
that.append(unblock)
|
that.append(unblock)
|
||||||
})
|
})
|
||||||
var loading = true
|
var loading = true
|
||||||
|
var touching = false
|
||||||
that.brightness(0)
|
that.brightness(0)
|
||||||
that.addEventListener("loadedmetadata", event => {
|
that.addEventListener("loadedmetadata", event => {
|
||||||
//
|
//
|
||||||
|
@ -331,42 +353,40 @@ window.VideoPlayer = function(options) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var time = that.querySelector('.controls .time div'),
|
var time = that.querySelector('.controls .time div');
|
||||||
progress = that.querySelector('.controls .position .progress')
|
const progressbar = that.querySelector('.seekbar div[role="progressbar"]');
|
||||||
that.querySelector('.controls .position').addEventListener("click", event => {
|
function setProgressPosition(value) {
|
||||||
var bar = event.target
|
progressbar.style.width = value + '%';
|
||||||
while (bar && !bar.classList.contains('bar')) {
|
progressbar.setAttribute('aria-valuenow', value);
|
||||||
bar = bar.parentElement
|
|
||||||
}
|
}
|
||||||
if (bar && bar.classList.contains('bar')) {
|
that.querySelector('.controls .position input').addEventListener('input', event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
var rect = bar.getBoundingClientRect()
|
setProgressPosition(event.target.value)
|
||||||
var x = event.clientX - rect.x
|
var position = event.target.value/100 * self.options.duration
|
||||||
var percent = x / rect.width
|
displayTime(position)
|
||||||
var position = percent * self.options.duration
|
|
||||||
if (self.options.position) {
|
|
||||||
position += self.options.position
|
|
||||||
}
|
|
||||||
progress.style.width = (100 * percent) + '%'
|
|
||||||
that.currentTime(position)
|
that.currentTime(position)
|
||||||
}
|
hideControlsLater()
|
||||||
})
|
})
|
||||||
that.addEventListener("timeupdate", event => {
|
function displayTime(currentTime) {
|
||||||
var currentTime = that.currentTime(),
|
duration = formatDuration(self.options.duration)
|
||||||
duration = self.options.duration
|
|
||||||
if (self.options.position) {
|
|
||||||
currentTime -= self.options.position
|
|
||||||
}
|
|
||||||
progress.style.width = (100 * currentTime / duration) + '%'
|
|
||||||
duration = formatDuration(duration)
|
|
||||||
currentTime = formatDuration(currentTime)
|
currentTime = formatDuration(currentTime)
|
||||||
while (duration && duration.startsWith('00:')) {
|
while (duration && duration.startsWith('00:')) {
|
||||||
duration = duration.slice(3)
|
duration = duration.slice(3)
|
||||||
}
|
}
|
||||||
currentTime = currentTime.slice(currentTime.length - duration.length)
|
currentTime = currentTime.slice(currentTime.length - duration.length)
|
||||||
time.innerText = `${currentTime} / ${duration}`
|
time.innerText = `${currentTime} / ${duration}`
|
||||||
|
}
|
||||||
|
|
||||||
|
that.addEventListener("timeupdate", event => {
|
||||||
|
var currentTime = that.currentTime(),
|
||||||
|
duration = self.options.duration
|
||||||
|
if (self.options.position) {
|
||||||
|
currentTime -= self.options.position
|
||||||
|
}
|
||||||
|
setProgressPosition(100 * currentTime / duration)
|
||||||
|
displayTime(currentTime)
|
||||||
})
|
})
|
||||||
|
|
||||||
that.addEventListener("play", event => {
|
that.addEventListener("play", event => {
|
||||||
|
|
|
@ -1,35 +1,4 @@
|
||||||
|
|
||||||
const getSortValue = function(value) {
|
|
||||||
var sortValue = value;
|
|
||||||
function trim(value) {
|
|
||||||
return value.replace(/^\W+(?=\w)/, '');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
isEmpty(value)
|
|
||||||
|| isNull(value)
|
|
||||||
|| isUndefined(value)
|
|
||||||
) {
|
|
||||||
sortValue = null;
|
|
||||||
} else if (isString(value)) {
|
|
||||||
// make lowercase and remove leading non-word characters
|
|
||||||
sortValue = trim(value.toLowerCase());
|
|
||||||
// move leading articles to the end
|
|
||||||
// and remove leading non-word characters
|
|
||||||
['a', 'an', 'the'].forEach(function(article) {
|
|
||||||
if (new RegExp('^' + article + ' ').test(sortValue)) {
|
|
||||||
sortValue = trim(sortValue.slice(article.length + 1))
|
|
||||||
+ ', ' + sortValue.slice(0, article.length);
|
|
||||||
return false; // break
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// remove thousand separators and pad numbers
|
|
||||||
sortValue = sortValue.replace(/(\d),(?=(\d{3}))/g, '$1')
|
|
||||||
.replace(/\d+/g, function(match) {
|
|
||||||
return match.padStart(64, '0')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return sortValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
const sortByKey = function(array, by) {
|
const sortByKey = function(array, by) {
|
||||||
return array.sort(function(a, b) {
|
return array.sort(function(a, b) {
|
||||||
|
|
|
@ -129,6 +129,10 @@ async function loadData(id, args) {
|
||||||
<span class="icon">${icon.down}</span>
|
<span class="icon">${icon.down}</span>
|
||||||
${layerData.title}
|
${layerData.title}
|
||||||
</h3>`)
|
</h3>`)
|
||||||
|
data.layers[layer] = sortBy(data.layers[layer], [
|
||||||
|
{key: "in", operator: "+"},
|
||||||
|
{key: "created", operator: "+"}
|
||||||
|
])
|
||||||
data.layers[layer].forEach(annotation => {
|
data.layers[layer].forEach(annotation => {
|
||||||
if (pandora.url) {
|
if (pandora.url) {
|
||||||
annotation.value = annotation.value.replace(
|
annotation.value = annotation.value.replace(
|
||||||
|
|
|
@ -125,7 +125,10 @@ const clickLink = function(event) {
|
||||||
}
|
}
|
||||||
document.location.hash = '#' + link.slice(1)
|
document.location.hash = '#' + link.slice(1)
|
||||||
} else {
|
} else {
|
||||||
if (!link.startsWith('/m')) {
|
if (link.includes('/download/')) {
|
||||||
|
document.location.href = link
|
||||||
|
return
|
||||||
|
} else if (!link.startsWith('/m')) {
|
||||||
link = '/m' + link
|
link = '/m' + link
|
||||||
}
|
}
|
||||||
history.pushState({}, '', link);
|
history.pushState({}, '', link);
|
||||||
|
@ -161,3 +164,59 @@ const getVideoURL = function(id, resolution, part, track, streamId) {
|
||||||
return prefix + '/' + getVideoURLName(id, resolution, part, track, streamId);
|
return prefix + '/' + getVideoURLName(id, resolution, part, track, streamId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSortValue = function(value) {
|
||||||
|
var sortValue = value;
|
||||||
|
function trim(value) {
|
||||||
|
return value.replace(/^\W+(?=\w)/, '');
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
isEmpty(value)
|
||||||
|
|| isNull(value)
|
||||||
|
|| isUndefined(value)
|
||||||
|
) {
|
||||||
|
sortValue = null;
|
||||||
|
} else if (isString(value)) {
|
||||||
|
// make lowercase and remove leading non-word characters
|
||||||
|
sortValue = trim(value.toLowerCase());
|
||||||
|
// move leading articles to the end
|
||||||
|
// and remove leading non-word characters
|
||||||
|
['a', 'an', 'the'].forEach(function(article) {
|
||||||
|
if (new RegExp('^' + article + ' ').test(sortValue)) {
|
||||||
|
sortValue = trim(sortValue.slice(article.length + 1))
|
||||||
|
+ ', ' + sortValue.slice(0, article.length);
|
||||||
|
return false; // break
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// remove thousand separators and pad numbers
|
||||||
|
sortValue = sortValue.replace(/(\d),(?=(\d{3}))/g, '$1')
|
||||||
|
.replace(/\d+/g, function(match) {
|
||||||
|
return match.padStart(64, '0')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return sortValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
function sortBy(array, by, map) {
|
||||||
|
return array.sort(function(a, b) {
|
||||||
|
var aValue, bValue, index = 0, key, ret = 0;
|
||||||
|
while (ret == 0 && index < by.length) {
|
||||||
|
key = by[index].key;
|
||||||
|
aValue = getSortValue(
|
||||||
|
map && map[key] ? map[key](a[key], a) : a[key]
|
||||||
|
);
|
||||||
|
bValue = getSortValue(
|
||||||
|
map && map[key] ? map[key](b[key], b) : b[key]
|
||||||
|
);
|
||||||
|
if ((aValue === null) != (bValue === null)) {
|
||||||
|
ret = aValue === null ? 1 : -1;
|
||||||
|
} else if (aValue < bValue) {
|
||||||
|
ret = by[index].operator == '+' ? -1 : 1;
|
||||||
|
} else if (aValue > bValue) {
|
||||||
|
ret = by[index].operator == '+' ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -302,6 +302,8 @@ if __name__ == "__main__":
|
||||||
if old <= 6581:
|
if old <= 6581:
|
||||||
run('./bin/pip', 'install', '-U', 'pip')
|
run('./bin/pip', 'install', '-U', 'pip')
|
||||||
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
||||||
|
if old <= 6659:
|
||||||
|
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
||||||
else:
|
else:
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
branch = get_branch()
|
branch = get_branch()
|
||||||
|
@ -322,6 +324,7 @@ if __name__ == "__main__":
|
||||||
current_branch = get_branch(path)
|
current_branch = get_branch(path)
|
||||||
revno = get_version(path)
|
revno = get_version(path)
|
||||||
if repo == 'pandora':
|
if repo == 'pandora':
|
||||||
|
print("Pandora Version pre update: ", revno)
|
||||||
pandora_old_revno = revno
|
pandora_old_revno = revno
|
||||||
current += revno
|
current += revno
|
||||||
if current_branch != branch:
|
if current_branch != branch:
|
||||||
|
@ -345,6 +348,7 @@ if __name__ == "__main__":
|
||||||
new += '+'
|
new += '+'
|
||||||
os.chdir(join(base, 'pandora'))
|
os.chdir(join(base, 'pandora'))
|
||||||
if pandora_old_revno != pandora_new_revno:
|
if pandora_old_revno != pandora_new_revno:
|
||||||
|
print("Pandora Version post update: ", pandora_new_revno)
|
||||||
os.chdir(base)
|
os.chdir(base)
|
||||||
run('./update.py', 'postupdate', pandora_old_revno, pandora_new_revno)
|
run('./update.py', 'postupdate', pandora_old_revno, pandora_new_revno)
|
||||||
os.chdir(join(base, 'pandora'))
|
os.chdir(join(base, 'pandora'))
|
||||||
|
@ -361,7 +365,7 @@ if __name__ == "__main__":
|
||||||
and row not in ['BEGIN;', 'COMMIT;']
|
and row not in ['BEGIN;', 'COMMIT;']
|
||||||
]
|
]
|
||||||
if diff:
|
if diff:
|
||||||
print('Database has changed, please make a backup and run %s db' % sys.argv[0])
|
print('Database has changed, please make a backup and run: sudo pandoractl update db')
|
||||||
elif branch != 'master':
|
elif branch != 'master':
|
||||||
print('pan.do/ra is at the latest release,\nyou can run "%s switch master" to switch to the development version' % sys.argv[0])
|
print('pan.do/ra is at the latest release,\nyou can run "%s switch master" to switch to the development version' % sys.argv[0])
|
||||||
elif current != new:
|
elif current != new:
|
||||||
|
|
|
@ -25,53 +25,20 @@ LXC=`grep -q lxc /proc/1/environ && echo 'yes' || echo 'no'`
|
||||||
if [ -e /etc/os-release ]; then
|
if [ -e /etc/os-release ]; then
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
fi
|
fi
|
||||||
if [ -z "$UBUNTU_CODENAME" ]; then
|
|
||||||
UBUNTU_CODENAME=bionic
|
|
||||||
fi
|
|
||||||
if [ "$VERSION_CODENAME" = "bullseye" ]; then
|
|
||||||
UBUNTU_CODENAME=focal
|
|
||||||
fi
|
|
||||||
if [ "$VERSION_CODENAME" = "bookworm" ]; then
|
|
||||||
UBUNTU_CODENAME=lunar
|
|
||||||
fi
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
echo "deb http://ppa.launchpad.net/j/pandora/ubuntu ${UBUNTU_CODENAME} main" > /etc/apt/sources.list.d/j-pandora.list
|
|
||||||
|
|
||||||
apt-get install -y gnupg
|
apt-get install -y gnupg curl
|
||||||
|
|
||||||
if [ -e /etc/apt/trusted.gpg.d ]; then
|
distribution=bookworm
|
||||||
gpg --dearmor > /etc/apt/trusted.gpg.d/j-pandora.gpg <<EOF
|
for version in bookworm trixie bionic focal jammy noble; do
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
if [ "$VERSION_CODENAME" = $version ]; then
|
||||||
Version: GnuPG v1
|
distribution=$VERSION_CODENAME
|
||||||
|
|
||||||
mI0ESXYhEgEEALl9jDTdmgpApPbjN+7b85dC92HisPUp56ifEkKJOBj0X5HhRqxs
|
|
||||||
Wjx/zlP4/XJGrHnxJyrdPxjSwAXz7bNdeggkN4JWdusTkr5GOXvggQnng0X7f/rX
|
|
||||||
oJwoEGtYOCODLPs6PC0qjh5yPzJVeiRsKUOZ7YVNnwNwdfS4D8RZvtCrABEBAAG0
|
|
||||||
FExhdW5jaHBhZCBQUEEgZm9yIGpeiLYEEwECACAFAkl2IRICGwMGCwkIBwMCBBUC
|
|
||||||
CAMEFgIDAQIeAQIXgAAKCRAohRM8AZde82FfA/9OB/64/YLaCpizHZ8f6DK3rGgF
|
|
||||||
e6mX3rFK8yOKGGL06316VhDzfzMiZSauUZ0t+lKHR/KZYeSaFwEoUoblTG/s4IIo
|
|
||||||
9aBMHWhVXJW6eifKUmTGqEn2/0UxoWQq2C3F6njMkCaP+ALOD5uzaSYGdjqAUAwS
|
|
||||||
pAAGSEQ4uz6bYSeM4Q==
|
|
||||||
=SM2a
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
apt-key add - <<EOF
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Version: GnuPG v1
|
|
||||||
|
|
||||||
mI0ESXYhEgEEALl9jDTdmgpApPbjN+7b85dC92HisPUp56ifEkKJOBj0X5HhRqxs
|
|
||||||
Wjx/zlP4/XJGrHnxJyrdPxjSwAXz7bNdeggkN4JWdusTkr5GOXvggQnng0X7f/rX
|
|
||||||
oJwoEGtYOCODLPs6PC0qjh5yPzJVeiRsKUOZ7YVNnwNwdfS4D8RZvtCrABEBAAG0
|
|
||||||
FExhdW5jaHBhZCBQUEEgZm9yIGpeiLYEEwECACAFAkl2IRICGwMGCwkIBwMCBBUC
|
|
||||||
CAMEFgIDAQIeAQIXgAAKCRAohRM8AZde82FfA/9OB/64/YLaCpizHZ8f6DK3rGgF
|
|
||||||
e6mX3rFK8yOKGGL06316VhDzfzMiZSauUZ0t+lKHR/KZYeSaFwEoUoblTG/s4IIo
|
|
||||||
9aBMHWhVXJW6eifKUmTGqEn2/0UxoWQq2C3F6njMkCaP+ALOD5uzaSYGdjqAUAwS
|
|
||||||
pAAGSEQ4uz6bYSeM4Q==
|
|
||||||
=SM2a
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
EOF
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
curl -Ls https://code.0x2620.org/api/packages/0x2620/debian/repository.key -o /etc/apt/keyrings/pandora.asc
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/pandora.asc] https://code.0x2620.org/api/packages/0x2620/debian $distribution main" > /etc/apt/sources.list.d/pandora.list
|
||||||
|
|
||||||
echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/99languages
|
echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/99languages
|
||||||
|
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
|
|
Loading…
Reference in a new issue