Heavily improved audio player

This commit is contained in:
Eduard Urbach 2018-03-11 15:43:17 +01:00
parent 26703c467a
commit 950155bdd2
9 changed files with 177 additions and 37 deletions

View File

@ -2,23 +2,33 @@ component SoundTrack(track *arn.SoundTrack)
SoundTrackMedia(track, track.Media[0]) SoundTrackMedia(track, track.Media[0])
component SoundTrackMedia(track *arn.SoundTrack, media *arn.ExternalMedia) component SoundTrackMedia(track *arn.SoundTrack, media *arn.ExternalMedia)
.sound-track.mountable(id=track.ID) .soundtrack.mountable(id=track.ID)
SoundTrackContent(track, media) SoundTrackContent(track, media)
SoundTrackFooter(track) SoundTrackFooter(track)
component SoundTrackContent(track *arn.SoundTrack, media *arn.ExternalMedia) component SoundTrackContent(track *arn.SoundTrack, media *arn.ExternalMedia)
.sound-track-content .soundtrack-content
if track.MainAnime() != nil if track.MainAnime() != nil
a.sound-track-anime-link.ajax(href="/anime/" + track.MainAnime().ID) a.soundtrack-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) 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" 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 else
ExternalMedia(media) ExternalMedia(media)
component SoundTrackFooter(track *arn.SoundTrack) component SoundTrackFooter(track *arn.SoundTrack)
.sound-track-footer .soundtrack-footer
if track.Title == "" if track.Title == ""
a.ajax(href=track.Link() + "/edit") untitled a.ajax(href=track.Link() + "/edit") untitled
else else

View File

@ -9,4 +9,4 @@ component AnimeTracks(anime *arn.Anime, tracks []*arn.SoundTrack)
.anime-soundtrack.mountable(data-mountable-type="track") .anime-soundtrack.mountable(data-mountable-type="track")
.video-container .video-container
iframe.video.lazy(data-src=track.Media[0].EmbedLink(), allowfullscreen="allowfullscreen") iframe.video.lazy(data-src=track.Media[0].EmbedLink(), allowfullscreen="allowfullscreen")
a.sound-track-footer.ajax(href=track.Link())= track.Title a.soundtrack-footer.ajax(href=track.Link())= track.Title

View File

@ -6,7 +6,7 @@ component TrackList(tracks []*arn.SoundTrack, viewUser *arn.User, user *arn.User
if len(tracks) == 0 if len(tracks) == 0
p.no-data.mountable= viewUser.Nick + " hasn't added any tracks yet." p.no-data.mountable= viewUser.Nick + " hasn't added any tracks yet."
else else
.sound-tracks .soundtracks
each track in tracks each track in tracks
SoundTrack(track) SoundTrack(track)

View File

@ -1,17 +1,17 @@
component Track(track *arn.SoundTrack, user *arn.User) component Track(track *arn.SoundTrack, user *arn.User)
SoundTrackTabs(track, user) SoundTrackTabs(track, user)
.sound-track-full-page .soundtrack-full-page
if track.Title == "" if track.Title == ""
h1.mountable untitled h1.mountable untitled
else else
h1.mountable= track.Title h1.mountable= track.Title
.widget-form.sound-track-media-list .widget-form.soundtrack-media-list
each media in track.Media each media in track.Media
.widget.mountable .widget.mountable
h3.widget-title= media.Service h3.widget-title= media.Service
.sound-track-media.video-container .soundtrack-media.video-container
iframe.lazy.video(data-src=media.EmbedLink(), allowfullscreen="allowfullscreen") iframe.lazy.video(data-src=media.EmbedLink(), allowfullscreen="allowfullscreen")
if user != nil && media.Service == "Youtube" && track.File != "" if user != nil && media.Service == "Youtube" && track.File != ""
@ -23,10 +23,10 @@ component Track(track *arn.SoundTrack, user *arn.User)
.widget.mountable .widget.mountable
h3.widget-title Anime h3.widget-title Anime
.sound-track-anime-list .soundtrack-anime-list
each anime in track.Anime() each anime in track.Anime()
a.sound-track-anime-list-item.ajax(href=anime.Link(), title=anime.Title.ByUser(user)) a.soundtrack-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)) 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 if len(track.Beatmaps()) > 0
.widget.mountable .widget.mountable

View File

@ -1,15 +1,18 @@
.sound-track-media-list .soundtrack-media-list
vertical vertical
.sound-track-media .soundtrack-media
width 100%
position relative
iframe iframe
width 100% width 100%
.sound-track-anime-list .soundtrack-anime-list
horizontal-wrap horizontal-wrap
.sound-track-anime-list-item .soundtrack-anime-list-item
anime-mini-item anime-mini-item
.sound-track-anime-list-item-image .soundtrack-anime-list-item-image
anime-mini-item-image anime-mini-item-image

View File

@ -14,7 +14,7 @@ component SoundTracks(tracks []*arn.SoundTrack, loadMoreIndex int, user *arn.Use
Icon("pencil") Icon("pencil")
span Edit draft span Edit draft
#load-more-target.sound-tracks #load-more-target.soundtracks
SoundTracksScrollable(tracks, user) SoundTracksScrollable(tracks, user)
if loadMoreIndex != 0 if loadMoreIndex != 0

View File

@ -1,14 +1,14 @@
.sound-tracks .soundtracks
horizontal-wrap horizontal-wrap
justify-content space-around justify-content space-around
.sound-track .soundtrack
vertical vertical
flex 1 flex 1
flex-basis 500px flex-basis 500px
padding 1rem padding 1rem
.sound-track-content .soundtrack-content
horizontal horizontal
border-radius 3px border-radius 3px
overflow hidden overflow hidden
@ -21,8 +21,95 @@
object-fit cover object-fit cover
width 100% width 100%
height 200px 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 text-align center
margin-bottom 1rem margin-bottom 1rem
margin-top 0.4rem margin-top 0.4rem
@ -31,13 +118,13 @@
span span
opacity 0.65 opacity 0.65
.sound-track-anime-link .soundtrack-anime-link
display none display none
> 800px > 800px
.sound-track-anime-link .soundtrack-anime-link
display block display block
.sound-track-anime-image .soundtrack-anime-image
width 142px width 142px
height 200px height 200px

View File

@ -8,23 +8,22 @@ var audioPlayerPlay = document.getElementById("audio-player-play")
var audioPlayerPause = document.getElementById("audio-player-pause") var audioPlayerPause = document.getElementById("audio-player-pause")
// Play audio file // Play audio file
export function playAudio(arn: AnimeNotifier, button: HTMLButtonElement) { export function playAudio(arn: AnimeNotifier, element: HTMLElement) {
if(!audioContext) { if(!audioContext) {
audioContext = new AudioContext() audioContext = new AudioContext()
} }
playID++ playID++
// Stop existing audioNode // Stop current track
if(audioNode) { stopAudio(arn)
audioNode.stop()
audioNode.disconnect() arn.currentSoundTrackId = element.dataset.soundtrackId
audioNode = null element.classList.add("playing")
}
// Request // Request
let request = new XMLHttpRequest() let request = new XMLHttpRequest()
request.open("GET", button.dataset.audioSrc, true) request.open("GET", element.dataset.audioSrc, true)
request.responseType = "arraybuffer" request.responseType = "arraybuffer"
request.onload = () => { request.onload = () => {
@ -38,8 +37,7 @@ export function playAudio(arn: AnimeNotifier, button: HTMLButtonElement) {
audioNode.onended = (event: MediaStreamErrorEvent) => { audioNode.onended = (event: MediaStreamErrorEvent) => {
if(currentPlayCount === playID) { if(currentPlayCount === playID) {
audioPlayer.classList.add("fade-out") stopAudio(arn)
audioNode.disconnect()
} }
} }
}, console.error) }, console.error)
@ -53,6 +51,38 @@ export function playAudio(arn: AnimeNotifier, button: HTMLButtonElement) {
audioPlayerPause.classList.remove("fade-out") 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 // Pause audio
export function pauseAudio(arn: AnimeNotifier, button: HTMLButtonElement) { export function pauseAudio(arn: AnimeNotifier, button: HTMLButtonElement) {
if(!audioNode) { if(!audioNode) {

View File

@ -32,6 +32,7 @@ export class AnimeNotifier {
mainPageLoaded: boolean mainPageLoaded: boolean
isLoading: boolean isLoading: boolean
lastReloadContentPath: string lastReloadContentPath: string
currentSoundTrackId: string
elementFound: MutationQueue elementFound: MutationQueue
elementNotFound: MutationQueue elementNotFound: MutationQueue
@ -158,6 +159,7 @@ export class AnimeNotifier {
Promise.resolve().then(() => this.lazyLoad()), Promise.resolve().then(() => this.lazyLoad()),
Promise.resolve().then(() => this.displayLocalDates()), Promise.resolve().then(() => this.displayLocalDates()),
Promise.resolve().then(() => this.setSelectBoxValue()), Promise.resolve().then(() => this.setSelectBoxValue()),
Promise.resolve().then(() => this.markPlayingSoundTrack()),
Promise.resolve().then(() => this.assignActions()), Promise.resolve().then(() => this.assignActions()),
Promise.resolve().then(() => this.updatePushUI()), Promise.resolve().then(() => this.updatePushUI()),
Promise.resolve().then(() => this.dragAndDrop()), 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() { setSelectBoxValue() {
for(let element of document.getElementsByTagName("select")) { for(let element of document.getElementsByTagName("select")) {
element.value = element.getAttribute("value") element.value = element.getAttribute("value")