Heavily improved audio player
This commit is contained in:
parent
26703c467a
commit
950155bdd2
@ -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
|
||||||
|
@ -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
|
@ -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)
|
||||||
|
|
@ -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
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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
|
@ -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) {
|
||||||
|
@ -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")
|
||||||
|
Loading…
Reference in New Issue
Block a user