From 21db49fe4869e3158511ce72f96dee2fbfbe8920 Mon Sep 17 00:00:00 2001 From: j Date: Sun, 10 Oct 2021 16:06:43 +0100 Subject: [PATCH] ascroll for item/edits --- .gitignore | 2 + app/static/css/partials/_film.scss | 14 ++ app/static/css/partials/_text.scss | 8 + app/static/css/style.scss | 1 + app/static/js/ascroll.js | 164 +++++++++++++----- app/templates/base.html | 6 +- app/templates/essays.html | 4 - app/templates/fallback.html | 90 ++++++++++ app/templates/film.html | 24 +-- app/templates/films.html | 3 + app/templates/index.html | 9 +- app/templates/text.html | 10 ++ app/templates/texts.html | 10 ++ app/text/admin.py | 11 +- app/text/migrations/0002_rename_essay_text.py | 17 ++ app/text/models.py | 22 ++- app/text/views.py | 16 +- app/urls.py | 5 +- app/video/models.py | 7 + 19 files changed, 348 insertions(+), 75 deletions(-) create mode 100644 app/static/css/partials/_text.scss delete mode 100644 app/templates/essays.html create mode 100644 app/templates/fallback.html create mode 100644 app/templates/text.html create mode 100644 app/templates/texts.html create mode 100644 app/text/migrations/0002_rename_essay_text.py diff --git a/.gitignore b/.gitignore index 27aa007..467e24f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ venv __pycache__ db.sqlite3 www/ +*.swp +venv diff --git a/app/static/css/partials/_film.scss b/app/static/css/partials/_film.scss index a098201..972f2ef 100644 --- a/app/static/css/partials/_film.scss +++ b/app/static/css/partials/_film.scss @@ -2,8 +2,10 @@ .films { width: 100vw; + margin: 8px; .film { width: 100vw; + margin-bottom: 16px; display: flex; flex-direction: row; .left, .right { @@ -34,3 +36,15 @@ } } + +.film { + .info { + margin: 8px; + } + .play { + text-align: center; + margin: 8px; + margin-top: 16px; + margin-bottom: 16px; + } +} diff --git a/app/static/css/partials/_text.scss b/app/static/css/partials/_text.scss new file mode 100644 index 0000000..369ce44 --- /dev/null +++ b/app/static/css/partials/_text.scss @@ -0,0 +1,8 @@ + + +.texts { + .text { + margin: 8px; + } + +} diff --git a/app/static/css/style.scss b/app/static/css/style.scss index d5fb348..6ebb911 100644 --- a/app/static/css/style.scss +++ b/app/static/css/style.scss @@ -2,4 +2,5 @@ @import "partials/layout"; @import "partials/burger"; @import "partials/film"; +@import "partials/text"; @import "partials/ascroll"; diff --git a/app/static/js/ascroll.js b/app/static/js/ascroll.js index f4b123c..8226e20 100644 --- a/app/static/js/ascroll.js +++ b/app/static/js/ascroll.js @@ -31,12 +31,15 @@ function updatePlayerPosition(video, lastKnownScrollPosition) { video.style.display = 'block'; } -function updatePlayer(video, frame, currentTime) { +function updatePlayer(video, frame, currentTime, src) { var rect = frame.getBoundingClientRect(); video.style.opacity = 0 console.log('update player', rect) video.style.top = (rect.top + window.scrollY) + 'px' video.style.display = 'block'; + if (src) { + video.src = src + } //video.poster = frame.querySelector('img').src video.currentTime = currentTime video.controls = true @@ -67,55 +70,132 @@ function onVisibilityChange(el, callback) { } } +function loadItem(config) { + pandoraAPI('get', {id: config.item, keys: ['id', 'title', 'layers']}).then(response => { + var ascroll = document.querySelector('#ascroll') + var loaded = false -pandoraAPI('get', {id: film.id, keys: ['id', 'title', 'layers']}).then(response => { - var ascroll = document.querySelector('#ascroll') - var loaded = false + var video = document.createElement('video') + video.classList.add('player') + video.muted = true + video.src = `${baseURL}/${config.item}/480p.webm` + ascroll.appendChild(video) - var video = document.createElement('video') - video.classList.add('player') - video.muted = true - video.src = `${baseURL}/${film.id}/480p.webm` - ascroll.appendChild(video) + var h1 = document.createElement('h1') + h1.innerHTML = response.data.title + ascroll.appendChild(h1) - var h1 = document.createElement('h1') - h1.innerHTML = response.data.title - ascroll.appendChild(h1) + response.data.layers[layer].forEach(annotation => { + if (config.user && annotation.user != config.user) { + return + } + var div = document.createElement('div') + div.classList.add('annotation') + div.innerHTML = ` +
+
${annotation.value}
- response.data.layers[layer].forEach(annotation => { - var div = document.createElement('div') - div.classList.add('annotation') - div.innerHTML = ` -
-
${annotation.value}
+ ` + ascroll.appendChild(div) + var frame = div.querySelector('.frame') + document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) { + if (loaded && visible) + updatePlayer(video, frame, annotation['in']) - ` - ascroll.appendChild(div) - var frame = div.querySelector('.frame') - document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) { - if (loaded && visible) - updatePlayer(video, frame, annotation['in']) + })) + }) + loaded = true + let frame = ascroll.querySelector('.annotation .frame') + if (frame) { + updatePlayer(video, frame, 0) + } - })) + /* + document.addEventListener('scroll', function(e) { + lastKnownScrollPosition = window.scrollY; + + if (!ticking) { + window.requestAnimationFrame(function() { + updatePlayerPosition(video, lastKnownScrollPosition); + ticking = false; + }); + + ticking = true; + } + }) + */ }) - loaded = true - let frame = ascroll.querySelector('.annotation .frame') - if (frame) { - updatePlayer(video, frame, 0) - } +} +function loadEdit(config) { + pandoraAPI('getEdit', {id: config.edit, keys: []}).then(response => { + console.log(response) + var ascroll = document.querySelector('#ascroll') + var loaded = false - /* - document.addEventListener('scroll', function(e) { - lastKnownScrollPosition = window.scrollY; + var video = document.createElement('video') + video.classList.add('player') + video.muted = true + /* + video.src = `${baseURL}/${config.item}/480p.webm` + */ + ascroll.appendChild(video) - if (!ticking) { - window.requestAnimationFrame(function() { - updatePlayerPosition(video, lastKnownScrollPosition); - ticking = false; - }); + var h1 = document.createElement('h1') + var first + h1.innerHTML = response.data.name + ascroll.appendChild(h1) - ticking = true; - } + response.data.clips.forEach(clip => { + clip.layers[layer].forEach(annotation => { + if (config.user && annotation.user != config.user) { + return + } + if (!first) { + first = annotation + } + var div = document.createElement('div') + div.classList.add('annotation') + div.innerHTML = ` +
+
${annotation.value}
+ + ` + ascroll.appendChild(div) + var frame = div.querySelector('.frame') + document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) { + var src = `${baseURL}/${annotation.id.split('/')[0]}/480p.webm` + if (loaded && visible) + updatePlayer(video, frame, annotation['in'], src) + + })) + }) + }) + loaded = true + let frame = ascroll.querySelector('.annotation .frame') + if (frame) { + var src = `${baseURL}/${ifrst.id.split('/')[0]}/480p.webm` + updatePlayer(video, frame, first['in'], src) + } + + /* + document.addEventListener('scroll', function(e) { + lastKnownScrollPosition = window.scrollY; + + if (!ticking) { + window.requestAnimationFrame(function() { + updatePlayerPosition(video, lastKnownScrollPosition); + ticking = false; + }); + + ticking = true; + } + }) + */ }) - */ -}) +} + +if (config.item) { + loadItem(config) +} else if (config.edit) { + loadEdit(config) +} diff --git a/app/templates/base.html b/app/templates/base.html index 189bf59..f28e4d3 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -13,16 +13,14 @@
- + phtantasmapolis: Looking Back to the Future
未竟之城:回顧未來
diff --git a/app/templates/essays.html b/app/templates/essays.html deleted file mode 100644 index 8d368ac..0000000 --- a/app/templates/essays.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "base.html" %} -{% block main %} -some overview page with stuff -{% endblock %} diff --git a/app/templates/fallback.html b/app/templates/fallback.html new file mode 100644 index 0000000..659328b --- /dev/null +++ b/app/templates/fallback.html @@ -0,0 +1,90 @@ + + + + + + phantas.ma + + +
+
+

Phantas.ma

+

+
+
+ + diff --git a/app/templates/film.html b/app/templates/film.html index e898943..a67760d 100644 --- a/app/templates/film.html +++ b/app/templates/film.html @@ -1,21 +1,23 @@ {% extends "base.html" %} {% block main %} - - - +
+

{{ film.data.title | safe }}

{{ film.data.director|join:", "|safe }}

{{ film.data.summary|safe }}

+
-
- Watch Fullscreen -
-
- Watch with Annotations -
-
- Watch with Annotations (Mandarin title) + +
+ + {% for text in film.related_texts %} + + {% endfor %}
{% endblock %} diff --git a/app/templates/films.html b/app/templates/films.html index 53817ca..1807c81 100644 --- a/app/templates/films.html +++ b/app/templates/films.html @@ -7,11 +7,14 @@

{{ film.data.title | safe }}

{{ film.data.director|join:", "|safe }}

+ {% comment %}
+
+ {% endcomment %}
{% endfor %}
diff --git a/app/templates/index.html b/app/templates/index.html index 8d368ac..de19c98 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,4 +1,11 @@ {% extends "base.html" %} {% block main %} -some overview page with stuff +
+
+ Phantas.ma/polis is mainly in support by the Cultural Taiwan Foundation and in cooperation with SEA plateaus +
+
+ 本計畫由財團法人文化臺灣基金會支持、SEA plateaus協力 +
+
{% endblock %} diff --git a/app/templates/text.html b/app/templates/text.html new file mode 100644 index 0000000..8888823 --- /dev/null +++ b/app/templates/text.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block main %} +
+{% endblock %} +{% block end %} + + +{% endblock %} diff --git a/app/templates/texts.html b/app/templates/texts.html new file mode 100644 index 0000000..9c7d616 --- /dev/null +++ b/app/templates/texts.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block main %} + +
+ {% for text in texts %} + + {% endfor %} +{% endblock %} diff --git a/app/text/admin.py b/app/text/admin.py index 8c38f3f..f9f28e5 100644 --- a/app/text/admin.py +++ b/app/text/admin.py @@ -1,3 +1,12 @@ from django.contrib import admin -# Register your models here. +from . import models + + +@admin.decorators.register(models.Text) +class TextAdmin(admin.ModelAdmin): + pass + +@admin.decorators.register(models.Page) +class PageAdmin(admin.ModelAdmin): + pass diff --git a/app/text/migrations/0002_rename_essay_text.py b/app/text/migrations/0002_rename_essay_text.py new file mode 100644 index 0000000..4241c2e --- /dev/null +++ b/app/text/migrations/0002_rename_essay_text.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.7 on 2021-10-10 14:25 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('text', '0001_initial'), + ] + + operations = [ + migrations.RenameModel( + old_name='Essay', + new_name='Text', + ), + ] diff --git a/app/text/models.py b/app/text/models.py index 94058f7..0ef0c35 100644 --- a/app/text/models.py +++ b/app/text/models.py @@ -1,5 +1,7 @@ import logging +import json +from django.conf import settings from django.contrib.auth import get_user_model from django.db import models @@ -19,15 +21,27 @@ class Page(models.Model): teaser = models.TextField() body = models.TextField() -class Essay(models.Model): +class Text(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) slug = models.SlugField() public = models.BooleanField(default=False) + title = models.TextField() + teaser = models.TextField(default="", blank=True) + body = models.TextField(default="", blank=True) + data = models.JSONField(default=dict) - title = models.TextField() - teaser = models.TextField() - body = models.TextField() + def __str__(self): + return self.title + + def get_absolute_url(self): + return '/' + settings.URL_PREFIX + 'assemblies/' + self.slug + + def json(self): + data = {} + data['title'] = self.title + data.update(self.data) + return json.dumps(data) diff --git a/app/text/views.py b/app/text/views.py index 0f6eb92..6fdfc24 100644 --- a/app/text/views.py +++ b/app/text/views.py @@ -2,6 +2,10 @@ from django.shortcuts import render, redirect, get_object_or_404 from . import models +def fallback(request): + context = {} + return render(request, 'fallback.html', context) + def index(request): context = {} return render(request, 'index.html', context) @@ -10,12 +14,12 @@ def about(request): context = {} return render(request, 'about.html', context) -def essays(request): +def texts(request): context = {} - context['essays'] = models.Essay.objects.filter(public=True).order_by('created') - return render(request, 'essays.html', context) + context['texts'] = models.Text.objects.filter(public=True).order_by('created') + return render(request, 'texts.html', context) -def essay(request, slug, lang): +def text(request, slug): context = {} - context['essay'] = get_object_or_404(models.Essay, slug=slug) - return render(request, 'essay.html', context) + context['text'] = get_object_or_404(models.Text, slug=slug) + return render(request, 'text.html', context) diff --git a/app/urls.py b/app/urls.py index 96d308a..4497acc 100644 --- a/app/urls.py +++ b/app/urls.py @@ -30,9 +30,10 @@ urlpatterns = [ path(settings.URL_PREFIX + 'edit//play/', video.edit_play, name='edit_play'), path(settings.URL_PREFIX + 'edit//', video.edit, name='edit'), path(settings.URL_PREFIX + 'tv/', video.tv, name='tv'), - path(settings.URL_PREFIX + 'essays/', text.essays, name='essays'), - path(settings.URL_PREFIX + 'essay//', text.essay, name='essay'), + path(settings.URL_PREFIX + 'assemblies/', text.texts, name='texts'), + path(settings.URL_PREFIX + 'assemblies/', text.text, name='text'), path(settings.URL_PREFIX + 'about/', text.about, name='about'), path(settings.URL_PREFIX[:-1], text.index, name='index'), + path('', text.fallback, name='fallback'), ] diff --git a/app/video/models.py b/app/video/models.py index 8c0ab91..c42c323 100644 --- a/app/video/models.py +++ b/app/video/models.py @@ -22,6 +22,13 @@ class Film(models.Model): def __str__(self): return self.data.get('title', self.slug) + def get_absolute_url(self): + return '/' + settings.URL_PREFIX + 'film/' + self.slug + + def related_texts(self): + from ..text.models import Text + return Text.objects.filter(data__item=self.padma_id) + class Edit(models.Model): created = models.DateTimeField(auto_now_add=True)