From 83c938702eb67bd6d154bb98b632e64e5e7e183f Mon Sep 17 00:00:00 2001 From: julian laplace Date: Thu, 27 Oct 2022 16:42:57 +0200 Subject: add player and image viewer --- public/assets/css/audioplayer.css | 130 ++++++++++++++++++++++ public/assets/css/hootstream.css | 42 ++++++- public/assets/img/doc-white.svg | 1 + public/assets/img/doc.svg | 1 + public/assets/img/graph-white.svg | 1 + public/assets/img/graph.svg | 1 + public/assets/img/grid-white.svg | 1 + public/assets/img/grid.svg | 1 + public/assets/img/note-white.svg | 1 + public/assets/img/note.svg | 1 + public/assets/img/notes-white.svg | 1 + public/assets/img/notes.svg | 1 + public/assets/img/pause-white.svg | 1 + public/assets/img/pause.svg | 1 + public/assets/img/play-white.svg | 1 + public/assets/img/play.svg | 1 + public/assets/js/index.js | 2 + public/assets/js/lib/views/details/audio.js | 101 ++++++++++------- public/assets/js/lib/views/details/audioPlayer.js | 123 ++++++++++++++++++++ public/assets/js/lib/views/stream/hootstream.js | 79 ++++++------- views/hootstream/index.ejs | 1 - views/hootstream/templates.ejs | 2 +- views/pages/details.ejs | 1 - views/pages/profile.ejs | 1 - views/pages/stream.ejs | 1 + views/partials/footer.ejs | 1 + views/partials/header.ejs | 1 + views/partials/player.ejs | 23 ++++ views/partials/scripts.ejs | 1 + 29 files changed, 436 insertions(+), 87 deletions(-) create mode 100644 public/assets/css/audioplayer.css create mode 100644 public/assets/img/doc-white.svg create mode 100644 public/assets/img/doc.svg create mode 100644 public/assets/img/graph-white.svg create mode 100644 public/assets/img/graph.svg create mode 100644 public/assets/img/grid-white.svg create mode 100644 public/assets/img/grid.svg create mode 100644 public/assets/img/note-white.svg create mode 100644 public/assets/img/note.svg create mode 100644 public/assets/img/notes-white.svg create mode 100644 public/assets/img/notes.svg create mode 100644 public/assets/img/pause-white.svg create mode 100644 public/assets/img/pause.svg create mode 100644 public/assets/img/play-white.svg create mode 100644 public/assets/img/play.svg create mode 100644 public/assets/js/lib/views/details/audioPlayer.js create mode 100644 views/partials/player.ejs diff --git a/public/assets/css/audioplayer.css b/public/assets/css/audioplayer.css new file mode 100644 index 0000000..49a2809 --- /dev/null +++ b/public/assets/css/audioplayer.css @@ -0,0 +1,130 @@ +.icon.notes { + background-image: url(/assets/img/notes-white.svg); +} +.icon.note { + background-image: url(/assets/img/note-white.svg); +} +.icon.grid { + background-image: url(/assets/img/grid-white.svg); +} +.icon.graph { + background-image: url(/assets/img/graph-white.svg); +} +.playing .icon.audio, +.icon.pause { + background-image: url(/assets/img/pause-white.svg); +} +.icon.audio, +.icon.play { + background-image: url(/assets/img/play-white.svg); + background-size: 80% 80%; +} + +.player { + display: flex; + flex-direction: row; + justify-content: center; + position: fixed; + bottom: 2rem; + left: 0; + width: 100%; + transition: 0.2s cubic-bezier(0, 0, 1, 1); + opacity: 0; +} +.player.active { + opacity: 1; +} +.player:hover { + opacity: 1; +} +.player .controls { + width: 100%; + max-width: 45rem; + display: flex; + flex-direction: column; + padding: 1rem; +} + +.player .controls { + background: linear-gradient(-90deg, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.95)); +} +.player .trackInfo { + justify-content: space-between; + padding: 1rem 1.5rem; +} +.player .row { + display: flex; + flex-direction: row; +} +.player .icon { + cursor: pointer; + height: 3rem; + width: 4rem; + display: flex; + justify-content: center; + align-items: center; +} +.player .pos { + flex: 1; + position: relative; + cursor: pointer; + height: 3rem; + opacity: 0.8; +} +.player .pos:hover { + opacity: 1; +} +.player .title, +.player .time { + font-size: 1rem; + color: #eee; +} +.player .icon img { + display: none; + width: 2.5em; +} +.player .icon.active .play_icon { + display: none; +} +.player .icon.active .pause_icon { + display: block; +} +.player .icon .pause_icon { + display: none; +} +.player .icon .play_icon { + display: block; +} +.player .track { + pointer-events: none; + position: absolute; + top: 50%; + left: 0; + margin-top: -1px; + height: 2px; + background: #fff; + width: calc(100% - 1.5rem); +} +.player .dot { + pointer-events: none; + position: absolute; + top: 50%; + left: 0; + margin-top: -0.5rem; + width: 1rem; + height: 1rem; + border-radius: 50%; + background: #fff; + box-shadow: 0 0 3px rgba(255, 255, 255, 0.1); +} +.player .track, +.player .dot { + transition: box-shadow 0.1s, background 0.1s, left 0.1s, transform 0.1s; +} +.player .pos:hover .track { + background: #fff; +} +.player .pos:hover .dot { + background: #fff; + box-shadow: 0 0 5px rgba(255, 255, 255, 1); +} diff --git a/public/assets/css/hootstream.css b/public/assets/css/hootstream.css index 96b140c..470cb72 100644 --- a/public/assets/css/hootstream.css +++ b/public/assets/css/hootstream.css @@ -6,7 +6,7 @@ margin-top: 1.5rem; font-size: 16px; } -#hootstream #audio { +#audio { display: none; } #hootstream form { @@ -54,8 +54,46 @@ .hoot:hover > .text { opacity: 1 !important; } + +/** ImAgeS and other image lISTS!! */ +#hootevents .imageList { + margin-bottom: 1rem; + display: flex; + flex-direction: row; + align-items: flex-end; +} #hootevents .hootText img { - max-width: 100%; + max-width: calc(100vmin - 2rem); + max-height: calc(100vmin - 1rem); +} +#hootevents .imageList img { + max-width: calc(100vmin - 2rem); + max-height: calc(100vmin - 1rem); + display: block; +} +#hootevents .imageList .image { + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem; + max-height: 100vmin; + opacity: 0.95; + transition: opacity 0.1s; +} +#hootevents .imageList .caption { + margin: 1rem; + font-size: 0.9rem; + color: rgba(128, 128, 128, 0.5); + transition: color 0.1s; +} +#hootevents .imageList .image:hover { + opacity: 1; +} +#hootevents .imageList .image:hover .caption { + color: rgba(255, 255, 255, 0.8); +} +#hootevents .imageList .image:hover a { + opacity: 1 !important; } /** metadata */ diff --git a/public/assets/img/doc-white.svg b/public/assets/img/doc-white.svg new file mode 100644 index 0000000..dce69d3 --- /dev/null +++ b/public/assets/img/doc-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/doc.svg b/public/assets/img/doc.svg new file mode 100644 index 0000000..fb3237a --- /dev/null +++ b/public/assets/img/doc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/graph-white.svg b/public/assets/img/graph-white.svg new file mode 100644 index 0000000..ab65d5b --- /dev/null +++ b/public/assets/img/graph-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/graph.svg b/public/assets/img/graph.svg new file mode 100644 index 0000000..0abb9c2 --- /dev/null +++ b/public/assets/img/graph.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/grid-white.svg b/public/assets/img/grid-white.svg new file mode 100644 index 0000000..e84c482 --- /dev/null +++ b/public/assets/img/grid-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/grid.svg b/public/assets/img/grid.svg new file mode 100644 index 0000000..ea1563d --- /dev/null +++ b/public/assets/img/grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/note-white.svg b/public/assets/img/note-white.svg new file mode 100644 index 0000000..fb02b41 --- /dev/null +++ b/public/assets/img/note-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/note.svg b/public/assets/img/note.svg new file mode 100644 index 0000000..13e42c8 --- /dev/null +++ b/public/assets/img/note.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/notes-white.svg b/public/assets/img/notes-white.svg new file mode 100644 index 0000000..6968998 --- /dev/null +++ b/public/assets/img/notes-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/notes.svg b/public/assets/img/notes.svg new file mode 100644 index 0000000..aba0726 --- /dev/null +++ b/public/assets/img/notes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/pause-white.svg b/public/assets/img/pause-white.svg new file mode 100644 index 0000000..317b67a --- /dev/null +++ b/public/assets/img/pause-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/pause.svg b/public/assets/img/pause.svg new file mode 100644 index 0000000..48baa51 --- /dev/null +++ b/public/assets/img/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/play-white.svg b/public/assets/img/play-white.svg new file mode 100644 index 0000000..a14157f --- /dev/null +++ b/public/assets/img/play-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/play.svg b/public/assets/img/play.svg new file mode 100644 index 0000000..9ef742f --- /dev/null +++ b/public/assets/img/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/js/index.js b/public/assets/js/index.js index 1c8deb5..ef2b8f4 100644 --- a/public/assets/js/index.js +++ b/public/assets/js/index.js @@ -17,6 +17,8 @@ var app = (function () { }; app.ready = function () { + audio.init(); + app.player = new AudioPlayer(); app.router.route(); }; diff --git a/public/assets/js/lib/views/details/audio.js b/public/assets/js/lib/views/details/audio.js index fb72e0b..2329886 100644 --- a/public/assets/js/lib/views/details/audio.js +++ b/public/assets/js/lib/views/details/audio.js @@ -1,26 +1,27 @@ -var audio = (function () { - var audio = {}; +const audio = (function () { + const audio = {}; - var el, - music = [], - current_index = -1; - var links, comment, parent; - var selected = false; - var playing = false; - var built = false; - var initted = false; + let music = []; + let current_index = -1; + let links, parent; + let selected = false; + let playing = false; + let built = false; + let initted = false; + let ui = null; audio.init = function () { if (initted) { audio.index(); return; } - comment = document.querySelector("#comment"); parent = document.querySelector("#audio"); audio.index(); audio.build(); - el.src = music[0]; + if (music.length) { + audio.el.src = music[0]; + } initted = true; }; @@ -32,7 +33,7 @@ var audio = (function () { if (!link.href.match(/\.(mp3|wav|aiff?|m4a|ogg|opus|flac)$/)) return; link.dataset.index = music.length; music.push(link); - if (playing && link.href === el.src) { + if (playing && link.href === audio.el.src) { current_index = parseInt(link.dataset.index); } }); @@ -44,28 +45,55 @@ var audio = (function () { audio.build = function () { if (built) return; built = true; - el = audio.el = document.createElement("audio"); - el.setAttribute("controls", true); - // el.addEventListener("loadeddata", () => { if (selected) el.play() }) - el.addEventListener("ended", audio.next); - parent.appendChild(el); + audio.el = document.createElement("audio"); + audio.el.setAttribute("controls", true); + // audio.el.addEventListener("loadeddata", () => { if (selected) audio.el.play() }) + parent.appendChild(audio.el); document.body.addEventListener("keydown", audio.keydown); }; audio.destroy = function () { - el.pause(); - el = null; - parent.removeChild(el); + audio.el.pause(); + parent.removeChild(audio.el); + audio.el = null; document.body.removeEventListener("keydown", audio.keydown); built = false; }; + audio.listen = function (uiController) { + ui = uiController; + }; + audio.play = function (index) { playing = true; current_index = (parseInt(index) + music.length) % music.length; - el.src = music[current_index].href; - el.play(); + audio.el.src = music[current_index].href; + audio.el.play(); audio.set_cursor(); + if (ui) { + ui.onPlay(music[current_index]); + } + }; + + audio.pause = function () { + audio.el.pause(); + if (ui) { + ui.onPause(); + } + }; + + audio.toggle = function () { + if (audio.el.paused) { + audio.el.play(); + if (ui) { + ui.onPlay(music[current_index]); + } + } else { + audio.el.pause(); + if (ui) { + ui.onPause(); + } + } }; audio.set_cursor = function () { @@ -84,16 +112,11 @@ var audio = (function () { audio.play(current_index + 1); }; - audio.toggle = function () { - if (el.paused) el.play(); - else el.pause(); - }; - - audio.keydown = function (e) { - function element_is_text_input(el) { - var tagName = el.tagName.toLowerCase(); + audio.keydown = function (event) { + function element_is_text_input(element) { + var tagName = element.tagName.toLowerCase(); return ( - (tagName == "input" && el.type == "text") || + (tagName == "input" && element.type == "text") || tagName == "textarea" || tagName == "button" ); @@ -101,24 +124,24 @@ var audio = (function () { if (element_is_text_input(document.activeElement)) { return; } - if (app.typing || e.ctrlKey || e.altKey || e.metaKey) return; - switch (e.keyCode) { + if (app.typing || event.ctrlKey || event.altKey || event.metaKey) return; + switch (event.keyCode) { case 37: // left - if (e.shiftKey) { - el.currentTime -= 10; + if (event.shiftKey) { + audio.el.currentTime -= 10; } else { audio.prev(); } break; case 39: // right - if (e.shiftKey) { - el.currentTime += 10; + if (event.shiftKey) { + audio.el.currentTime += 10; } else { audio.next(); } break; case 32: // spacebar - e.preventDefault(); + event.preventDefault(); audio.toggle(); break; } diff --git a/public/assets/js/lib/views/details/audioPlayer.js b/public/assets/js/lib/views/details/audioPlayer.js new file mode 100644 index 0000000..700daa5 --- /dev/null +++ b/public/assets/js/lib/views/details/audioPlayer.js @@ -0,0 +1,123 @@ +const AudioPlayer = View.extend({ + el: "#audioPlayer", + + events: { + "click .icon": "toggle", + }, + + initialize: function () { + this.$title = this.$(".title"); + + this.icon_el = this.$(".icon").get(0); + this.pos_el = this.$(".pos").get(0); + this.track_el = this.$(".track").get(0); + this.dot_el = this.$(".dot").get(0); + this.time_el = this.$(".time").get(0); + + this.mousedown = this.mousedown.bind(this); + this.mousemove = this.mousemove.bind(this); + this.mouseup = this.mouseup.bind(this); + + if (is_mobile) { + this.pos_el.addEventListener("touchstart", (e) => + this.mousedown(e.touches[0]) + ); + this.pos_el.addEventListener("touchmove", (e) => + this.mousemove(this.e.touches[0]) + ); + window.addEventListener("touchend", this.mouseup.bind); + } else { + this.pos_el.addEventListener("mousedown", this.mousedown); + this.pos_el.addEventListener("mousemove", this.mousemove); + window.addEventListener("mouseup", this.mouseup); + } + this.down = false; + this.mousex = 0; + + audio.listen(this); + audio.el.addEventListener("timeupdate", this.onTimeUpdate.bind(this)); + }, + + /** + * Mouse movements + * */ + mousedown: function (e) { + e.preventDefault && e.preventDefault(); + const track_left = this.pos_el.offsetLeft; + this.down = true; + this.mousex = (e.pageX - track_left) / this.track_el.offsetWidth; + }, + + mousemove: function (e, isTouch) { + if (!this.down) return; + const track_left = this.pos_el.offsetLeft; + this.mousex = Math.min( + Math.max(0, (e.pageX - track_left) / this.track_el.offsetWidth), + 1 + ); + this.dot_el.style.transform = + "translateX(" + this.mousex * this.track_el.offsetWidth + "px)"; + }, + + mouseup: function (e) { + if (!this.down) return; + this.down = false; + var t = this.mousex * (audio.el.duration || 1); + audio.el.currentTime = Math.round(t); + this.dot_el.style.transform = + "translateX(" + this.mousex * this.track_el.offsetWidth + "px)"; + }, + + toggle: function () { + audio.toggle(); + }, + + /** + * Receiving play events + */ + onPlay: function (element) { + if (!this.active) { + this.active = true; + this.$el.addClass("active"); + } + // if (index === music.length) return stop(); + if (!this.icon_el.classList.contains("active")) { + this.icon_el.classList.add("active"); + } + this.onTimeUpdate(); + + if (element) { + const title = element.innerText; + this.$title.html(title); + } + }, + + onPause: function () { + this.icon_el.classList.remove("active"); + }, + + onStop: function () { + this.$el.removeClass("active"); + this.active = false; + }, + + onTimeUpdate: function () { + if (this.down) { + return; + } + let { currentTime, duration } = audio.el; + let t = currentTime / (duration || 1); + this.dot_el.style.transform = + "translateX(" + this.track_el.offsetWidth * t + "px)"; + this.time_el.innerHTML = time(currentTime) + " / " + time(duration); + }, +}); + +function time(n) { + if (!n) return "0:00"; + n = Math.floor(n); + let s = n % 60; + if (s < 10) s = "0" + s; + let m = Math.floor(n / 60); + return m + ":" + s; +} diff --git a/public/assets/js/lib/views/stream/hootstream.js b/public/assets/js/lib/views/stream/hootstream.js index 6c02cc6..777489d 100644 --- a/public/assets/js/lib/views/stream/hootstream.js +++ b/public/assets/js/lib/views/stream/hootstream.js @@ -104,12 +104,14 @@ var HootStream = View.extend({ return true; } if ( - filters.images && - thread.files && - thread.files.some((file) => IMAGE_REGEXP.test(file.filename)) + filters.files && + thread.files.some((file) => !AUDIO_REGEXP.test(file.filename)) ) { return true; } + if (filters.images && thread.images.length) { + return true; + } return false; }) .map( @@ -120,18 +122,15 @@ var HootStream = View.extend({ const thread = threadLookup[thread_id]; const threadData = { ...thread, - files: - filters.images || filters.music - ? thread.files.filter((file) => { - if (AUDIO_REGEXP.test(file.filename)) { - return filters.music; - } - if (IMAGE_REGEXP.test(file.filename)) { - return filters.images; - } - return filters.files; - }) - : [], + images: filters.images ? thread.images : [], + files: filters.music + ? thread.files.filter((file) => { + if (AUDIO_REGEXP.test(file.filename)) { + return filters.music; + } + return filters.files; + }) + : [], comments: filters.hoots ? thread.comments : [], query: data.query, }; @@ -257,38 +256,34 @@ var HootStream = View.extend({ comment_count: `${thread.comment_count || 0} c.`, comment_opacity: age_opacity * get_size_opacity(thread.comment_count), }), - // this.renderImages( - // isViewingThread || postedToday ? images : images.slice(0, 6) - // ), + this.renderImages( + isViewingThread || postedToday ? images : images.slice(0, 6) + ), this.renderFiles( isViewingThread || postedToday ? files : files.slice(0, 10) ), ...this.renderHoots({ - hoots: comments - .slice(0, 1) - .map( - trimComment({ - isViewingThread, - lines: 5, - snippetSize: 512, - cropSize: 256, - }) - ), + hoots: comments.slice(0, 1).map( + trimComment({ + isViewingThread, + lines: 5, + snippetSize: 512, + cropSize: 256, + }) + ), className: "first_post", }), ...this.renderHoots({ hoots: isViewingThread || postedToday - ? comments - .slice(1) - .map( - trimComment({ - isViewingThread, - lines: 1, - snippetSize: 256, - cropSize: 128, - }) - ) + ? comments.slice(1).map( + trimComment({ + isViewingThread, + lines: 1, + snippetSize: 256, + cropSize: 128, + }) + ) : comments .slice(1) .slice(-5) @@ -305,13 +300,13 @@ var HootStream = View.extend({ ]; }, - renderImages: function (files) { - if (!files.length) { + renderImages: function (images) { + if (!images.length) { return null; } const $table = $("
"); - for (const file of files) { - const $el = this.renderFile(this.imageTemplate, file); + for (const image of images) { + const $el = this.renderFile(this.imageTemplate, image); $table.append($el); } return $table; diff --git a/views/hootstream/index.ejs b/views/hootstream/index.ejs index 165e7e7..6a98948 100644 --- a/views/hootstream/index.ejs +++ b/views/hootstream/index.ejs @@ -3,7 +3,6 @@
-
<% include ./hootform %> diff --git a/views/hootstream/templates.ejs b/views/hootstream/templates.ejs index fc4ace5..972a308 100644 --- a/views/hootstream/templates.ejs +++ b/views/hootstream/templates.ejs @@ -52,7 +52,7 @@ diff --git a/views/partials/header.ejs b/views/partials/header.ejs index 1be8e7d..969e7d4 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -6,6 +6,7 @@ + diff --git a/views/partials/player.ejs b/views/partials/player.ejs new file mode 100644 index 0000000..8e58ff1 --- /dev/null +++ b/views/partials/player.ejs @@ -0,0 +1,23 @@ +
+
+
+
+
+ The player is unloaded +
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index bf65bff..0d69bd3 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -52,6 +52,7 @@ + -- cgit v1.2.3-70-g09d2