A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Tweak last.fm integration

+59 -46
+20 -10
src/Applications/UI.elm
··· 303 303 -- Last.fm 304 304 ----------------------------------------- 305 305 | GotLastFmSession (Result Http.Error String) 306 - | Scrobble { duration : Float, timestamp : Int, trackId : String } 306 + | Scrobble { duration : Int, timestamp : Int, trackId : String } 307 307 ----------------------------------------- 308 308 -- Page Transitions 309 309 ----------------------------------------- ··· 597 597 returnWithModel model (Ports.play ()) 598 598 599 599 SetAudioDuration duration -> 600 - return { model | audioDuration = duration } 600 + (case model.tracks.nowPlaying of 601 + Just ( _, track ) -> 602 + { duration = round duration 603 + , msg = Bypass 604 + , track = track 605 + } 606 + |> LastFm.nowPlaying model.lastFm 607 + |> returnWithCommand 608 + 609 + Nothing -> 610 + return 611 + ) 612 + { model | audioDuration = duration } 601 613 602 614 SetAudioHasStalled hasStalled -> 603 615 return { model | audioHasStalled = hasStalled } ··· 809 821 Just ( _, track ) -> 810 822 if trackId == track.id then 811 823 ( model 812 - , LastFm.scrobble model.lastFm duration timestamp track Bypass 824 + , LastFm.scrobble model.lastFm 825 + { duration = duration 826 + , msg = Bypass 827 + , timestamp = timestamp 828 + , track = track 829 + } 813 830 ) 814 831 815 832 else ··· 1392 1409 model 1393 1410 |> update (TracksMsg <| Tracks.SetNowPlaying nowPlaying) 1394 1411 |> addCommand portCmd 1395 - |> (case nowPlaying of 1396 - Just identifiedTrack -> 1397 - addCommand (LastFm.nowPlaying model.lastFm identifiedTrack Bypass) 1398 - 1399 - Nothing -> 1400 - identity 1401 - ) 1402 1412 1403 1413 AddToQueue { inFront, tracks } -> 1404 1414 (if inFront then
+1 -1
src/Applications/UI/Ports.elm
··· 73 73 port requestStop : (() -> msg) -> Sub msg 74 74 75 75 76 - port scrobble : ({ duration : Float, timestamp : Int, trackId : String } -> msg) -> Sub msg 76 + port scrobble : ({ duration : Int, timestamp : Int, trackId : String } -> msg) -> Sub msg 77 77 78 78 79 79 port setAudioPosition : (Float -> msg) -> Sub msg
+21 -24
src/Javascript/audio-engine.js
··· 361 361 return this.app.ports.setAudioPosition.send(0) 362 362 } 363 363 364 - setDurationIfNecessary(node, this.app) 364 + setDurationIfNecessary.call(this, node) 365 365 this.app.ports.setAudioPosition.send(node.currentTime) 366 366 367 367 if (navigator.mediaSession && navigator.mediaSession.setPositionState) { ··· 412 412 413 413 function audioPlayEvent(event) { 414 414 this.app.ports.setAudioIsPlaying.send(true) 415 - 416 - if (navigator.mediaSession) { 417 - navigator.mediaSession.playbackState = "playing" 418 - } 419 - 420 - if (event.target.duration && event.target.duration >= 30) { 421 - const timestamp = Math.floor( Date.now() / 1000 ) 422 - const scrobbleTimeoutDuration = Math.min(240 + 0.5, event.target.duration / 1.95) 423 - 424 - this.scrobbleTimeout = setTimeout(_ => { 425 - this.app.ports.scrobble.send({ 426 - duration: event.target.duration, 427 - timestamp: timestamp, 428 - trackId: event.target.getAttribute("rel") 429 - }) 430 - }, scrobbleTimeoutDuration * 1000) 431 - } 415 + if (navigator.mediaSession) navigator.mediaSession.playbackState = "playing" 432 416 } 433 417 434 418 ··· 439 423 440 424 441 425 function audioCanPlayEvent(event) { 442 - setDurationIfNecessary(event.target, this.app) 426 + setDurationIfNecessary.call(this, event.target) 443 427 } 444 428 445 429 ··· 496 480 let lastSetDuration = 0 497 481 498 482 499 - function setDurationIfNecessary(audio, app) { 500 - if (audio.duration != lastSetDuration) { 501 - app.ports.setAudioDuration.send(audio.duration || 0) 502 - lastSetDuration = audio.duration 503 - } 483 + function setDurationIfNecessary(audio) { 484 + if (audio.duration === lastSetDuration) return; 485 + 486 + this.app.ports.setAudioDuration.send(audio.duration || 0) 487 + lastSetDuration = audio.duration 488 + 489 + if (!lastSetDuration || lastSetDuration < 30) return; 490 + 491 + const timestamp = Math.floor(Date.now() / 1000) 492 + const scrobbleTimeoutDuration = Math.min(240 + 0.5, lastSetDuration / 1.95) 493 + 494 + this.scrobbleTimeout = setTimeout(_ => { 495 + this.app.ports.scrobble.send({ 496 + duration: Math.round(lastSetDuration), 497 + timestamp: timestamp, 498 + trackId: audio.getAttribute("rel") 499 + }) 500 + }, scrobbleTimeoutDuration * 1000) 504 501 } 505 502 506 503
+11 -10
src/Library/LastFm.elm
··· 6 6 import List.Ext as List 7 7 import MD5 8 8 import String.Ext as String 9 - import Tracks exposing (IdentifiedTrack, Track) 9 + import Tracks exposing (Track) 10 10 import Tuple.Ext as Tuple 11 11 import Url exposing (Url) 12 12 import Url.Ext as Url ··· 100 100 -- 🎵 101 101 102 102 103 - nowPlaying : Model -> IdentifiedTrack -> msg -> Cmd msg 104 - nowPlaying model ( _, track ) msg = 103 + nowPlaying : Model -> { duration : Int, msg : msg, track : Track } -> Cmd msg 104 + nowPlaying model { duration, msg, track } = 105 105 case model.sessionKey of 106 106 Just sessionKey -> 107 107 Http.post ··· 109 109 apiUrl 110 110 , body = 111 111 authenticatedBody 112 - [ ( "artist", track.tags.artist ) 112 + [ ( "album", track.tags.album ) 113 + , ( "artist", track.tags.artist ) 114 + , ( "duration", String.fromInt duration ) 113 115 , ( "track", track.tags.title ) 114 - , ( "album", track.tags.album ) 115 116 , ( "trackNumber", String.fromInt track.tags.nr ) 116 117 117 118 -- ··· 126 127 Cmd.none 127 128 128 129 129 - scrobble : Model -> Float -> Int -> Track -> msg -> Cmd msg 130 - scrobble model duration timestamp track msg = 130 + scrobble : Model -> { duration : Int, msg : msg, timestamp : Int, track : Track } -> Cmd msg 131 + scrobble model { duration, msg, timestamp, track } = 131 132 case model.sessionKey of 132 133 Just sessionKey -> 133 134 Http.post ··· 135 136 apiUrl 136 137 , body = 137 138 authenticatedBody 138 - [ ( "artist", track.tags.artist ) 139 + [ ( "album", track.tags.album ) 140 + , ( "artist", track.tags.artist ) 141 + , ( "duration", String.fromInt duration ) 139 142 , ( "track", track.tags.title ) 140 - , ( "album", track.tags.album ) 141 143 , ( "trackNumber", String.fromInt track.tags.nr ) 142 144 143 145 -- 144 - , ( "duration", String.fromInt <| round duration ) 145 146 , ( "method", "track.scrobble" ) 146 147 , ( "sk", sessionKey ) 147 148 , ( "timestamp", String.fromInt timestamp )
+6 -1
src/Library/Return2.elm
··· 1 - module Return2 exposing (Return, addCommand, andThen, mapCommand, mapModel, return, returnWithModel, withModel) 1 + module Return2 exposing (..) 2 2 3 3 -- 🌳 4 4 ··· 30 30 returnWithModel : model -> Cmd msg -> Return model msg 31 31 returnWithModel = 32 32 Tuple.pair 33 + 34 + 35 + returnWithCommand : Cmd msg -> model -> Return model msg 36 + returnWithCommand cmd model = 37 + ( model, cmd ) 33 38 34 39 35 40