diff --git a/pandora/clip/models.py b/pandora/clip/models.py index e66408a7..8ae0cd11 100644 --- a/pandora/clip/models.py +++ b/pandora/clip/models.py @@ -190,12 +190,6 @@ class MetaClip(object): def __str__(self): return self.public_id - def get_first_frame(self, resolution=None): - if resolution is None: - resolution = max(settings.CONFIG['video']['resolutions']) - return '/%s/%sp%0.03f.jpg' % (self.item.public_id, resolution, float(self.start)) - - class Meta: unique_together = ("item", "start", "end") diff --git a/pandora/document/models.py b/pandora/document/models.py index 326b9387..30006ecc 100644 --- a/pandora/document/models.py +++ b/pandora/document/models.py @@ -567,7 +567,7 @@ class Document(models.Model, FulltextMixin): if len(crop) == 4: path = os.path.join(folder, '%s.jpg' % ','.join(map(str, crop))) if not os.path.exists(path): - img = Image.open(src).convert('RGB').crop(crop) + img = Image.open(src).crop(crop) img.save(path) else: img = Image.open(path) diff --git a/pandora/document/views.py b/pandora/document/views.py index 112f1595..47bf5bd8 100644 --- a/pandora/document/views.py +++ b/pandora/document/views.py @@ -12,10 +12,9 @@ from oxdjango.decorators import login_required_json from oxdjango.http import HttpFileResponse from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response, HttpErrorJson from django import forms -from django.conf import settings from django.db.models import Count, Sum +from django.conf import settings from django.http import HttpResponse -from django.shortcuts import render from item import utils from item.models import Item @@ -513,33 +512,3 @@ def autocompleteDocuments(request, data): response['data']['items'] = [i['value'] for i in qs] return render_to_json_response(response) actions.register(autocompleteDocuments) - - -def document(request, fragment): - context = {} - parts = fragment.split('/') - id = parts[0] - page = None - crop = None - if len(parts) == 2: - rect = parts[1].split(',') - if len(rect) == 1: - page = rect[0] - else: - crop = rect - document = models.Document.objects.filter(id=ox.fromAZ(id)).first() - if document and document.access(request.user): - context['title'] = document.data['title'] - if document.data.get('description'): - context['description'] = document.data['description'] - link = request.build_absolute_uri(document.get_absolute_url()) - public_id = ox.toAZ(document.id) - preview = '/documents/%s/512p.jpg' % public_id - if page: - preview = '/documents/%s/512p%s.jpg' % (public_id, page) - if crop: - preview = '/documents/%s/512p%s.jpg' % (public_id, ','.join(crop)) - context['preview'] = request.build_absolute_uri(preview) - context['url'] = request.build_absolute_uri('/documents/' + fragment) - context['settings'] = settings - return render(request, "document.html", context) diff --git a/pandora/mobile/__init__.py b/pandora/mobile/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pandora/mobile/admin.py b/pandora/mobile/admin.py deleted file mode 100644 index 8c38f3f3..00000000 --- a/pandora/mobile/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/pandora/mobile/apps.py b/pandora/mobile/apps.py deleted file mode 100644 index 13970a58..00000000 --- a/pandora/mobile/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class MobileConfig(AppConfig): - name = 'mobile' diff --git a/pandora/mobile/migrations/__init__.py b/pandora/mobile/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pandora/mobile/models.py b/pandora/mobile/models.py deleted file mode 100644 index 71a83623..00000000 --- a/pandora/mobile/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/pandora/mobile/tests.py b/pandora/mobile/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/pandora/mobile/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/pandora/mobile/views.py b/pandora/mobile/views.py deleted file mode 100644 index fbc0f44c..00000000 --- a/pandora/mobile/views.py +++ /dev/null @@ -1,75 +0,0 @@ -from django.conf import settings -from django.shortcuts import render - -import ox - - -def index(request, fragment): - from item.models import Item - from edit.models import Edit - from document.models import Document - context = {} - parts = fragment.split('/') - if parts[0] in ('document', 'documents'): - type = 'document' - id = parts[1] - page = None - crop = None - if len(parts) == 3: - rect = parts[2].split(',') - if len(rect) == 1: - page = rect[0] - else: - crop = rect - document = Document.objects.filter(id=ox.fromAZ(id)).first() - if document and document.access(request.user): - context['title'] = document.data['title'] - link = request.build_absolute_uri(document.get_absolute_url()) - # FIXME: get preview image or fragment parse from url - public_id = ox.toAZ(document.id) - preview = '/documents/%s/512p.jpg' % public_id - if page: - preview = '/documents/%s/512p%s.jpg' % (public_id, page) - if crop: - preview = '/documents/%s/512p%s.jpg' % (public_id, ','.join(crop)) - context['preview'] = request.build_absolute_uri(preview) - - elif parts[0] == 'edits': - type = 'edit' - id = parts[1] - id = id.split(':') - username = id[0] - name = ":".join(id[1:]) - name = name.replace('_', ' ') - edit = Edit.objects.filter(user__username=username, name=name).first() - if edit and edit.accessible(request.user): - link = request.build_absolute_uri('/m' + edit.get_absolute_url()) - context['title'] = name - context['description'] = edit.description.split('\n\n')[0] - # FIXME: use sort from parts if needed - context['preview'] = request.build_absolute_uri(edit.get_clips().first().get_first_frame()) - else: - type = 'item' - id = parts[0] - item = Item.objects.filter(public_id=id).first() - if item and item.accessible(request.user): - link = request.build_absolute_uri(item.get_absolute_url()) - if len(parts) > 1 and parts[1] in ('editor', 'player'): - parts = [parts[0]] + parts[2:] - if len(parts) > 1: - inout = parts[1] - if '-' in inout: - inout = inout.split('-') - else: - inout = inout.split(',') - inout = [ox.parse_timecode(p) for p in inout] - if len(inout) == 3: - inout.pop(1) - context['preview'] = link + '/480p%s.jpg' % inout[0] - else: - context['preview'] = link + '/480p.jpg' - context['title'] = item.get('title') - if context: - context['url'] = request.build_absolute_uri('/m/' + fragment) - context['settings'] = settings - return render(request, "mobile/index.html", context) diff --git a/pandora/settings.py b/pandora/settings.py index eae5c942..f6fba9f7 100644 --- a/pandora/settings.py +++ b/pandora/settings.py @@ -67,8 +67,7 @@ STATICFILES_DIRS = ( STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - "sass_processor.finders.CssFinder", - "compressor.finders.CompressorFinder", + #'django.contrib.staticfiles.finders.DefaultStorageFinder', ) GEOIP_PATH = normpath(join(PROJECT_ROOT, '..', 'data', 'geo')) @@ -125,9 +124,6 @@ INSTALLED_APPS = ( 'django_extensions', 'django_celery_results', 'django_celery_beat', - 'compressor', - 'sass_processor', - 'app', 'log', 'annotation', @@ -154,7 +150,6 @@ INSTALLED_APPS = ( 'websocket', 'taskqueue', 'home', - 'mobile', ) AUTH_USER_MODEL = 'system.User' diff --git a/pandora/templates/document.html b/pandora/templates/document.html deleted file mode 100644 index c893d363..00000000 --- a/pandora/templates/document.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - {{head_title}} - - {% include "baseheader.html" %} - - {%if description %} {%endif%} - - - - - - - {%if description %}{%endif%} - - - - - - diff --git a/pandora/templates/item.html b/pandora/templates/item.html index 20a84eb7..6b6e22fd 100644 --- a/pandora/templates/item.html +++ b/pandora/templates/item.html @@ -22,7 +22,7 @@
-
+
{{title}}
{{description_html|safe}}
{% for c in clips %} diff --git a/pandora/templates/mobile/index.html b/pandora/templates/mobile/index.html deleted file mode 100644 index 5cda3d82..00000000 --- a/pandora/templates/mobile/index.html +++ /dev/null @@ -1,45 +0,0 @@ -{% load static sass_tags compress %} - - - - - {% if title %} - {{title}} - - - {% endif %} - {%if description %} - - - {%endif%} - {% if preview %} - - - - {% endif %} - {% if url %} - - {% endif %} - - {% compress css file m %} - - - {% endcompress %} - - - -
- {% compress js file m %} - - - - - - - - - - - {% endcompress %} - - diff --git a/pandora/urls.py b/pandora/urls.py index 36af1aa0..d8eba049 100644 --- a/pandora/urls.py +++ b/pandora/urls.py @@ -26,7 +26,6 @@ import edit.views import itemlist.views import item.views import item.site -import mobile.views import translation.views import urlalias.views @@ -48,7 +47,6 @@ urlpatterns = [ re_path(r'^collection/(?P.*?)/icon(?P\d*).jpg$', documentcollection.views.icon), re_path(r'^documents/(?P[A-Z0-9]+)/(?P\d*)p(?P[\d,]*).jpg$', document.views.thumbnail), re_path(r'^documents/(?P[A-Z0-9]+)/(?P.*?\.[^\d]{3})$', document.views.file), - re_path(r'^documents/(?P.*?)$', document.views.document), re_path(r'^edit/(?P.*?)/icon(?P\d*).jpg$', edit.views.icon), re_path(r'^list/(?P.*?)/icon(?P\d*).jpg$', itemlist.views.icon), re_path(r'^text/(?P.*?)/icon(?P\d*).jpg$', text.views.icon), @@ -67,7 +65,6 @@ urlpatterns = [ re_path(r'^robots.txt$', app.views.robots_txt), re_path(r'^sitemap.xml$', item.views.sitemap_xml), re_path(r'^sitemap(?P\d+).xml$', item.views.sitemap_part_xml), - re_path(r'm/(?P.*?)$', mobile.views.index), path(r'', item.site.urls), ] #sould this not be enabled by default? nginx should handle those diff --git a/requirements.txt b/requirements.txt index 737f77ce..66f20ff0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,9 +5,6 @@ celery<5.0,>4.3 django-celery-results<2 django-celery-beat django-extensions==2.2.9 -libsass -django-compressor -django-sass-processor gunicorn==20.0.4 html5lib requests<3.0.0,>=2.24.0 diff --git a/static/js/mainMenu.js b/static/js/mainMenu.js index 457f168d..a7906d8a 100644 --- a/static/js/mainMenu.js +++ b/static/js/mainMenu.js @@ -609,8 +609,6 @@ pandora.ui.mainMenu = function() { pandora.$ui.player.options({fullscreen: true}); } else if (data.id == 'embed') { pandora.$ui.embedDialog = pandora.ui.embedDialog().open(); - } else if (data.id == 'share') { - pandora.$ui.shareDialog = pandora.ui.shareDialog().open(); } else if (data.id == 'advancedfind') { pandora.$ui.filterDialog = pandora.ui.filterDialog().open(); } else if (data.id == 'clearquery') { @@ -1911,8 +1909,7 @@ pandora.ui.mainMenu = function() { }) } ] }, {}, - { id: 'embed', title: Ox._('Embed...') }, - { id: 'share', title: Ox._('Share...') } + { id: 'embed', title: Ox._('Embed...') } ]} } diff --git a/static/js/shareDialog.js b/static/js/shareDialog.js deleted file mode 100644 index cf399f2b..00000000 --- a/static/js/shareDialog.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -pandora.ui.shareDialog = function(/*[url, ]callback*/) { - - if (arguments.length == 1) { - var url, callback = arguments[0]; - } else { - var url = arguments[0], callback = arguments[1]; - } - var url = document.location.href.replace(document.location.hostname, document.location.hostname + '/m'), - $content = Ox.Element().append( - Ox.Input({ - height: 100, - width: 256, - placeholder: 'Share Link', - type: 'textarea', - disabled: true, - value: url - }) - ), - that = pandora.ui.iconDialog({ - buttons: [ - Ox.Button({ - id: 'close', - title: Ox._('Close') - }).bindEvent({ - click: function() { - that.close(); - } - }), - ], - keys: {enter: 'close', escape: 'close'}, - content: $content, - title: "Share current view", - }); - return that; -} diff --git a/static/mobile/css/reset.css b/static/mobile/css/reset.css deleted file mode 100644 index b84ca0d8..00000000 --- a/static/mobile/css/reset.css +++ /dev/null @@ -1,46 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -u, i, center, -ol, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; - -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/static/mobile/css/style.css b/static/mobile/css/style.css deleted file mode 100644 index 811b250c..00000000 --- a/static/mobile/css/style.css +++ /dev/null @@ -1,171 +0,0 @@ -body { - margin: 0; - //background: rgb(240, 240, 240); - //background: rgb(144, 144, 144); - //color: rgb(0, 0, 0); - background: rgb(16, 16, 16); - color: rgb(240, 240, 240); - font-family: "Noto Sans", "Lucida Grande", "Segoe UI", "DejaVu Sans", "Lucida Sans Unicode", Helvetica, Arial, sans-serif; - line-height: normal; -} - -a { - color: rgb(128, 128, 255) -} -iframe { - max-width: 100%; -} - -.player { - max-width: 100%; -} -video, .poster { - border-bottom: 1px solid yellow; -} -.content { - display: flex; - flex-direction: column; - min-height: 100vh; - max-width: 1000px; - margin: auto; -} -.title { - padding-top: 16px; - padding-bottom: 0; - margin-bottom: 4px; - font-size: 14pt; - font-weight: bold; - border-bottom: 1px solid pink; - text-wrap: balance; -} -.byline { - padding: 0; - padding-bottom: 16px; -} -.player { - text-align: center; - padding-top: 0px; - padding-bottom: 0px; -} -@media(max-width:768px) { - .title, - .byline { - padding-left: 4px; - padding-right: 4px; - } - .player { - position: sticky; - top: 0px; - } -} -.player video { - z-index: 0; -} -.value { - padding: 4px; - padding-top: 16px; - padding-left: 0; - padding-right: 0; - flex: 1; -} -@media(max-width:768px) { - .value { - padding-left: 4px; - padding-right: 4px; - } -} -.more { - padding-top: 16px; - padding-bottom: 16px; - text-align: center; - font-size: 16pt; -} -.layer.active { - padding-top: 8px; -} -.layer.active:first-child { - padding-top: 0px; -} -.layer h3 { - font-weight: bold; - padding: 4px; - padding-left: 0; - margin: 0; - //display: none; -} -.layer.active h3 { - display: block; -} - -.annotation { - padding: 4px; - border-bottom: 1px solid blueviolet; - display: none; -} -.annotation.active { - display: block; -} -.annotation.active.place, -.annotation.active.string { - display: inline-block; - border: 1px solid blueviolet; - margin-bottom: 4px; -} -@media(max-width:768px) { - .annotation a img { - width: 100%; - } -} - -.layer h3 { - cursor: pointer; -} -.layer .icon svg { - width: 12px; - height: 12px; -} -.layer.collapsed .annotation.active { - display: none; -} -.rotate { - transform: rotate(90deg) translateY(-100%); - transform-origin:bottom left; -} - -.document.text { - line-height: 1.5; - letter-spacing: 0.1px; - word-wrap: break-word; - hyphens: auto; -} -.document.text p { - padding-bottom: 1em; -} -.document.text img { - max-width: 100vw; - margin-left: -4px; - margin-right: -4px; -} -figure { - text-align: center; -} -figure img { - max-width: 100vw; - margin-left: -4px; - margin-right: -4px; -} -@media(max-width:768px) { -.document.text { - padding-left: 4px; - padding-right: 4px; -} -} - -ol li { - margin-left: 1em; -} - -.blocked svg { - width: 64px; - height: 64px; -} diff --git a/static/mobile/js/VideoElement.js b/static/mobile/js/VideoElement.js deleted file mode 100644 index 8238d95d..00000000 --- a/static/mobile/js/VideoElement.js +++ /dev/null @@ -1,730 +0,0 @@ -'use strict'; - -/*@ -VideoElement VideoElement Object - options Options object - autoplay autoplay - items array of objects with src,in,out,duration - loop loop playback - playbackRate playback rate - position start position - self Shared private variable - ([options[, self]]) -> VideoElement Object - loadedmetadata loadedmetadata - itemchange itemchange - seeked seeked - seeking seeking - sizechange sizechange - ended ended -@*/ - -(function() { - var queue = [], - queueSize = 100, - restrictedElements = [], - requiresUserGesture = mediaPlaybackRequiresUserGesture(), - unblock = []; - - -window.VideoElement = function(options) { - - var self = {}, - that = document.createElement("div"); - - self.options = { - autoplay: false, - items: [], - loop: false, - muted: false, - playbackRate: 1, - position: 0, - volume: 1 - } - Object.assign(self.options, options); - debug(self.options) - - that.style.position = "relative" - that.style.width = "100%" - that.style.height = "100%" - that.style.maxHeight = "100vh" - that.style.margin = 'auto' - if (self.options.aspectratio) { - that.style.aspectRatio = self.options.aspectratio - } else { - that.style.height = '128px' - } - that.triggerEvent = function(event, data) { - if (event != 'timeupdate') { - debug('Video', 'triggerEvent', event, data); - } - event = new Event(event) - event.data = data - that.dispatchEvent(event) - } - - - /* - .update({ - items: function() { - self.loadedMetadata = false; - loadItems(function() { - self.loadedMetadata = true; - var update = true; - if (self.currentItem >= self.numberOfItems) { - self.currentItem = 0; - } - if (!self.numberOfItems) { - self.video.src = ''; - that.triggerEvent('durationchange', { - duration: that.duration() - }); - } else { - if (self.currentItemId != self.items[self.currentItem].id) { - // check if current item is in new items - self.items.some(function(item, i) { - if (item.id == self.currentItemId) { - self.currentItem = i; - loadNextVideo(); - update = false; - return true; - } - }); - if (update) { - self.currentItem = 0; - self.currentItemId = self.items[self.currentItem].id; - } - } - if (!update) { - that.triggerEvent('seeked'); - that.triggerEvent('durationchange', { - duration: that.duration() - }); - } else { - setCurrentVideo(function() { - that.triggerEvent('seeked'); - that.triggerEvent('durationchange', { - duration: that.duration() - }); - }); - } - } - }); - }, - playbackRate: function() { - self.video.playbackRate = self.options.playbackRate; - } - }) - .css({width: '100%', height: '100%'}); - */ - - debug('Video', 'VIDEO ELEMENT OPTIONS', self.options); - - self.currentItem = -1; - self.currentTime = 0; - self.currentVideo = 0; - self.items = []; - self.loadedMetadata = false; - that.paused = self.paused = true; - self.seeking = false; - self.loading = true; - self.buffering = true; - self.videos = [getVideo(), getVideo()]; - self.video = self.videos[self.currentVideo]; - self.video.classList.add("active") - self.volume = self.options.volume; - self.muted = self.options.muted; - self.brightness = document.createElement('div') - self.brightness.style.top = '0' - self.brightness.style.left = '0' - self.brightness.style.width = '100%' - self.brightness.style.height = '100%' - self.brightness.style.background = 'rgb(0, 0, 0)' - self.brightness.style.opacity = '0' - self.brightness.style.position = "absolute" - that.append(self.brightness) - - self.timeupdate = setInterval(function() { - if (!self.paused - && !self.loading - && self.loadedMetadata - && self.items[self.currentItem] - && self.items[self.currentItem].out - && self.video.currentTime >= self.items[self.currentItem].out) { - setCurrentItem(self.currentItem + 1); - } - }, 30); - - // mobile browsers only allow playing media elements after user interaction - if (restrictedElements.length > 0) { - unblock.push(setSource) - setTimeout(function() { - that.triggerEvent('requiresusergesture'); - }) - } else { - setSource(); - } - - function getCurrentTime() { - var item = self.items[self.currentItem]; - return self.seeking || self.loading - ? self.currentTime - : item ? item.position + self.video.currentTime - item['in'] : 0; - } - - function getset(key, value) { - var ret; - if (isUndefined(value)) { - ret = self.video[key]; - } else { - self.video[key] = value; - ret = that; - } - return ret; - } - - function getVideo() { - var video = getVideoElement() - video.style.display = "none" - video.style.width = "100%" - video.style.height = "100%" - video.style.margin = "auto" - video.style.background = '#000' - if (self.options.aspectratio) { - video.style.aspectRatio = self.options.aspectratio - } else { - video.style.height = '128px' - } - video.style.top = 0 - video.style.left = 0 - video.style.position = "absolute" - video.preload = "metadata" - video.addEventListener("ended", event => { - if (self.video == video) { - setCurrentItem(self.currentItem + 1); - } - }) - video.addEventListener("loadedmetadata", event => { - //console.log("!!", video.src, "loaded", 'current?', video == self.video) - }) - video.addEventListener("progress", event => { - // stop buffering if buffered to end point - var item = self.items[self.currentItem], - nextItem = mod(self.currentItem + 1, self.numberOfItems), - next = self.items[nextItem], - nextVideo = self.videos[mod(self.currentVideo + 1, self.videos.length)]; - if (self.video == video && (video.preload != 'none' || self.buffering)) { - if (clipCached(video, item)) { - video.preload = 'none'; - self.buffering = false; - if (nextItem != self.currentItem) { - nextVideo.preload = 'auto'; - } - } else { - if (nextItem != self.currentItem && nextVideo.preload != 'none' && nextVideo.src) { - nextVideo.preload = 'none'; - } - } - } else if (nextVideo == video && video.preload != 'none' && nextVideo.src) { - if (clipCached(video, next)) { - video.preload = 'none'; - } - } - - function clipCached(video, item) { - var cached = false - for (var i=0; i= item.out) { - cached = true - } - } - return cached - } - }) - video.addEventListener("volumechange", event => { - if (self.video == video) { - that.triggerEvent('volumechange') - } - }) - video.addEventListener("play", event => { - /* - if (self.video == video) { - that.triggerEvent('play') - } - */ - }) - video.addEventListener("pause", event => { - /* - if (self.video == video) { - that.triggerEvent('pause') - } - */ - }) - video.addEventListener("timeupdate", event => { - if (self.video == video) { - /* - var box = self.video.getBoundingClientRect() - if (box.width && box.height) { - that.style.width = box.width + 'px' - that.style.height = box.height + 'px' - } - */ - that.triggerEvent('timeupdate', { - currentTime: getCurrentTime() - }) - } - }) - video.addEventListener("seeking", event => { - if (self.video == video) { - that.triggerEvent('seeking') - } - }) - video.addEventListener("stop", event => { - if (self.video == video) { - //self.video.pause(); - that.triggerEvent('ended'); - } - }) - that.append(video) - return video - } - - function getVideoElement() { - var video; - if (requiresUserGesture) { - if (queue.length) { - video = queue.pop(); - } else { - video = document.createElement('video'); - restrictedElements.push(video); - } - } else { - video = document.createElement('video'); - } - video.playsinline = true - video.setAttribute('playsinline', 'playsinline') - video.setAttribute('webkit-playsinline', 'webkit-playsinline') - video.WebKitPlaysInline = true - return video - }; - - function getVolume() { - var volume = 1; - if (self.items[self.currentItem] && isNumber(self.items[self.currentItem].volume)) { - volume = self.items[self.currentItem].volume; - } - return self.volume * volume; - } - - - function isReady(video, callback) { - if (video.seeking && !self.paused && !self.seeking) { - that.triggerEvent('seeking'); - debug('Video', 'isReady', 'seeking'); - video.addEventListener('seeked', function(event) { - debug('Video', 'isReady', 'seeked'); - that.triggerEvent('seeked'); - callback(video); - }, {once: true}) - } else if (video.readyState) { - callback(video); - } else { - that.triggerEvent('seeking'); - video.addEventListener('loadedmetadata', function(event) { - callback(video); - }, {once: true}); - video.addEventListener('seeked', event => { - that.triggerEvent('seeked'); - }, {once: true}) - } - } - - function loadItems(callback) { - debug('loadItems') - var currentTime = 0, - items = self.options.items.map(function(item) { - return isObject(item) ? {...item} : {src: item}; - }); - - self.items = items; - self.numberOfItems = self.items.length; - items.forEach(item => { - item['in'] = item['in'] || 0; - item.position = currentTime; - if (item.out) { - item.duration = item.out - item['in']; - } - if (item.duration) { - if (!item.out) { - item.out = item.duration; - } - currentTime += item.duration; - item.id = getId(item); - } else { - getVideoInfo(item.src, function(info) { - item.duration = info.duration; - if (!item.out) { - item.out = item.duration; - } - currentTime += item.duration; - item.id = getId(item); - }); - } - }) - debug('loadItems done') - callback && callback(); - - function getId(item) { - return item.id || item.src + '/' + item['in'] + '-' + item.out; - } - } - - function loadNextVideo() { - if (self.numberOfItems <= 1) { - return; - } - var item = self.items[self.currentItem], - nextItem = mod(self.currentItem + 1, self.numberOfItems), - next = self.items[nextItem], - nextVideo = self.videos[mod(self.currentVideo + 1, self.videos.length)]; - nextVideo.addEventListener('loadedmetadata', function() { - if (self.video != nextVideo) { - nextVideo.currentTime = next['in'] || 0; - } - }, {once: true}); - nextVideo.src = next.src; - nextVideo.preload = 'metadata'; - } - - function setCurrentItem(item) { - debug('Video', 'sCI', item, self.numberOfItems); - var interval; - if (item >= self.numberOfItems || item < 0) { - if (self.options.loop) { - item = mod(item, self.numberOfItems); - } else { - self.seeking = false; - self.ended = true; - that.paused = self.paused = true; - self.video && self.video.pause(); - that.triggerEvent('ended'); - return; - } - } - self.video && self.video.pause(); - self.currentItem = item; - self.currentItemId = self.items[self.currentItem].id; - setCurrentVideo(function() { - if (!self.loadedMetadata) { - self.loadedMetadata = true; - that.triggerEvent('loadedmetadata'); - } - debug('Video', 'sCI', 'trigger itemchange', - self.items[self.currentItem]['in'], self.video.currentTime, self.video.seeking); - that.triggerEvent('sizechange'); - that.triggerEvent('itemchange', { - item: self.currentItem - }); - }); - } - - function setCurrentVideo(callback) { - var css = {}, - muted = self.muted, - item = self.items[self.currentItem], - next; - debug('Video', 'sCV', JSON.stringify(item)); - - ['left', 'top', 'width', 'height'].forEach(function(key) { - css[key] = self.videos[self.currentVideo].style[key]; - }); - self.currentTime = item.position; - self.loading = true; - if (self.video) { - self.videos[self.currentVideo].style.display = "none" - self.videos[self.currentVideo].classList.remove("active") - self.video.pause(); - } - self.currentVideo = mod(self.currentVideo + 1, self.videos.length); - self.video = self.videos[self.currentVideo]; - self.video.classList.add("active") - self.video.muted = true; // avoid sound glitch during load - if (!self.video.attributes.src || self.video.attributes.src.value != item.src) { - self.loadedMetadata && debug('Video', 'caching next item failed, reset src'); - self.video.src = item.src; - } - self.video.preload = 'metadata'; - self.video.volume = getVolume(); - self.video.playbackRate = self.options.playbackRate; - Object.keys(css).forEach(key => { - self.video.style[key] = css[key] - }) - self.buffering = true; - debug('Video', 'sCV', self.video.src, item['in'], - self.video.currentTime, self.video.seeking); - isReady(self.video, function(video) { - var in_ = item['in'] || 0; - - function ready() { - debug('Video', 'sCV', 'ready'); - self.seeking = false; - self.loading = false; - self.video.muted = muted; - !self.paused && self.video.play(); - self.video.style.display = 'block' - callback && callback(); - loadNextVideo(); - } - if (video.currentTime == in_) { - debug('Video', 'sCV', 'already at position', item.id, in_, video.currentTime); - ready(); - } else { - self.video.addEventListener("seeked", event => { - debug('Video', 'sCV', 'seeked callback'); - ready(); - }, {once: true}) - if (!self.seeking) { - debug('Video', 'sCV set in', video.src, in_, video.currentTime, video.seeking); - self.seeking = true; - video.currentTime = in_; - if (self.paused) { - var promise = self.video.play(); - if (promise !== undefined) { - promise.then(function() { - self.video.pause(); - self.video.muted = muted; - }).catch(function() { - self.video.pause(); - self.video.muted = muted; - }); - } else { - self.video.pause(); - self.video.muted = muted; - } - } - } - } - }); - } - - function setCurrentItemTime(currentTime) { - debug('Video', 'sCIT', currentTime, self.video.currentTime, - 'delta', currentTime - self.video.currentTime); - isReady(self.video, function(video) { - if (self.video == video) { - if(self.video.seeking) { - self.video.addEventListener("seeked", event => { - that.triggerEvent('seeked'); - self.seeking = false; - }, {once: true}) - } else if (self.seeking) { - that.triggerEvent('seeked'); - self.seeking = false; - } - video.currentTime = currentTime; - } - }); - } - - function setCurrentTime(time) { - debug('Video', 'sCT', time); - var currentTime, currentItem; - self.items.forEach(function(item, i) { - if (time >= item.position - && time < item.position + item.duration) { - currentItem = i; - currentTime = time - item.position + item['in']; - return false; - } - }); - if (self.items.length) { - // Set to end of items if time > duration - if (isUndefined(currentItem) && isUndefined(currentTime)) { - currentItem = self.items.length - 1; - currentTime = self.items[currentItem].duration + self.items[currentItem]['in']; - } - debug('Video', 'sCT', time, '=>', currentItem, currentTime); - if (currentItem != self.currentItem) { - setCurrentItem(currentItem); - } - self.seeking = true; - self.currentTime = time; - that.triggerEvent('seeking'); - setCurrentItemTime(currentTime); - } else { - self.currentTime = 0; - } - } - - function setSource() { - self.loadedMetadata = false; - loadItems(function() { - setCurrentTime(self.options.position); - self.options.autoplay && setTimeout(function() { - that.play(); - }); - }); - } - - - /*@ - brightness get/set brightness - @*/ - that.brightness = function() { - var ret; - if (arguments.length == 0) { - ret = 1 - parseFloat(self.brightness.style.opacity); - } else { - self.brightness.style.opacity = 1 - arguments[0] - ret = that; - } - return ret; - }; - - /*@ - buffered buffered - @*/ - that.buffered = function() { - return self.video.buffered; - }; - - /*@ - currentTime get/set currentTime - @*/ - that.currentTime = function() { - var ret; - if (arguments.length == 0) { - ret = getCurrentTime(); - } else { - self.ended = false; - setCurrentTime(arguments[0]); - ret = that; - } - return ret; - }; - - /*@ - duration duration - @*/ - that.duration = function() { - return self.items ? self.items.reduce((duration, item) => { - return duration + item.duration; - }, 0) : NaN; - }; - - /*@ - muted get/set muted - @*/ - that.muted = function(value) { - if (!isUndefined(value)) { - self.muted = value; - } - return getset('muted', value); - }; - - /*@ - pause pause - @*/ - that.pause = function() { - that.paused = self.paused = true; - self.video.pause(); - that.paused && that.triggerEvent('pause') - }; - - /*@ - play play - @*/ - that.play = function() { - if (self.ended) { - that.currentTime(0); - } - isReady(self.video, function(video) { - self.ended = false; - that.paused = self.paused = false; - self.seeking = false; - video.play(); - that.triggerEvent('play') - }); - }; - - that.removeElement = function() { - self.currentTime = getCurrentTime(); - self.loading = true; - clearInterval(self.timeupdate); - //Chrome does not properly release resources, reset manually - //http://code.google.com/p/chromium/issues/detail?id=31014 - self.videos.forEach(function(video) { - video.src = '' - }); - return Ox.Element.prototype.removeElement.apply(that, arguments); - }; - - /*@ - videoHeight get videoHeight - @*/ - that.videoHeight = function() { - return self.video.videoHeight; - }; - - /*@ - videoWidth get videoWidth - @*/ - that.videoWidth = function() { - return self.video.videoWidth; - }; - - /*@ - volume get/set volume - @*/ - that.volume = function(value) { - if (isUndefined(value)) { - value = self.volume - } else { - self.volume = value; - self.video.volume = getVolume(); - } - return value; - }; - - return that; - -}; - -// mobile browsers only allow playing media elements after user interaction - - function mediaPlaybackRequiresUserGesture() { - // test if play() is ignored when not called from an input event handler - var video = document.createElement('video'); - video.muted = true - video.play(); - return video.paused; - } - - - async function removeBehaviorsRestrictions() { - debug('Video', 'remove restrictions on video', self.video, restrictedElements.length, queue.length); - if (restrictedElements.length > 0) { - var rElements = restrictedElements; - restrictedElements = []; - rElements.forEach(async function(video) { - await video.load(); - }); - setTimeout(function() { - var u = unblock; - unblock = []; - u.forEach(function(callback) { callback(); }); - }, 1000); - } - while (queue.length < queueSize) { - var video = document.createElement('video'); - video.load(); - queue.push(video); - } - } - - if (requiresUserGesture) { - window.addEventListener('keydown', removeBehaviorsRestrictions); - window.addEventListener('mousedown', removeBehaviorsRestrictions); - window.addEventListener('touchstart', removeBehaviorsRestrictions); - } -})(); diff --git a/static/mobile/js/VideoPlayer.js b/static/mobile/js/VideoPlayer.js deleted file mode 100644 index 829fc02f..00000000 --- a/static/mobile/js/VideoPlayer.js +++ /dev/null @@ -1,400 +0,0 @@ -(function() { - -window.VideoPlayer = function(options) { - - var self = {}, that; - self.options = { - autoplay: false, - controls: true, - items: [], - loop: false, - muted: false, - playbackRate: 1, - position: 0, - volume: 1 - } - Object.assign(self.options, options); - that = VideoElement(options); - - self.controls = document.createElement('div') - self.controls.classList.add('mx-controls') - //self.controls.style.display = "none" - if (self.options.controls) { - var ratio = `aspect-ratio: ${self.options.aspectratio};` - if (!self.options.aspectratio) { - ratio = 'height: 128px;' - } - self.controls.innerHTML = ` - -
-
${icon.play}
-
-
-
- ${icon.mute} -
-
-
-
- -
-
-
-
-
-
- ${isIOS || !self.options.aspectratio ? "" : icon.enterFullscreen} -
-
- ` - var toggleVideo = event => { - event.preventDefault() - event.stopPropagation() - if (that.paused) { - that.play() - } else { - that.pause() - } - } - async function toggleFullscreen(event) { - if (isIOS) { - return - } - event.preventDefault() - event.stopPropagation() - if (!document.fullscreenElement) { - that.classList.add('fullscreen') - if (that.webkitRequestFullscreen) { - await that.webkitRequestFullscreen() - } else { - await that.requestFullscreen() - } - console.log('entered fullscreen') - var failed = false - if (!screen.orientation.type.startsWith("landscape")) { - await screen.orientation.lock("landscape").catch(err => { - console.log('no luck with lock', err) - /* - document.querySelector('.error').innerHTML = '' + err - that.classList.remove('fullscreen') - document.exitFullscreen(); - screen.orientation.unlock() - failed = true - */ - }) - } - if (that.paused && !failed) { - that.play() - } - } else { - that.classList.remove('fullscreen') - document.exitFullscreen(); - screen.orientation.unlock() - } - } - var toggleSound = event => { - event.preventDefault() - event.stopPropagation() - if (that.muted()) { - that.muted(false) - } else { - that.muted(true) - } - } - var showControls - var toggleControls = event => { - if (self.controls.style.opacity == '0') { - event.preventDefault() - event.stopPropagation() - self.controls.style.opacity = '1' - showControls = setTimeout(() => { - self.controls.style.opacity = that.paused ? '1' : '0' - showControls = null - }, 3000) - } else { - self.controls.style.opacity = '0' - } - } - self.controls.addEventListener("mousemove", event => { - if (showControls) { - clearTimeout(showControls) - } - self.controls.style.opacity = '1' - showControls = setTimeout(() => { - self.controls.style.opacity = that.paused ? '1' : '0' - showControls = null - }, 3000) - }) - self.controls.addEventListener("mouseleave", event => { - if (showControls) { - clearTimeout(showControls) - } - self.controls.style.opacity = that.paused ? '1' : '0' - showControls = null - }) - self.controls.addEventListener("touchstart", toggleControls) - self.controls.querySelector('.toggle').addEventListener("click", toggleVideo) - self.controls.querySelector('.volume').addEventListener("click", toggleSound) - self.controls.querySelector('.fullscreen-btn').addEventListener("click", toggleFullscreen) - document.addEventListener('fullscreenchange', event => { - if (!document.fullscreenElement) { - screen.orientation.unlock() - that.classList.remove('fullscreen') - that.querySelector('.fullscreen-btn').innerHTML = icon.enterFullscreen - } else { - self.controls.querySelector('.fullscreen-btn').innerHTML = icon.exitFullscreen - } - }) - that.append(self.controls) - } - - function getVideoWidth() { - if (document.fullscreenElement) { - return '' - } - var av = that.querySelector('video.active') - return av ? av.getBoundingClientRect().width + 'px' : '100%' - } - - var playOnLoad = false - var unblock = document.createElement("div") - - that.addEventListener("requiresusergesture", event => { - unblock.style.position = "absolute" - unblock.style.width = '100%' - unblock.style.height = '100%' - unblock.style.backgroundImage = `url(${self.options.poster})` - unblock.style.zIndex = '1000' - unblock.style.backgroundPosition = "top left" - unblock.style.backgroundRepeat = "no-repeat" - unblock.style.backgroundSize = "cover" - unblock.style.display = 'flex' - unblock.classList.add('mx-controls') - unblock.classList.add('poster') - unblock.innerHTML = ` -
-
${icon.play}
-
-
- ` - self.controls.style.opacity = '0' - unblock.addEventListener("click", event => { - event.preventDefault() - event.stopPropagation() - playOnLoad = true - unblock.querySelector('.toggle').innerHTML = ` -
${icon.loading}
- ` - }, {once: true}) - that.append(unblock) - }) - var loading = true - that.brightness(0) - that.addEventListener("loadedmetadata", event => { - // - }) - that.addEventListener("seeked", event => { - if (loading) { - that.brightness(1) - loading = false - } - if (playOnLoad) { - playOnLoad = false - var toggle = self.controls.querySelector('.toggle') - toggle.title = 'Pause' - toggle.querySelector('div').innerHTML = icon.pause - self.controls.style.opacity = '0' - unblock.remove() - that.play() - } - }) - - var time = that.querySelector('.controls .time div'), - progress = that.querySelector('.controls .position .progress') - that.querySelector('.controls .position').addEventListener("click", event => { - var bar = event.target - while (bar && !bar.classList.contains('bar')) { - bar = bar.parentElement - } - if (bar && bar.classList.contains('bar')) { - event.preventDefault() - event.stopPropagation() - var rect = bar.getBoundingClientRect() - var x = event.clientX - rect.x - var percent = x / rect.width - var position = percent * self.options.duration - if (self.options.position) { - position += self.options.position - } - progress.style.width = (100 * percent) + '%' - that.currentTime(position) - } - }) - that.addEventListener("timeupdate", event => { - var currentTime = that.currentTime(), - duration = self.options.duration - if (self.options.position) { - currentTime -= self.options.position - } - progress.style.width = (100 * currentTime / duration) + '%' - duration = formatDuration(duration) - currentTime = formatDuration(currentTime) - while (duration && duration.startsWith('00:')) { - duration = duration.slice(3) - } - currentTime = currentTime.slice(currentTime.length - duration.length) - time.innerText = `${currentTime} / ${duration}` - - }) - - that.addEventListener("play", event => { - var toggle = self.controls.querySelector('.toggle') - toggle.title = 'Pause' - toggle.querySelector('div').innerHTML = icon.pause - self.controls.style.opacity = '0' - }) - that.addEventListener("pause", event => { - var toggle = self.controls.querySelector('.toggle') - toggle.title = 'Play' - toggle.querySelector('div').innerHTML = icon.play - self.controls.style.opacity = '1' - }) - that.addEventListener("ended", event => { - var toggle = self.controls.querySelector('.toggle') - toggle.title = 'Play' - toggle.querySelector('div').innerHTML = icon.play - self.controls.style.opacity = '1' - }) - that.addEventListener("seeking", event => { - //console.log("seeking") - - }) - that.addEventListener("seeked", event => { - //console.log("seeked") - }) - that.addEventListener("volumechange", event => { - var volume = self.controls.querySelector('.volume') - if (that.muted()) { - volume.innerHTML = icon.unmute - volume.title = "Unmute" - } else { - volume.innerHTML = icon.mute - volume.title = "Mute" - } - }) - window.addEventListener('resize', event => { - // - }) - return that -}; - -})(); diff --git a/static/mobile/js/api.js b/static/mobile/js/api.js deleted file mode 100644 index 5e989597..00000000 --- a/static/mobile/js/api.js +++ /dev/null @@ -1,25 +0,0 @@ -var pandora = { - format: getFormat(), - hostname: document.location.hostname || 'pad.ma' -} - -var pandoraURL = document.location.hostname ? "" : `https://${pandora.hostname}` -var cache = cache || {} - -async function pandoraAPI(action, data) { - var url = pandoraURL + '/api/' - var key = JSON.stringify([action, data]) - if (!cache[key]) { - var response = await fetch(url, { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - action: action, - data: data - }) - }) - cache[key] = await response.json() - } - return cache[key] -} - diff --git a/static/mobile/js/documents.js b/static/mobile/js/documents.js deleted file mode 100644 index 2d58b0ff..00000000 --- a/static/mobile/js/documents.js +++ /dev/null @@ -1,112 +0,0 @@ - -async function loadDocument(id, args) { - var data = window.data = {} - var parts = id.split('/') - data.id = parts.shift() - data.site = pandora.hostname - - if (parts.length == 2) { - data.page = parts.shift() - } - - if (parts.length == 1) { - var rect = parts[0].split(',') - if (rect.length == 1) { - data.page = parts[0] - } else { - data.crop = rect - } - } else if (parts.length == 2) { - - } - - var response = await pandoraAPI('getDocument', { - id: data.id, - keys: [ - "id", - "title", - "extension", - "text", - ] - }) - if (response.status.code != 200) { - return { - site: data.site, - error: response.status - } - } - data.document = response['data'] - data.title = data.document.name - data.link = `${pandora.proto}://${data.site}/documents/${data.document.id}` - return data -} - -async function renderDocument(data) { - if (data.error) { - return renderError(data) - } - div = document.createElement('div') - div.className = "content" - if (!data.document) { - div.innerHTML = `
document not found
` - } else if (data.document.extension == "html") { - div.innerHTML = ` -

${data.document.title}

-
- ${data.document.text} -
- - ` - div.querySelectorAll('.text a').forEach(a => { - a.addEventListener("click", clickLink) - - }) - } else if (data.document.extension == "pdf" && data.page && data.crop) { - var img = `${pandora.proto}://${data.site}/documents/${data.document.id}/1024p${data.page},${data.crop.join(',')}.jpg` - data.link = getLink(`documents/${data.document.id}/${data.page}`) - div.innerHTML = ` -

${data.document.title}

-
- -
- - ` - } else if (data.document.extension == "pdf") { - var page = data.page || 1, - file = encodeURIComponent(`/documents/${data.document.id}/${safeDocumentName(data.document.title)}.pdf`) - div.innerHTML = ` -

${data.document.title}

-
- -
- - ` - } else if (data.document.extension == "jpg" || data.document.extension == "png") { - var img = `${pandora.proto}://${data.site}/documents/${data.document.id}/${safeDocumentName(data.document.title)}.${data.document.extension}` - var open_text = `Open on ${data.site}` - if (data.crop) { - img = `${pandora.proto}://${data.site}/documents/${data.document.id}/1024p${data.crop.join(',')}.jpg` - data.link = getLink(`documents/${data.document.id}`) - open_text = `Open image` - } - div.innerHTML = ` -

${data.document.title}

-
- -
- - ` - - } else { - div.innerHTML = `unsupported document type` - } - document.querySelector(".content").replaceWith(div) -} diff --git a/static/mobile/js/edits.js b/static/mobile/js/edits.js deleted file mode 100644 index f23766ef..00000000 --- a/static/mobile/js/edits.js +++ /dev/null @@ -1,230 +0,0 @@ - -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) { - 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(a[key]) - bValue = getSortValue(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; - }); -}; - -async function sortClips(edit, sort) { - var key = sort.key, index; - if (key == 'position') { - key = 'in'; - } - if ([ - 'id', 'index', 'in', 'out', 'duration', - 'title', 'director', 'year', 'videoRatio' - ].indexOf(key) > -1) { - sortBy(sort); - index = 0; - edit.clips.forEach(function(clip) { - clip.sort = index++; - if (sort.operator == '-') { - clip.sort = -clip.sort; - } - }); - } else { - var response = await pandoraAPI('sortClips', { - edit: edit.id, - sort: [sort] - }) - edit.clips.forEach(function(clip) { - clip.sort = response.data.clips.indexOf(clip.id); - if (sort.operator == '-') { - clip.sort = -clip.sort; - } - }); - sortBy({ - key: 'sort', - operator: '+' - }); - } - function sortBy(key) { - edit.clips = sortByKey(edit.clips, [key]); - } -} - -async function loadEdit(id, args) { - var data = window.data = {} - data.id = id - data.site = pandora.hostname - - var response = await pandoraAPI('getEdit', { - id: data.id, - keys: [ - ] - }) - if (response.status.code != 200) { - return { - site: data.site, - error: response.status - } - } - data.edit = response['data'] - if (data.edit.status !== 'public') { - return { - site: data.site, - error: { - code: 403, - text: 'permission denied' - } - } - } - data.layers = {} - data.videos = [] - - if (args.sort) { - await sortClips(data.edit, args.sort) - } - - data.edit.duration = 0; - data.edit.clips.forEach(function(clip) { - clip.position = data.edit.duration; - data.edit.duration += clip.duration; - }); - - data.edit.clips.forEach(clip => { - var start = clip['in'] || 0, end = clip.out, position = 0; - clip.durations.forEach((duration, idx) => { - if (!duration) { - return - } - if (position + duration <= start || position > end) { - // pass - } else { - var video = {} - var oshash = clip.streams[idx] - video.src = getVideoURL(clip.item, 480, idx+1, '', oshash) - /* - if (clip['in'] && clip.out) { - video.src += `#t=${clip['in']},${clip.out}` - } - */ - if (isNumber(clip.volume)) { - video.volume = clip.volume; - } - if ( - position <= start - && position + duration > start - ) { - video['in'] = start - position; - } - if (position + duration >= end) { - video.out = end - position; - } - if (video['in'] && video.out) { - video.duration = video.out - video['in'] - } else if (video.out) { - video.duration = video.out; - } else if (!isUndefined(video['in'])) { - video.duration = duration - video['in']; - video.out = duration; - } else { - video.duration = duration; - video['in'] = 0; - video.out = video.duration; - } - data.videos.push(video) - } - position += duration - }) - Object.keys(clip.layers).forEach(layer => { - data.layers[layer] = data.layers[layer] || [] - clip.layers[layer].forEach(annotation => { - if (args.users && !args.users.includes(annotation.user)) { - return - } - if (args.layers && !args.layers.includes(layer)) { - return - } - var a = {...annotation} - a['id'] = clip['id'] + '/' + a['id']; - a['in'] = Math.max( - clip['position'], - a['in'] - clip['in'] + clip['position'] - ); - a.out = Math.min( - clip['position'] + clip['duration'], - a.out - clip['in'] + clip['position'] - ); - data.layers[layer].push(a) - }) - }) - }) - var value = [] - pandora.layerKeys.forEach(layer => { - if (!data.layers[layer]) { - return - } - var html = [] - var layerData = getObjectById(pandora.site.layers, layer) - html.push(`

- ${icon.down} - ${layerData.title} -

`) - data.layers[layer].forEach(annotation => { - html.push(` -
- ${annotation.value} -
- `) - }) - value.push('
' + html.join('\n') + '
') - }) - data.value = value.join('\n') - - data.title = data.edit.name - data.byline = data.edit.description - data.link = `${pandora.proto}://${data.site}/edits/${data.edit.id}` - data.poster = data.videos[0].src.split('/48')[0] + `/480p${data.videos[0].in}.jpg` - data.aspectratio = data.edit.clips[0].videoRatio - data.duration = data.edit.duration - return data - -} diff --git a/static/mobile/js/icons.js b/static/mobile/js/icons.js deleted file mode 100644 index 295a12fe..00000000 --- a/static/mobile/js/icons.js +++ /dev/null @@ -1,182 +0,0 @@ -var icon = {} -icon.enterFullscreen = ` - - - - - - - - -` -icon.exitFullscreen = ` - - - - - - - - -` - -icon.mute = ` - - - - - - - -` - -icon.unmute = ` - - - - -` - -icon.play = ` - - - -` -icon.pause = ` - - - - - -` -icon.loading = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -icon.right = ` - - - -` -icon.left = ` - - - -` -icon.down = ` - - - -` diff --git a/static/mobile/js/item.js b/static/mobile/js/item.js deleted file mode 100644 index 61463aa5..00000000 --- a/static/mobile/js/item.js +++ /dev/null @@ -1,133 +0,0 @@ - -async function loadData(id, args) { - var data = window.data = {} - data.id = id - data.site = pandora.hostname - - var response = await pandoraAPI('get', { - id: data.id.split('/')[0], - keys: [ - "id", - "title", - "director", - "summary", - "streams", - "duration", - "durations", - "layers", - "rightslevel", - "videoRatio" - ] - }) - - if (response.status.code != 200) { - return { - site: data.site, - error: response.status - } - } - data.item = response['data'] - if (data.item.rightslevel > pandora.site.capabilities.canPlayClips['guest']) { - return { - site: data.site, - error: { - code: 403, - text: 'permission denied' - } - } - } - if (id.split('/').length == 1 || id.split('/')[1] == 'info') { - data.view = 'info' - data.title = data.item.title - data.byline = data.item.director ? data.item.director.join(', ') : '' - data.link = `${pandora.proto}://${data.site}/${data.item.id}/info` - let poster = pandora.site.user.ui.icons == 'posters' ? 'poster' : 'icon' - data.icon = `${pandora.proto}://${data.site}/${data.item.id}/${poster}.jpg` - return data - } - - if (id.includes('-') || id.includes(',')) { - var inout = id.split('/')[1].split(id.includes('-') ? '-' : ',').map(parseDuration) - data.out = inout.pop() - data['in'] = inout.pop() - } else if (args.full) { - data.out = data.item.duration - data['in'] = 0 - } else { - var annotation = await pandoraAPI('getAnnotation', { - id: data.id, - }) - if (annotation.status.code != 200) { - return { - site: data.site, - error: annotation.status - } - } - data.annotation = annotation['data'] - data['in'] = data.annotation['in'] - data.out = data.annotation['out'] - } - - data.layers = {} - - pandora.layerKeys.forEach(layer => { - data.item.layers[layer].forEach(annot => { - if (data.annotation) { - if (annot.id == data.annotation.id) { - data.layers[layer] = data.layers[layer] || [] - data["layers"][layer].push(annot) - } - } else if (annot['out'] > data['in'] && annot['in'] < data['out']) { - if (args.users && !args.users.includes(annot.user)) { - return - } - if (args.layers && !args.layers.includes(layer)) { - return - } - data.layers[layer] = data.layers[layer] || [] - //annot['in'] = Math.max([annot['in'], data['in']]) - //annot['out'] = Math.min([annot['out'], data['out']]) - data["layers"][layer].push(annot) - } - }) - }) - data.videos = [] - data.item.durations.forEach((duration, idx) => { - var oshash = data.item.streams[idx] - var url = getVideoURL(data.item.id, 480, idx+1, '', oshash) - data.videos.push({ - src: url, - duration: duration - }) - }) - var value = [] - Object.keys(data.layers).forEach(layer => { - var html = [] - var layerData = getObjectById(pandora.site.layers, layer) - html.push(`

- ${icon.down} - ${layerData.title} -

`) - data.layers[layer].forEach(annotation => { - html.push(` -
- ${annotation.value} -
- `) - }) - value.push('
' + html.join('\n') + '
') - }) - data.value = value.join('\n') - - data.title = data.item.title - data.byline = data.item.director ? data.item.director.join(', ') : '' - data.link = `${pandora.proto}://${data.site}/${data.item.id}/${data["in"]},${data.out}` - data.poster = `${pandora.proto}://${data.site}/${data.item.id}/480p${data["in"]}.jpg` - data.aspectratio = data.item.videoRatio - if (data['in'] == data['out']) { - data['out'] += 0.04 - } - data.duration = data.out - data['in'] - return data -} - diff --git a/static/mobile/js/main.js b/static/mobile/js/main.js deleted file mode 100644 index c69a9f79..00000000 --- a/static/mobile/js/main.js +++ /dev/null @@ -1,129 +0,0 @@ - - -function parseURL() { - var fragment = document.location.hash.slice(1) - if (!fragment && document.location.pathname.startsWith('/m/')) { - var prefix = document.location.protocol + '//' + document.location.hostname + '/m/' - fragment = document.location.href.slice(prefix.length) - } - var args = fragment.split('?') - var id = args.shift() - if (args) { - args = args.join('?').split('&').map(arg => { - var kv = arg.split('=') - k = kv.shift() - v = kv.join('=') - if (['users', 'layers'].includes(k)) { - v = v.split(',') - } - return [k, v] - }).filter(kv => { - return kv[0].length - }) - if (args) { - args = Object.fromEntries(args); - } else { - args = {} - } - } else { - args = {} - } - var type = "item" - if (id.startsWith('document')) { - id = id.split('/') - id.shift() - id = id.join('/') - type = "document" - } else if (id.startsWith('edits/')) { - var parts = id.split('/') - parts.shift() - id = parts.shift().replace(/_/g, ' ') - type = "edit" - if (parts.length >= 2) { - args.sort = parts[1] - if (args.sort[0] == '-') { - args.sort = { - key: args.sort.slice(1), - operator: '-' - } - } else if (args.sort[0] == '+') { - args.sort = { - key: args.sort.slice(1), - operator: '+' - } - } else { - args.sort = { - key: args.sort, - operator: '+' - } - } - } - args.parts = parts - } else { - if (id.endsWith('/player') || id.endsWith('/editor')) { - args.full = true - } - id = id.replace('/editor/', '/').replace('/player/', '/') - type = "item" - } - return [type, id, args] -} - -function render() { - var type, id, args; - [type, id, args] = parseURL() - document.querySelector(".content").innerHTML = loadingScreen - if (type == "document") { - loadDocument(id, args).then(renderDocument) - } else if (type == "edit") { - loadEdit(id, args).then(renderItem) - } else { - loadData(id, args).then(renderItem) - } - -} -var loadingScreen = ` - -
${icon.loading}
-` - -document.querySelector(".content").innerHTML = loadingScreen -pandoraAPI("init").then(response => { - pandora = { - ...pandora, - ...response.data - } - pandora.proto = pandora.site.site.https ? 'https' : 'http' - if (pandora.site.site.videoprefix.startsWith('//')) { - pandora.site.site.videoprefix = pandora.proto + ':' + pandora.site.site.videoprefix - } - var layerKeys = [] - var subtitleLayer = pandora.site.layers.filter(layer => {return layer.isSubtitles})[0] - if (subtitleLayer) { - layerKeys.push(subtitleLayer.id) - } - pandora.site.layers.map(layer => { - return layer.id - }).filter(layer => { - return !subtitleLayer || layer != subtitleLayer.id - }).forEach(layer => { - layerKeys.push(layer) - }) - pandora.layerKeys = layerKeys - id = document.location.hash.slice(1) - window.addEventListener("hashchange", event => { - render() - }) - window.addEventListener("popstate", event => { - console.log("popsatte") - render() - }) - window.addEventListener('resize', event => { - }) - render() -}) diff --git a/static/mobile/js/render.js b/static/mobile/js/render.js deleted file mode 100644 index a7519342..00000000 --- a/static/mobile/js/render.js +++ /dev/null @@ -1,120 +0,0 @@ - -function renderItemInfo(data) { - div = document.createElement('div') - div.className = "content" - div.innerHTML = ` -
${data.title}
- -
- -
- - ` - document.querySelector(".content").replaceWith(div) -} - -function renderItem(data) { - if (data.error) { - return renderError(data) - } - if (data.view == "info") { - return renderItemInfo(data) - } - div = document.createElement('div') - div.className = "content" - div.innerHTML = ` -
${data.title}
- -
-
-
-
${data.value}
- - ` - var comments = ` -
- - -
-
- ` - - div.querySelectorAll('.layer a').forEach(a => { - a.addEventListener("click", clickLink) - }) - - div.querySelectorAll('.layer').forEach(layer => { - layer.querySelector('h3').addEventListener("click", event => { - var img = layer.querySelector('h3 .icon') - if (layer.classList.contains("collapsed")) { - layer.classList.remove("collapsed") - img.innerHTML = icon.down - } else { - layer.classList.add("collapsed") - img.innerHTML = icon.right - } - }) - }) - - var video = window.video = VideoPlayer({ - items: data.videos, - poster: data.poster, - position: data["in"] || 0, - duration: data.duration, - aspectratio: data.aspectratio - }) - div.querySelector('.video').replaceWith(video) - video.classList.add('video') - - video.addEventListener("loadedmetadata", event => { - // - }) - video.addEventListener("timeupdate", event => { - var currentTime = video.currentTime() - if (currentTime >= data['out']) { - if (!video.paused) { - video.pause() - } - video.currentTime(data['in']) - } - div.querySelectorAll('.annotation').forEach(annot => { - var now = currentTime - var start = parseFloat(annot.dataset.in) - var end = parseFloat(annot.dataset.out) - if (now >= start && now <= end) { - annot.classList.add("active") - annot.parentElement.classList.add('active') - } else { - annot.classList.remove("active") - if (!annot.parentElement.querySelector('.active')) { - annot.parentElement.classList.remove('active') - } - } - }) - - }) - document.querySelector(".content").replaceWith(div) -} - -function renderError(data) { - var link = '/' + document.location.hash.slice(1) - div = document.createElement('div') - div.className = "content" - div.innerHTML = ` - -
- Page not found
- Open on ${data.site} -
- ` - document.querySelector(".content").replaceWith(div) -} diff --git a/static/mobile/js/utils.js b/static/mobile/js/utils.js deleted file mode 100644 index d77024f1..00000000 --- a/static/mobile/js/utils.js +++ /dev/null @@ -1,160 +0,0 @@ - -const parseDuration = function(string) { - return string.split(':').reverse().slice(0, 4).reduce(function(p, c, i) { - return p + (parseFloat(c) || 0) * (i == 3 ? 86400 : Math.pow(60, i)); - }, 0); -}; - -const formatDuration = function(seconds) { - var parts = [ - parseInt(seconds / 86400), - parseInt(seconds % 86400 / 3600), - parseInt(seconds % 3600 / 60), - s = parseInt(seconds % 60) - ] - return parts.map(p => { return p.toString().padStart(2, '0')}).join(':') -} - -const typeOf = function(value) { - return Object.prototype.toString.call(value).slice(8, -1).toLowerCase(); -}; -const isUndefined = function(value) { - return typeOf(value) == 'undefined'; -} -const isNumber = function(value) { - return typeOf(value) == 'number'; -}; -const isObject = function(value) { - return typeOf(value) == 'object'; -}; -const isNull = function(value) { - return typeOf(value) == 'null'; -}; -const isString = function(value) { - return typeOf(value) == 'string'; -}; -const isEmpty = function(value) { - var type = typeOf(value) - if (['arguments', 'array', 'nodelist', 'string'].includes(value)) { - return value.length == 0 - } - if (['object', 'storage'].includes(type)) { - return Object.keys(value).length; - } - return false -}; -const mod = function(number, by) { - return (number % by + by) % by; -}; - -const getObjectById = function(array, id) { - return array.filter(obj => { return obj.id == id})[0] -} - -const debug = function() { - if (localStorage.debug) { - console.log.apply(null, arguments) - } -}; - -const canPlayMP4 = function() { - var video = document.createElement('video'); - if (video.canPlayType && video.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace('no', '')) { - return true - } - return false -}; - -const canPlayWebm = function() { - var video = document.createElement('video'); - if (video.canPlayType && video.canPlayType('video/webm; codecs="vp8, vorbis"').replace('no', '')) { - return true - } - return false -}; - -const getFormat = function() { - //var format = canPlayWebm() ? "webm" : "mp4" - var format = canPlayMP4() ? "mp4" : "webm" - return format -} - -const safeDocumentName = function(name) { - ['\\?', '#', '%', '/'].forEach(function(c) { - var r = new RegExp(c, 'g') - name = name.replace(r, '_'); - }) - return name; -}; - -const getVideoInfo = function() { - console.log("FIXME implement getvideoInfo") -} - -const isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent); - - -const getLink = function(fragment) { - if (document.location.hash.length > 2) { - return '#' + fragment - } else { - return '/m/' + fragment - } -} - -const clickLink = function(event) { - var a = event.target - while (a && a.tagName != 'A') { - a = a.parentElement - } - if (!a) { - return - } - var href = a.attributes.href.value - var prefix = document.location.protocol + '//' + document.location.hostname - if (href.startsWith(prefix)) { - href = href.slice(prefix.length) - } - if (href.startsWith('/')) { - event.preventDefault() - event.stopPropagation() - var link = href.split('#embed')[0] - if (document.location.hash.length > 2) { - if (link.startsWith('/m')) { - link = link.slice(2) - } - document.location.hash = '#' + link.slice(1) - } else { - if (!link.startsWith('/m')) { - link = '/m' + link - } - history.pushState({}, '', link); - render() - } - } -} - -const getUid = (function() { - var uid = 0; - return function() { - return ++uid; - }; -}()); - - -const getVideoURLName = function(id, resolution, part, track, streamId) { - return id + '/' + resolution + 'p' + part + (track ? '.' + track : '') - + '.' + pandora.format + (streamId ? '?' + streamId : ''); -}; - -const getVideoURL = function(id, resolution, part, track, streamId) { - var uid = getUid(), - prefix = pandora.site.site.videoprefix - .replace('{id}', id) - .replace('{part}', part) - .replace('{resolution}', resolution) - .replace('{uid}', uid) - .replace('{uid42}', uid % 42); - return prefix + '/' + getVideoURLName(id, resolution, part, track, streamId); -}; - diff --git a/update.py b/update.py index 5cf0dfca..2cc118f6 100755 --- a/update.py +++ b/update.py @@ -303,8 +303,6 @@ if __name__ == "__main__": run('./bin/pip', 'install', 'yt-dlp>=2022.3.8.2') if old < 6465: run('./bin/pip', 'install', '-r', 'requirements.txt') - if old < 6507: - run('./bin/pip', 'install', '-r', 'requirements.txt') else: if len(sys.argv) == 1: branch = get_branch()