From 950155bdd26963bac46aaedc2a3061c8b6bb04cb Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sun, 11 Mar 2018 15:43:17 +0100 Subject: [PATCH] Heavily improved audio player --- mixins/SoundTrack.pixy | 22 ++++-- pages/anime/tracks.pixy | 2 +- pages/profile/tracks.pixy | 2 +- pages/soundtrack/soundtrack.pixy | 12 +-- pages/soundtrack/soundtrack.scarlet | 13 ++-- pages/soundtracks/soundtracks.pixy | 2 +- pages/soundtracks/soundtracks.scarlet | 101 ++++++++++++++++++++++++-- scripts/Actions/Audio.ts | 50 ++++++++++--- scripts/AnimeNotifier.ts | 10 +++ 9 files changed, 177 insertions(+), 37 deletions(-) diff --git a/mixins/SoundTrack.pixy b/mixins/SoundTrack.pixy index cfe44600..3e58399f 100644 --- a/mixins/SoundTrack.pixy +++ b/mixins/SoundTrack.pixy @@ -2,23 +2,33 @@ component SoundTrack(track *arn.SoundTrack) SoundTrackMedia(track, track.Media[0]) component SoundTrackMedia(track *arn.SoundTrack, media *arn.ExternalMedia) - .sound-track.mountable(id=track.ID) + .soundtrack.mountable(id=track.ID) SoundTrackContent(track, media) SoundTrackFooter(track) component SoundTrackContent(track *arn.SoundTrack, media *arn.ExternalMedia) - .sound-track-content + .soundtrack-content if track.MainAnime() != nil - a.sound-track-anime-link.ajax(href="/anime/" + track.MainAnime().ID) - img.sound-track-anime-image.lazy(data-src=track.MainAnime().Image("medium"), data-webp="true", alt=track.MainAnime().Title.Canonical, title=track.MainAnime().Title.Canonical) + a.soundtrack-anime-link.ajax(href="/anime/" + track.MainAnime().ID) + img.soundtrack-anime-image.lazy(data-src=track.MainAnime().Image("medium"), data-webp="true", alt=track.MainAnime().Title.Canonical, title=track.MainAnime().Title.Canonical) if track.File != "" && media.Service == "Youtube" - img.soundtrack-image.action.lazy(data-action="playAudio", data-trigger="click", data-audio-src="https://notify.moe/audio/" + track.File, data-src="https://img.youtube.com/vi/" + media.ServiceID + "/maxresdefault.jpg", alt=track.Title) + .soundtrack-media + .soundtrack-play-area.action(data-action="toggleAudio", data-trigger="click", data-audio-src="https://notify.moe/audio/" + track.File, data-soundtrack-id=track.ID) + img.soundtrack-image.lazy(data-src="https://img.youtube.com/vi/" + media.ServiceID + "/maxresdefault.jpg", alt=track.Title) + button.soundtrack-play-button + RawIcon("play") + + .soundtrack-visualizer + .visualizer-box.visualizer-box-1 + .visualizer-box.visualizer-box-2 + .visualizer-box.visualizer-box-3 + else ExternalMedia(media) component SoundTrackFooter(track *arn.SoundTrack) - .sound-track-footer + .soundtrack-footer if track.Title == "" a.ajax(href=track.Link() + "/edit") untitled else diff --git a/pages/anime/tracks.pixy b/pages/anime/tracks.pixy index 72b93e15..fba189f3 100644 --- a/pages/anime/tracks.pixy +++ b/pages/anime/tracks.pixy @@ -9,4 +9,4 @@ component AnimeTracks(anime *arn.Anime, tracks []*arn.SoundTrack) .anime-soundtrack.mountable(data-mountable-type="track") .video-container iframe.video.lazy(data-src=track.Media[0].EmbedLink(), allowfullscreen="allowfullscreen") - a.sound-track-footer.ajax(href=track.Link())= track.Title \ No newline at end of file + a.soundtrack-footer.ajax(href=track.Link())= track.Title \ No newline at end of file diff --git a/pages/profile/tracks.pixy b/pages/profile/tracks.pixy index 894f06f6..1b4d3f9f 100644 --- a/pages/profile/tracks.pixy +++ b/pages/profile/tracks.pixy @@ -6,7 +6,7 @@ component TrackList(tracks []*arn.SoundTrack, viewUser *arn.User, user *arn.User if len(tracks) == 0 p.no-data.mountable= viewUser.Nick + " hasn't added any tracks yet." else - .sound-tracks + .soundtracks each track in tracks SoundTrack(track) \ No newline at end of file diff --git a/pages/soundtrack/soundtrack.pixy b/pages/soundtrack/soundtrack.pixy index f1017f6f..0c07dff6 100644 --- a/pages/soundtrack/soundtrack.pixy +++ b/pages/soundtrack/soundtrack.pixy @@ -1,17 +1,17 @@ component Track(track *arn.SoundTrack, user *arn.User) SoundTrackTabs(track, user) - .sound-track-full-page + .soundtrack-full-page if track.Title == "" h1.mountable untitled else h1.mountable= track.Title - .widget-form.sound-track-media-list + .widget-form.soundtrack-media-list each media in track.Media .widget.mountable h3.widget-title= media.Service - .sound-track-media.video-container + .soundtrack-media.video-container iframe.lazy.video(data-src=media.EmbedLink(), allowfullscreen="allowfullscreen") if user != nil && media.Service == "Youtube" && track.File != "" @@ -23,10 +23,10 @@ component Track(track *arn.SoundTrack, user *arn.User) .widget.mountable h3.widget-title Anime - .sound-track-anime-list + .soundtrack-anime-list each anime in track.Anime() - a.sound-track-anime-list-item.ajax(href=anime.Link(), title=anime.Title.ByUser(user)) - img.sound-track-anime-list-item-image.lazy(data-src=anime.Image("small"), data-webp="true", alt=anime.Title.ByUser(user)) + a.soundtrack-anime-list-item.ajax(href=anime.Link(), title=anime.Title.ByUser(user)) + img.soundtrack-anime-list-item-image.lazy(data-src=anime.Image("small"), data-webp="true", alt=anime.Title.ByUser(user)) if len(track.Beatmaps()) > 0 .widget.mountable diff --git a/pages/soundtrack/soundtrack.scarlet b/pages/soundtrack/soundtrack.scarlet index a1ad6dc2..e5f3e6b9 100644 --- a/pages/soundtrack/soundtrack.scarlet +++ b/pages/soundtrack/soundtrack.scarlet @@ -1,15 +1,18 @@ -.sound-track-media-list +.soundtrack-media-list vertical -.sound-track-media +.soundtrack-media + width 100% + position relative + iframe width 100% -.sound-track-anime-list +.soundtrack-anime-list horizontal-wrap -.sound-track-anime-list-item +.soundtrack-anime-list-item anime-mini-item -.sound-track-anime-list-item-image +.soundtrack-anime-list-item-image anime-mini-item-image \ No newline at end of file diff --git a/pages/soundtracks/soundtracks.pixy b/pages/soundtracks/soundtracks.pixy index e09d9844..a3b55e68 100644 --- a/pages/soundtracks/soundtracks.pixy +++ b/pages/soundtracks/soundtracks.pixy @@ -14,7 +14,7 @@ component SoundTracks(tracks []*arn.SoundTrack, loadMoreIndex int, user *arn.Use Icon("pencil") span Edit draft - #load-more-target.sound-tracks + #load-more-target.soundtracks SoundTracksScrollable(tracks, user) if loadMoreIndex != 0 diff --git a/pages/soundtracks/soundtracks.scarlet b/pages/soundtracks/soundtracks.scarlet index a98d01cd..0e6147a5 100644 --- a/pages/soundtracks/soundtracks.scarlet +++ b/pages/soundtracks/soundtracks.scarlet @@ -1,14 +1,14 @@ -.sound-tracks +.soundtracks horizontal-wrap justify-content space-around -.sound-track +.soundtrack vertical flex 1 flex-basis 500px padding 1rem -.sound-track-content +.soundtrack-content horizontal border-radius 3px overflow hidden @@ -21,8 +21,95 @@ object-fit cover width 100% height 200px + filter brightness(100%) + transition filter 250ms ease -.sound-track-footer +.soundtrack-play-button + position absolute + top 50% + left 50% + transform translate(-50%, -50%) + border-radius 50% + width 92px + height 92px + font-size 3rem + color rgba(0, 0, 0, 0.15) + background rgba(255, 255, 255, 0.9) + pointer-events none + + .icon-play + transform translateX(27%) + +.soundtrack-visualizer + horizontal + justify-content center + align-items center + pointer-events none + position absolute + top 0 + left 0 + width 100% + height 100% + opacity 0 + transition opacity 250ms ease + +.visualizer-box + width 3px + height 15px + margin 0.2rem + border-radius 1px + transition all 250ms linear + animation scale-vertically 300ms infinite ease, change-color 1s infinite ease + animation-direction alternate + +.visualizer-box-1 + animation-delay 0 + +.visualizer-box-2 + animation-delay 100ms + +.visualizer-box-3 + animation-delay 200ms + +animation scale-vertically + 0% + transform scaleY(0.3) + opacity 0.8 + 100% + transform scaleY(1) + opacity 1 + +animation change-color + 0% + background-color hsl(45, 100%, 68%) + 100% + background-color hsl(235, 100%, 68%) + +.soundtrack-play-area + position absolute + top 0 + left 0 + width 100% + height 100% + cursor pointer + transition opacity 250ms ease + + &.playing + .soundtrack-play-button + opacity 0 + + .soundtrack-visualizer + opacity 1 + + .soundtrack-image + filter brightness(0) + + :hover + .soundtrack-play-button + color button-hover-color + background button-hover-background + +.soundtrack-footer text-align center margin-bottom 1rem margin-top 0.4rem @@ -31,13 +118,13 @@ span opacity 0.65 -.sound-track-anime-link +.soundtrack-anime-link display none > 800px - .sound-track-anime-link + .soundtrack-anime-link display block -.sound-track-anime-image +.soundtrack-anime-image width 142px height 200px \ No newline at end of file diff --git a/scripts/Actions/Audio.ts b/scripts/Actions/Audio.ts index 23f0536b..68a186df 100644 --- a/scripts/Actions/Audio.ts +++ b/scripts/Actions/Audio.ts @@ -8,23 +8,22 @@ var audioPlayerPlay = document.getElementById("audio-player-play") var audioPlayerPause = document.getElementById("audio-player-pause") // Play audio file -export function playAudio(arn: AnimeNotifier, button: HTMLButtonElement) { +export function playAudio(arn: AnimeNotifier, element: HTMLElement) { if(!audioContext) { audioContext = new AudioContext() } playID++ - // Stop existing audioNode - if(audioNode) { - audioNode.stop() - audioNode.disconnect() - audioNode = null - } + // Stop current track + stopAudio(arn) + + arn.currentSoundTrackId = element.dataset.soundtrackId + element.classList.add("playing") // Request let request = new XMLHttpRequest() - request.open("GET", button.dataset.audioSrc, true) + request.open("GET", element.dataset.audioSrc, true) request.responseType = "arraybuffer" request.onload = () => { @@ -38,8 +37,7 @@ export function playAudio(arn: AnimeNotifier, button: HTMLButtonElement) { audioNode.onended = (event: MediaStreamErrorEvent) => { if(currentPlayCount === playID) { - audioPlayer.classList.add("fade-out") - audioNode.disconnect() + stopAudio(arn) } } }, console.error) @@ -53,6 +51,38 @@ export function playAudio(arn: AnimeNotifier, button: HTMLButtonElement) { audioPlayerPause.classList.remove("fade-out") } +// Stop audio +export function stopAudio(arn: AnimeNotifier) { + if(!audioNode) { + return + } + + audioNode.stop() + audioNode.disconnect() + audioNode = null + + arn.currentSoundTrackId = undefined + + // Remove CSS class "playing" + let playingElements = document.getElementsByClassName("playing") + + for(let playing of playingElements) { + playing.classList.remove("playing") + } + + // Fade out sidebar player + audioPlayer.classList.add("fade-out") +} + +// Toggle audio +export function toggleAudio(arn: AnimeNotifier, element: HTMLElement) { + if(!arn.currentSoundTrackId) { + playAudio(arn, element) + } else { + stopAudio(arn) + } +} + // Pause audio export function pauseAudio(arn: AnimeNotifier, button: HTMLButtonElement) { if(!audioNode) { diff --git a/scripts/AnimeNotifier.ts b/scripts/AnimeNotifier.ts index 26aa69f3..1399e291 100644 --- a/scripts/AnimeNotifier.ts +++ b/scripts/AnimeNotifier.ts @@ -32,6 +32,7 @@ export class AnimeNotifier { mainPageLoaded: boolean isLoading: boolean lastReloadContentPath: string + currentSoundTrackId: string elementFound: MutationQueue elementNotFound: MutationQueue @@ -158,6 +159,7 @@ export class AnimeNotifier { Promise.resolve().then(() => this.lazyLoad()), Promise.resolve().then(() => this.displayLocalDates()), Promise.resolve().then(() => this.setSelectBoxValue()), + Promise.resolve().then(() => this.markPlayingSoundTrack()), Promise.resolve().then(() => this.assignActions()), Promise.resolve().then(() => this.updatePushUI()), Promise.resolve().then(() => this.dragAndDrop()), @@ -357,6 +359,14 @@ export class AnimeNotifier { } } + markPlayingSoundTrack() { + for(let element of findAll("soundtrack-play-area")) { + if(element.dataset.soundtrackId === this.currentSoundTrackId) { + element.classList.add("playing") + } + } + } + setSelectBoxValue() { for(let element of document.getElementsByTagName("select")) { element.value = element.getAttribute("value")