From f125c7d968efdca3c64de54ee9e65d0e0b42f142 Mon Sep 17 00:00:00 2001 From: Dave Rupert Date: Tue, 19 Jan 2021 22:25:04 -0600 Subject: [PATCH 1/2] First psss at lit-version --- demo.html | 10 -- index.html | 12 ++ package.json | 21 +++ podcast-player.html | 215 ----------------------------- podcast-player.js | 322 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 355 insertions(+), 225 deletions(-) delete mode 100644 demo.html create mode 100644 index.html create mode 100644 package.json delete mode 100644 podcast-player.html create mode 100644 podcast-player.js diff --git a/demo.html b/demo.html deleted file mode 100644 index aa24501..0000000 --- a/demo.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/index.html b/index.html new file mode 100644 index 0000000..6853f69 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + podcast-player demo + + + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..6320409 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "podcast-player", + "version": "1.0.0", + "description": "![Screenshot of podcast-player](https://s3.amazonaws.com/f.cl.ly/items/3Q47193Z0f00142R0O42/1d0pzyOUeVH2-d1m77Vxx9QkL0vexZ5bJNbyEGRCfbY.png)", + "main": "podcast-player.js", + "scripts": { + "start": "npx http-server", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/davatron5000/podcast-player.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/davatron5000/podcast-player/issues" + }, + "homepage": "https://github.com/davatron5000/podcast-player#readme" +} diff --git a/podcast-player.html b/podcast-player.html deleted file mode 100644 index 3ff5e6e..0000000 --- a/podcast-player.html +++ /dev/null @@ -1,215 +0,0 @@ - - diff --git a/podcast-player.js b/podcast-player.js new file mode 100644 index 0000000..56679cc --- /dev/null +++ b/podcast-player.js @@ -0,0 +1,322 @@ +import { + LitElement, + html, + css, +} from "https://unpkg.com/lit-element@2.2.1/lit-element.js?module"; + +class PodcastPlayer extends LitElement { + static get properties() { + return { + currentTime: { type: String }, + currentSpeedIdx: { type: Number }, + duration: { type: String }, + }; + } + + // TODO: Make styles optional? Easily overrideable? + static get styles() { + return css` + .sr-only { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; + } + + .podcast-player { + display: flex; + gap: 0.5rem; + width:100%; + align-items: center; + grid-gap: 0.5rem; + } + + .podcast-player .progress { + flex: 1; + } + + .podcast-player button svg { + width: 0.8em; + height: 0.8em; + } + + /* Speed */ + .podcast-player .button-speed:after { + content: 'x'; + } + + /* Play/Pause */ + .button-play .pause { + display: none; + } + + :host(.is-playing) .button-play .pause { + display: inline; + } + :host(.is-playing) .button-play .play { + display: none; + } + + /* Mute/Unmute */ + .button-mute .muted { + display: none; + } + + :host(.is-muted) .button-mute .muted { + display: inline; + } + + :host(.is-muted) .button-mute .unmuted { + display: none; + } + `; + } + + constructor() { + super(); + + // HTMLAudioElement + this.audio = this.querySelector("audio"); + this.audio.controls = false; // remove controls if it has 'em + + this.speeds = [1, 1.25, 1.5, 1.75, 2]; + this.currentSpeedIdx = 0; + this.currentTime = 0; + this.duration = 0; + + this.audio.addEventListener("timeupdate", this.handleTimeUpdate.bind(this)); + this.audio.addEventListener( + "loadedmetadata", + this.handleLoadedMetadata.bind(this) + ); + this.audio.addEventListener('ended', this.stop.bind(this)) + + window.addEventListener( + "DOMContentLoaded", + this.timeJump.bind(this), + false + ); + window.addEventListener("hashchange", this.timeJump.bind(this), false); + } + + handleLoadedMetadata() { + this.duration = this.audio.duration; + } + + handleTimeUpdate(e) { + this.currentTime = this.audio.currentTime; + } + + timeJump(event) { + let params = new URLSearchParams(window.location.hash.substring(1)); + let t = params.get("t") || 0; + + console.log(window, params, t, event); + + var timestamp = this.parseTime(t); + + if (t) { + // Preload the media + this.audio.setAttribute("preload", "true"); + // Set the current time. Will update if playing. Will fail if paused. + this.audio.currentTime = timestamp; + // If the media is able to play, play. + this.audio.addEventListener( + "canplay", + () => { + /* only start the player if it is not already playing */ + if (!this.audio.paused) { + return false; + } + + this.audio.currentTime = timestamp; + this.audio.play(); + this.classList.add("is-playing"); + }, + false + ); + } + } + + parseTime(str) { + var plain = /^\d+(\.\d+)?$/g, + npt = /^(?:npt:)?(?:(?:(\d+):)?(\d\d?):)?(\d\d?)(\.\d+)?$/, + quirks = /^(?:(\d\d?)[hH])?(?:(\d\d?)[mM])?(\d\d?)[sS]$/, + match; + if (plain.test(str)) { + return parseFloat(str); + } + match = npt.exec(str) || quirks.exec(str); + if (match) { + return ( + 3600 * (parseInt(match[1], 10) || 0) + + 60 * (parseInt(match[2], 10) || 0) + + parseInt(match[3], 10) + + (parseFloat(match[4]) || 0) + ); + } + return 0; + } + + toHHMMSS(totalsecs) { + var sec_num = parseInt(totalsecs, 10); // don't forget the second param + var hours = Math.floor(sec_num / 3600); + var minutes = Math.floor((sec_num - hours * 3600) / 60); + var seconds = sec_num - hours * 3600 - minutes * 60; + + if (hours < 10) { + hours = "0" + hours; + } + if (minutes < 10) { + minutes = "0" + minutes; + } + if (seconds < 10) { + seconds = "0" + seconds; + } + + hours = hours > 0 ? hours + ":" : ""; + minutes = minutes + ":"; + + var time = hours + minutes + seconds; + return time; + } + + changeSpeed() { + this.currentSpeedIdx = + this.currentSpeedIdx + 1 < this.speeds.length + ? this.currentSpeedIdx + 1 + : 0; + this.audio.playbackRate = this.speeds[this.currentSpeedIdx]; + } + + mute() { + this.audio.muted = !this.audio.muted; + this.classList.toggle("is-muted", this.audio.muted); + } + + play() { + if (this.audio.paused) { + console.log(this.currentTime, this.audio.currentTime); + this.audio.play(); + } else { + this.audio.pause(); + } + this.classList.toggle("is-playing", !this.audio.paused); + } + + stop() { + this.classList.toggle("is-playing", !this.audio.paused); + } + + rewind() { + this.audio.currentTime -= 30; + } + + ff() { + this.audio.currentTime += 30; + } + + seek(e) { + this.audio.currentTime = e.target.value; + } + + render() { + // TODO: Make icons, button text, button labels overrideable? + return html` + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ Current Time + ${this.toHHMMSS(this.currentTime)} +
+ + + +
+ Duration + ${this.toHHMMSS(this.duration)} +
+ + + + +
+ `; + } +} + +customElements.define("podcast-player", PodcastPlayer); From 2b315e2d3e6bc47972ddd396854abeb67ee95f72 Mon Sep 17 00:00:00 2001 From: Matsuuu Date: Wed, 20 Jan 2021 22:25:52 +0200 Subject: [PATCH 2/2] Move from classes to attributes --- podcast-player.js | 54 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/podcast-player.js b/podcast-player.js index 56679cc..bff9035 100644 --- a/podcast-player.js +++ b/podcast-player.js @@ -10,13 +10,15 @@ class PodcastPlayer extends LitElement { currentTime: { type: String }, currentSpeedIdx: { type: Number }, duration: { type: String }, + playing: { type: Boolean, reflect: true }, + muted: { type: Boolean, reflect: true }, }; } // TODO: Make styles optional? Easily overrideable? static get styles() { return css` - .sr-only { + .sr-only { clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; @@ -26,10 +28,10 @@ class PodcastPlayer extends LitElement { width: 1px; } - .podcast-player { + .podcast-player { display: flex; gap: 0.5rem; - width:100%; + width: 100%; align-items: center; grid-gap: 0.5rem; } @@ -45,7 +47,7 @@ class PodcastPlayer extends LitElement { /* Speed */ .podcast-player .button-speed:after { - content: 'x'; + content: "x"; } /* Play/Pause */ @@ -53,10 +55,10 @@ class PodcastPlayer extends LitElement { display: none; } - :host(.is-playing) .button-play .pause { + :host([playing]) .button-play .pause { display: inline; } - :host(.is-playing) .button-play .play { + :host([playing]) .button-play .play { display: none; } @@ -65,11 +67,11 @@ class PodcastPlayer extends LitElement { display: none; } - :host(.is-muted) .button-mute .muted { + :host([muted]) .button-mute .muted { display: inline; } - :host(.is-muted) .button-mute .unmuted { + :host([muted]) .button-mute .unmuted { display: none; } `; @@ -86,13 +88,15 @@ class PodcastPlayer extends LitElement { this.currentSpeedIdx = 0; this.currentTime = 0; this.duration = 0; + this.playing = false; + this.muted = false; this.audio.addEventListener("timeupdate", this.handleTimeUpdate.bind(this)); this.audio.addEventListener( "loadedmetadata", this.handleLoadedMetadata.bind(this) ); - this.audio.addEventListener('ended', this.stop.bind(this)) + this.audio.addEventListener("ended", this.stop.bind(this)); window.addEventListener( "DOMContentLoaded", @@ -134,7 +138,7 @@ class PodcastPlayer extends LitElement { this.audio.currentTime = timestamp; this.audio.play(); - this.classList.add("is-playing"); + this.playing = true; }, false ); @@ -194,7 +198,7 @@ class PodcastPlayer extends LitElement { mute() { this.audio.muted = !this.audio.muted; - this.classList.toggle("is-muted", this.audio.muted); + this.muted = this.audio.muted; } play() { @@ -204,11 +208,11 @@ class PodcastPlayer extends LitElement { } else { this.audio.pause(); } - this.classList.toggle("is-playing", !this.audio.paused); + this.playing = !this.audio.paused; } stop() { - this.classList.toggle("is-playing", !this.audio.paused); + this.playing = !this.audio.paused; } rewind() { @@ -271,7 +275,12 @@ class PodcastPlayer extends LitElement { 30
<<— - @@ -287,7 +296,9 @@ class PodcastPlayer extends LitElement {
Current Time - ${this.toHHMMSS(this.currentTime)} + ${this.toHHMMSS(this.currentTime)}
${this.toHHMMSS(this.duration)} - -