A music player that connects to your cloud/distributed storage.
at main 4.7 kB view raw
1module LastFm exposing (..) 2 3import Common 4import Http 5import Json.Decode as Json 6import List.Ext as List 7import MD5 8import String.Ext as String 9import Tracks exposing (Track) 10import Tuple.Ext as Tuple 11import Url exposing (Url) 12import Url.Ext as Url 13 14 15 16-- 🏔 17 18 19apiKey = 20 "4f0fe85b67baef8bb7d008a8754a95e5" 21 22 23apiUrl = 24 "https://ws.audioscrobbler.com/2.0" 25 26 27notSoSecret = 28 "0cec3ca0f58e04a5082f1131aba1e0d3" 29 30 31 32-- 🌳 33 34 35type alias Model = 36 { authenticating : Bool 37 , sessionKey : Maybe String 38 } 39 40 41initialModel : Model 42initialModel = 43 { authenticating = False 44 , sessionKey = Nothing 45 } 46 47 48authenticationCommand : (Result Http.Error String -> msg) -> Url -> Cmd msg 49authenticationCommand msg url = 50 case Url.extractQueryParam "token" url of 51 Just token -> 52 Http.get 53 { url = 54 authenticatedUrl 55 [ ( "method", "auth.getSession" ) 56 , ( "token", token ) 57 ] 58 , expect = 59 Json.string 60 |> Json.at [ "session", "key" ] 61 |> Http.expectJson msg 62 } 63 64 Nothing -> 65 Cmd.none 66 67 68 69-- 📣 70 71 72disconnect : Model -> Model 73disconnect model = 74 { model | sessionKey = Nothing } 75 76 77failedToAuthenticate : Model -> Model 78failedToAuthenticate model = 79 { model | authenticating = False } 80 81 82gotSessionKey : String -> Model -> Model 83gotSessionKey sessionKey model = 84 { model | sessionKey = Just sessionKey } 85 86 87 88-- 🎵 89 90 91nowPlaying : Model -> { duration : Int, msg : msg, track : Track } -> Cmd msg 92nowPlaying model { duration, msg, track } = 93 case model.sessionKey of 94 Just sessionKey -> 95 Http.post 96 { url = 97 apiUrl 98 , body = 99 [ ( "duration", String.fromInt duration ) 100 , ( "track", track.tags.title ) 101 , ( "trackNumber", String.fromInt track.tags.nr ) 102 103 -- 104 , ( "method", "track.updateNowPlaying" ) 105 , ( "sk", sessionKey ) 106 ] 107 |> addAlbum track 108 |> addArtist track 109 |> authenticatedBody 110 , expect = 111 Http.expectWhatever (always msg) 112 } 113 114 Nothing -> 115 Cmd.none 116 117 118scrobble : Model -> { duration : Int, msg : msg, timestamp : Int, track : Track } -> Cmd msg 119scrobble model { duration, msg, timestamp, track } = 120 case model.sessionKey of 121 Just sessionKey -> 122 Http.post 123 { url = 124 apiUrl 125 , body = 126 [ ( "duration", String.fromInt duration ) 127 , ( "track", track.tags.title ) 128 , ( "trackNumber", String.fromInt track.tags.nr ) 129 130 -- 131 , ( "method", "track.scrobble" ) 132 , ( "sk", sessionKey ) 133 , ( "timestamp", String.fromInt timestamp ) 134 ] 135 |> addAlbum track 136 |> addArtist track 137 |> authenticatedBody 138 , expect = 139 Http.expectWhatever (always msg) 140 } 141 142 Nothing -> 143 Cmd.none 144 145 146addAlbum track list = 147 case track.tags.album of 148 Just album -> 149 ( "album", album ) :: list 150 151 Nothing -> 152 list 153 154 155addArtist track list = 156 case track.tags.artist of 157 Just artist -> 158 ( "artist", artist ) :: list 159 160 Nothing -> 161 list 162 163 164 165-- 🔱 166 167 168authenticatedBody : List ( String, String ) -> Http.Body 169authenticatedBody params = 170 params 171 |> authenticatedParams 172 |> Common.queryString 173 |> String.dropLeft 1 174 |> Http.stringBody "application/x-www-form-urlencoded" 175 176 177authenticatedUrl : List ( String, String ) -> String 178authenticatedUrl params = 179 params 180 |> authenticatedParams 181 |> Common.queryString 182 |> String.append apiUrl 183 184 185authenticatedParams : List ( String, String ) -> List ( String, String ) 186authenticatedParams params = 187 let 188 extendedParams = 189 ( "api_key", apiKey ) :: params 190 in 191 extendedParams 192 |> List.sortBy Tuple.first 193 |> List.map (Tuple.uncurry String.append) 194 |> String.concat 195 |> String.addSuffix notSoSecret 196 |> MD5.hex 197 |> Tuple.pair "api_sig" 198 |> List.addTo extendedParams 199 |> (::) ( "format", "json" ) 200 |> List.sortBy Tuple.first