diff --git a/README.md b/README.md new file mode 100644 index 0000000..6213db9 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ + +Getting started: +``` +python3 -m venv venv +./venv/bin/pip install -r requirements.txt +./manage.py migrate +./manage.py load_titles +./mange.py runserver +``` diff --git a/app/settings.py b/app/settings.py index e45e806..4a2f6c6 100644 --- a/app/settings.py +++ b/app/settings.py @@ -143,3 +143,6 @@ STATICFILES_FINDERS = [ # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +TIMELINE_PREFIX = "https://aab21.pad.ma/" diff --git a/app/static/css/partials/_ascroll.scss b/app/static/css/partials/_ascroll.scss new file mode 100644 index 0000000..dab8c7d --- /dev/null +++ b/app/static/css/partials/_ascroll.scss @@ -0,0 +1,34 @@ +#ascroll { + font-family: "Noton Sans"; + width: 100vw; + + h1 { + margin: 4px; + margin-top: 32px; + margin-bottom: 64px; + font-size: 24px; + letter-spacing: 1px; + font-weight: bold; + } + .player { + position: absolute; + display: none; + width: 100vw; + height: auto; + //transition: opacity 0.4s; + + } + .annotation { + .frame { + img { + width: 100vw; + height: auto; + } + } + .text { + margin: 20px 20px; + font-size: 22px; + line-height: 26px; + } + } +} diff --git a/app/static/css/partials/_layout.scss b/app/static/css/partials/_layout.scss new file mode 100644 index 0000000..51dbe86 --- /dev/null +++ b/app/static/css/partials/_layout.scss @@ -0,0 +1,18 @@ +body { + background: #d3d; + color: #eee; + font-family: "Noto Sans"; + font-size: 20px; + overflow-x: hidden; +} + +main { + line-height: 1.2em; + +} + +nav { + a { + color: #fff; + } +} diff --git a/app/static/css/partials/_reset.scss b/app/static/css/partials/_reset.scss new file mode 100644 index 0000000..d9f27b5 --- /dev/null +++ b/app/static/css/partials/_reset.scss @@ -0,0 +1,48 @@ +/* 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, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +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; +} +ol, ul { + list-style: none; +} +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/app/static/css/partials/burger.scss b/app/static/css/partials/burger.scss new file mode 100644 index 0000000..18db412 --- /dev/null +++ b/app/static/css/partials/burger.scss @@ -0,0 +1,53 @@ + +.topnav { + background-color: #333; + position: relative; + height: 72px; + padding-left: 16px; + padding-top: 16px; + @media screen and (max-width: 799px) { + overflow: hidden; + z-index: 100; + height: initial; + padding-left: initial; + padding-top: initial; + } + + nav { + display: block; + text-align: right; + margin-right: 16px; + @media screen and (max-width: 799px) { + display: none; + text-align: initial; + margin-right: initial; + } + } + + a { + color: white; + text-decoration: none; + font-size: 17px; + @media screen and (max-width: 799px) { + padding: 14px 16px; + display: block; + } + + &.icon { + display: none; + @media screen and (max-width: 799px) { + display: block; + position: absolute; + right: 0; + top: 0; + } + } + + &:hover { + background-color: #ddd; + color: black; + } + } + + +} diff --git a/app/static/css/style.scss b/app/static/css/style.scss index 438b94c..b53e26c 100644 --- a/app/static/css/style.scss +++ b/app/static/css/style.scss @@ -1,12 +1,4 @@ -body { - background: #000; - color: #fff; - font-family: "Noto Sans"; - font-size: 20px; -} - -nav { - a { - color: #fff; - } -} +@import "partials/reset"; +@import "partials/layout"; +@import "partials/burger"; +@import "partials/ascroll"; diff --git a/app/static/js/ascroll.js b/app/static/js/ascroll.js new file mode 100644 index 0000000..f4b123c --- /dev/null +++ b/app/static/js/ascroll.js @@ -0,0 +1,121 @@ +var cache = cache || {} +var layer = 'descriptions' +var baseURL = 'https://pad.ma' +var imageResolution = 480 + +let lastKnownScrollPosition = 0 +let ticking = false; + + +async function pandoraAPI(action, data) { + var url = baseURL + '/api/' + //var url = '/pandoraAPI/' + 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] +} + +function updatePlayerPosition(video, lastKnownScrollPosition) { + console.log('update', lastKnownScrollPosition) + video.style.top = lastKnownScrollPosition + 'px' + video.style.display = 'block'; +} + +function updatePlayer(video, frame, currentTime) { + 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'; + //video.poster = frame.querySelector('img').src + video.currentTime = currentTime + video.controls = true + video.play() + video.style.opacity = 1 +} + +function isElementInViewport (el) { + var rect = el.getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */ + rect.right <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ + ); +} + +function onVisibilityChange(el, callback) { + var old_visible; + return function () { + var visible = isElementInViewport(el); + if (visible != old_visible) { + old_visible = visible; + if (typeof callback == 'function') { + callback(visible); + } + } + } +} + + +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}/${film.id}/480p.webm` + ascroll.appendChild(video) + + var h1 = document.createElement('h1') + h1.innerHTML = response.data.title + ascroll.appendChild(h1) + + response.data.layers[layer].forEach(annotation => { + var div = document.createElement('div') + div.classList.add('annotation') + div.innerHTML = ` +
+