A music player that connects to your cloud/distributed storage.
at main 230 lines 5.5 kB view raw
1module Sources.Services.Dropbox exposing (authorizationSourceData, authorizationUrl, defaults, getProperDirectoryPath, initialData, makeTrackUrl, makeTree, parseErrorResponse, parsePreparationResponse, parseTreeResponse, postProcessTree, prepare, properties) 2 3{-| Dropbox Service. 4-} 5 6import Base64 7import Common 8import Dict 9import Dict.Ext as Dict 10import Http 11import Json.Decode 12import Json.Encode 13import Sources exposing (Property, SourceData) 14import Sources.Pick 15import Sources.Processing exposing (..) 16import Sources.Services.Common exposing (cleanPath, noPrep) 17import Sources.Services.Dropbox.Parser as Parser 18import Time 19 20 21 22-- PROPERTIES 23-- 📟 24 25 26defaults = 27 { appKey = "kwsydtrzban41zr" 28 , name = "Music from Dropbox" 29 } 30 31 32{-| The list of properties we need from the user. 33 34Tuple: (property, label, placeholder, isPassword) 35Will be used for the forms. 36 37-} 38properties : List Property 39properties = 40 [ { key = "directoryPath" 41 , label = "Directory (Optional)" 42 , placeholder = "/" 43 , password = False 44 } 45 , { key = "appKey" 46 , label = "App key" 47 , placeholder = defaults.appKey 48 , password = False 49 } 50 ] 51 52 53{-| Initial data set. 54-} 55initialData : SourceData 56initialData = 57 Dict.fromList 58 [ ( "appKey", defaults.appKey ) 59 , ( "directoryPath", "" ) 60 , ( "name", defaults.name ) 61 ] 62 63 64 65-- AUTHORIZATION 66 67 68{-| Authorization url. 69-} 70authorizationUrl : SourceData -> String -> String 71authorizationUrl sourceData origin = 72 let 73 encodeData data = 74 data 75 |> Dict.toList 76 |> List.map (Tuple.mapSecond Json.Encode.string) 77 |> Json.Encode.object 78 79 state = 80 sourceData 81 |> encodeData 82 |> Json.Encode.encode 0 83 |> Base64.encode 84 in 85 [ ( "response_type", "token" ) 86 , ( "client_id", Dict.fetch "appKey" "unknown" sourceData ) 87 , ( "redirect_uri", origin ++ "?path=sources/new/dropbox" ) 88 , ( "state", state ) 89 ] 90 |> Common.queryString 91 |> String.append "https://www.dropbox.com/oauth2/authorize" 92 93 94{-| Authorization source data. 95-} 96authorizationSourceData : { codeOrToken : Maybe String, state : Maybe String } -> SourceData 97authorizationSourceData args = 98 args.state 99 |> Maybe.andThen (Base64.decode >> Result.toMaybe) 100 |> Maybe.withDefault "{}" 101 |> Json.Decode.decodeString (Json.Decode.dict Json.Decode.string) 102 |> Result.withDefault Dict.empty 103 |> Dict.unionFlipped initialData 104 |> Dict.update "accessToken" (\_ -> args.codeOrToken) 105 106 107 108-- PREPARATION 109 110 111prepare : String -> SourceData -> Marker -> (Result Http.Error String -> msg) -> Maybe (Cmd msg) 112prepare _ _ _ _ = 113 Nothing 114 115 116 117-- TREE 118 119 120{-| Create a directory tree. 121 122List all the tracks in the bucket. 123Or a specific directory in the bucket. 124 125-} 126makeTree : SourceData -> Marker -> Time.Posix -> (Result Http.Error String -> msg) -> Cmd msg 127makeTree srcData marker _ resultMsg = 128 let 129 accessToken = 130 Dict.fetch "accessToken" "" srcData 131 132 body = 133 (case marker of 134 TheBeginning -> 135 [ ( "limit", Json.Encode.int 2000 ) 136 , ( "path", Json.Encode.string (getProperDirectoryPath srcData) ) 137 , ( "recursive", Json.Encode.bool True ) 138 ] 139 140 InProgress cursor -> 141 [ ( "cursor", Json.Encode.string cursor ) 142 ] 143 144 TheEnd -> 145 [] 146 ) 147 |> Json.Encode.object 148 |> Http.jsonBody 149 150 url = 151 case marker of 152 TheBeginning -> 153 "https://api.dropboxapi.com/2/files/list_folder" 154 155 InProgress _ -> 156 "https://api.dropboxapi.com/2/files/list_folder/continue" 157 158 TheEnd -> 159 "" 160 in 161 Http.request 162 { method = "POST" 163 , headers = [ Http.header "Authorization" ("Bearer " ++ accessToken) ] 164 , url = url 165 , body = body 166 , expect = Http.expectStringResponse resultMsg Common.translateHttpResponse 167 , timeout = Nothing 168 , tracker = Nothing 169 } 170 171 172getProperDirectoryPath : SourceData -> String 173getProperDirectoryPath srcData = 174 let 175 path = 176 srcData 177 |> Dict.get "directoryPath" 178 |> Maybe.withDefault "" 179 |> cleanPath 180 in 181 if path == "" then 182 "" 183 184 else 185 "/" ++ path 186 187 188{-| Re-export parser functions. 189-} 190parsePreparationResponse : String -> Time.Posix -> SourceData -> Marker -> PrepationAnswer Marker 191parsePreparationResponse = 192 noPrep 193 194 195parseTreeResponse : String -> Marker -> TreeAnswer Marker 196parseTreeResponse = 197 Parser.parseTreeResponse 198 199 200parseErrorResponse : String -> Maybe String 201parseErrorResponse = 202 Parser.parseErrorResponse 203 204 205 206-- POST 207 208 209{-| Post process the tree results. 210 211!!! Make sure we only use music files that we can use. 212 213-} 214postProcessTree : List String -> List String 215postProcessTree = 216 Sources.Pick.selectMusicFiles 217 218 219 220-- TRACK URL 221 222 223{-| Create a public url for a file. 224 225We need this to play the track. 226 227-} 228makeTrackUrl : Time.Posix -> String -> SourceData -> HttpMethod -> String -> String 229makeTrackUrl _ _ srcData _ pathToFile = 230 "dropbox://" ++ Dict.fetch "accessToken" "" srcData ++ "@" ++ pathToFile