A music player that connects to your cloud/distributed storage.
at main 4.9 kB view raw
1module Sources.Services.Google.Parser exposing (..) 2 3import Dict 4import Json.Decode exposing (..) 5import Json.Decode.Ext exposing (..) 6import Maybe.Extra 7import Sources exposing (SourceData) 8import Sources.Pick 9import Sources.Processing exposing (Marker(..), PrepationAnswer, TreeAnswer) 10import Sources.Services.Google.Marker as Marker 11import String.Path 12import Time 13 14 15 16-- PREPARATION 17 18 19parsePreparationResponse : String -> Time.Posix -> SourceData -> Marker -> PrepationAnswer Marker 20parsePreparationResponse response currentTimePosix srcData _ = 21 let 22 newAccessToken = 23 response 24 |> decodeString (field "access_token" string) 25 |> Result.withDefault "" 26 27 currentTime = 28 -- Current time in milliseconds 29 Time.posixToMillis currentTimePosix 30 31 expiresAt = 32 -- Unix timestamp in milliseconds 33 response 34 |> decodeString (field "expires_in" int) 35 -- time in seconds 36 |> Result.withDefault 2500 37 |> (\s -> currentTime + s * 1000) 38 39 maybeRefreshToken = 40 response 41 |> decodeString (maybe <| field "refresh_token" string) 42 |> Result.toMaybe 43 |> Maybe.Extra.join 44 45 refreshTokenUpdater dict = 46 case maybeRefreshToken of 47 Just refreshToken -> 48 Dict.insert "refreshToken" refreshToken dict 49 50 Nothing -> 51 dict 52 in 53 srcData 54 |> Dict.insert "accessToken" newAccessToken 55 |> Dict.insert "expiresAt" (String.fromInt expiresAt) 56 |> refreshTokenUpdater 57 |> Dict.remove "authCode" 58 |> (\s -> { sourceData = s, marker = TheEnd }) 59 60 61 62-- TREE 63 64 65type alias Properties = 66 { id : String, name : String } 67 68 69type Item 70 = File Properties 71 | Directory Properties 72 73 74parseTreeResponse : String -> Marker -> TreeAnswer Marker 75parseTreeResponse response previousMarker = 76 let 77 nextPageToken = 78 response 79 |> decodeString (maybe <| field "nextPageToken" string) 80 |> Result.toMaybe 81 |> Maybe.Extra.join 82 83 items = 84 response 85 |> decodeString (field "files" <| listIgnore itemDecoder) 86 |> Result.withDefault [] 87 88 usedDirectory = 89 previousMarker 90 |> Marker.takeOne 91 |> Maybe.map Marker.itemDirectory 92 |> Maybe.withDefault "" 93 94 usedPath = 95 usedDirectory 96 |> String.Path.dropRight 1 97 |> String.Path.addSuffix 98 99 ( directories, files ) = 100 List.partition 101 (\item -> 102 case item of 103 Directory _ -> 104 True 105 106 File _ -> 107 False 108 ) 109 items 110 in 111 { filePaths = 112 files 113 |> List.map itemProperties 114 |> List.filter (.name >> Sources.Pick.isMusicFile) 115 |> List.map (\{ id, name } -> usedPath ++ id ++ "?name=" ++ name) 116 , marker = 117 previousMarker 118 |> Marker.removeOne 119 |> Marker.concat 120 (List.map 121 (itemProperties 122 >> (\props -> props.name ++ "/" ++ props.id) 123 >> String.append usedPath 124 >> Marker.Directory 125 ) 126 directories 127 ) 128 |> (case nextPageToken of 129 Just token -> 130 { directory = usedDirectory, token = token } 131 |> Marker.Param 132 |> List.singleton 133 |> Marker.concat 134 135 Nothing -> 136 identity 137 ) 138 } 139 140 141itemDecoder : Decoder Item 142itemDecoder = 143 map4 144 (\id name mime _ -> 145 case mime of 146 "application/vnd.google-apps.folder" -> 147 Directory { id = id, name = name } 148 149 _ -> 150 File { id = id, name = name } 151 ) 152 (field "id" string) 153 (field "name" string) 154 (field "mimeType" string) 155 (andThen 156 (\b -> 157 if b then 158 fail "Exclude deleted files" 159 160 else 161 succeed b 162 ) 163 (field "trashed" bool) 164 ) 165 166 167itemProperties : Item -> Properties 168itemProperties item = 169 case item of 170 Directory props -> 171 props 172 173 File props -> 174 props 175 176 177 178-- ERROR 179 180 181parseErrorResponse : String -> Maybe String 182parseErrorResponse response = 183 response 184 |> decodeString (at [ "error", "message" ] string) 185 |> Result.toMaybe