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.

Initial work for source processing

+405 -12
+44 -9
src/Applications/Brain.elm
··· 5 5 import Brain.Core exposing (..) 6 6 import Brain.Ports 7 7 import Brain.Reply as Reply exposing (Reply(..)) 8 + import Brain.Sources.Processing 8 9 import Json.Decode 9 10 import Replying exposing (return) 11 + import Sources.Processing.Encoding 10 12 11 13 12 14 ··· 31 33 ( ----------------------------------------- 32 34 -- Initial model 33 35 ----------------------------------------- 34 - { authentication = Brain.Authentication.initialModel } 36 + { authentication = Brain.Authentication.initialModel 37 + , sourceProcessing = Brain.Sources.Processing.initialModel 38 + } 35 39 ----------------------------------------- 36 40 -- Initial command 37 41 ----------------------------------------- 38 42 , Cmd.batch 39 - [ Cmd.map AuthenticationMsg Brain.Authentication.initialCommand ] 43 + [ Cmd.map AuthenticationMsg Brain.Authentication.initialCommand 44 + , Cmd.map SourceProcessingMsg Brain.Sources.Processing.initialCommand 45 + ] 40 46 ) 41 47 42 48 ··· 70 76 , msg = sub 71 77 } 72 78 79 + SourceProcessingMsg sub -> 80 + updateChild 81 + { mapCmd = SourceProcessingMsg 82 + , mapModel = \child -> { model | sourceProcessing = child } 83 + , update = Brain.Sources.Processing.update 84 + } 85 + { model = model.sourceProcessing 86 + , msg = sub 87 + } 88 + 73 89 74 90 75 91 -- 📣 | Children & Replies ··· 90 106 |> NotifyUI 91 107 92 108 LoadEnclosedUserData data -> 93 - data 94 - |> Alien.broadcast Alien.LoadEnclosedUserData 95 - |> NotifyUI 109 + NotifyUI (Alien.broadcast Alien.LoadEnclosedUserData data) 96 110 97 111 LoadHypaethralUserData data -> 98 - data 99 - |> Alien.broadcast Alien.LoadHypaethralUserData 100 - |> NotifyUI 112 + NotifyUI (Alien.broadcast Alien.LoadHypaethralUserData data) 113 + 114 + ReportSourceProcessingError data -> 115 + NotifyUI (Alien.broadcast Alien.ReportSourceProcessingError data) 101 116 102 117 103 118 updateChild = ··· 109 124 110 125 111 126 subscriptions : Model -> Sub Msg 112 - subscriptions _ = 127 + subscriptions model = 113 128 Sub.batch 114 129 [ Brain.Ports.fromCache translateAlienEvent 115 130 , Brain.Ports.fromUI translateAlienEvent 131 + 132 + ----------------------------------------- 133 + -- Children 134 + ----------------------------------------- 135 + , Sub.map 136 + SourceProcessingMsg 137 + (Brain.Sources.Processing.subscriptions model.sourceProcessing) 116 138 ] 117 139 118 140 ··· 124 146 125 147 Just Alien.AuthMethod -> 126 148 AuthenticationMsg (Brain.Authentication.MethodRetrieved event.data) 149 + 150 + Just Alien.ProcessSources -> 151 + -- Only proceed to the processing if we got all the necessary data, 152 + -- otherwise report an error in the UI. 153 + case Json.Decode.decodeValue Sources.Processing.Encoding.argumentsDecoder event.data of 154 + Ok arguments -> 155 + SourceProcessingMsg (Brain.Sources.Processing.Process arguments) 156 + 157 + Err error -> 158 + error 159 + |> Json.Decode.errorToString 160 + |> Alien.report Alien.ReportGenericError 161 + |> NotifyUI 127 162 128 163 Just Alien.SaveEnclosedUserData -> 129 164 AuthenticationMsg (Brain.Authentication.SaveEnclosedData event.data)
+5 -1
src/Applications/Brain/Core.elm
··· 2 2 3 3 import Alien 4 4 import Brain.Authentication 5 + import Brain.Sources.Processing 5 6 6 7 7 8 ··· 17 18 18 19 19 20 type alias Model = 20 - { authentication : Brain.Authentication.Model } 21 + { authentication : Brain.Authentication.Model 22 + , sourceProcessing : Brain.Sources.Processing.Model 23 + } 21 24 22 25 23 26 ··· 31 34 -- Children 32 35 ----------------------------------------- 33 36 | AuthenticationMsg Brain.Authentication.Msg 37 + | SourceProcessingMsg Brain.Sources.Processing.Msg
+1
src/Applications/Brain/Reply.elm
··· 13 13 | HideLoadingScreen 14 14 | LoadEnclosedUserData Json.Value 15 15 | LoadHypaethralUserData Json.Value 16 + | ReportSourceProcessingError Json.Value
+175
src/Applications/Brain/Sources/Processing.elm
··· 1 + module Brain.Sources.Processing exposing (Model, Msg(..), initialCommand, initialModel, subscriptions, update) 2 + 3 + import Brain.Reply exposing (Reply(..)) 4 + import Http exposing (Error(..)) 5 + import Json.Encode as Encode 6 + import Replying exposing (R3D3) 7 + import Sources exposing (Service, Source) 8 + import Sources.Processing exposing (..) 9 + import Sources.Services as Services 10 + import Time 11 + import Tracks exposing (Track) 12 + 13 + 14 + 15 + -- 🌳 16 + 17 + 18 + type alias Model = 19 + { currentTime : Time.Posix 20 + , origin : String 21 + , status : Status 22 + } 23 + 24 + 25 + initialModel : Model 26 + initialModel = 27 + { currentTime = Time.millisToPosix 0 28 + , origin = "ORIGIN_UNKNOWN" 29 + , status = NotProcessing 30 + } 31 + 32 + 33 + initialCommand : Cmd Msg 34 + initialCommand = 35 + Cmd.none 36 + 37 + 38 + 39 + -- 📣 40 + 41 + 42 + type Msg 43 + = Process Arguments 44 + | NextInLine 45 + ----------------------------------------- 46 + -- Steps 47 + ----------------------------------------- 48 + | PrepareStep Context (Result Http.Error String) 49 + | TreeStep Context (Result Http.Error String) 50 + | TagsStep ContextForTags 51 + ----------------------------------------- 52 + -- Bits & Pieces 53 + ----------------------------------------- 54 + | SetCurrentTime Time.Posix 55 + 56 + 57 + update : Msg -> Model -> R3D3 Model Msg Reply 58 + update msg model = 59 + case msg of 60 + {- If already processing, do nothing. 61 + If there are no sources, do nothing. 62 + If there are sources, start processing the first source. 63 + -} 64 + Process { origin, sources, tracks } -> 65 + ( model 66 + , Cmd.none 67 + , Nothing 68 + ) 69 + 70 + {- If not processing, do nothing. 71 + If there are no sources left, do nothing. 72 + If there are sources left, start processing the next source in line. 73 + -} 74 + NextInLine -> 75 + ( model 76 + , Cmd.none 77 + , Nothing 78 + ) 79 + 80 + ----------------------------------------- 81 + -- PHASE 1 82 + -- Prepare for processing. 83 + ----------------------------------------- 84 + PrepareStep context (Ok response) -> 85 + ( model 86 + , Cmd.none 87 + , Nothing 88 + ) 89 + 90 + PrepareStep context (Err err) -> 91 + ( model 92 + , Cmd.none 93 + , Just [ reportHttpError context.source err ] 94 + ) 95 + 96 + ----------------------------------------- 97 + -- PHASE 2 98 + -- Make a file list/tree. 99 + ----------------------------------------- 100 + TreeStep context (Ok response) -> 101 + ( model 102 + , Cmd.none 103 + , Nothing 104 + ) 105 + 106 + TreeStep context (Err err) -> 107 + ( model 108 + , Cmd.none 109 + , Just [ reportHttpError context.source err ] 110 + ) 111 + 112 + ----------------------------------------- 113 + -- PHASE 3 114 + -- Get the tags for each file in the file list. 115 + ----------------------------------------- 116 + TagsStep tagsContext -> 117 + ( model 118 + , Cmd.none 119 + , Nothing 120 + ) 121 + 122 + ----------------------------------------- 123 + -- BITS & PIECES 124 + ----------------------------------------- 125 + SetCurrentTime time -> 126 + ( { model | currentTime = time } 127 + , Cmd.none 128 + , Nothing 129 + ) 130 + 131 + 132 + 133 + -- 📣 | Common 134 + 135 + 136 + reportHttpError : Source -> Http.Error -> Reply 137 + reportHttpError source err = 138 + reportError { sourceId = source.id, error = translateHttpError source.service err } 139 + 140 + 141 + reportError : { sourceId : String, error : String } -> Reply 142 + reportError { sourceId, error } = 143 + [ ( "sourceId", Encode.string sourceId ) 144 + , ( "error", Encode.string error ) 145 + ] 146 + |> Encode.object 147 + |> ReportSourceProcessingError 148 + 149 + 150 + translateHttpError : Service -> Http.Error -> String 151 + translateHttpError service err = 152 + case err of 153 + NetworkError -> 154 + "Cannot connect to this source" 155 + 156 + Timeout -> 157 + "Source did not respond (timeout)" 158 + 159 + BadUrl _ -> 160 + "Diffuse error, invalid url was used" 161 + 162 + BadStatus _ -> 163 + "Got a faulty response from this source" 164 + 165 + BadBody response -> 166 + Services.parseErrorResponse service response 167 + 168 + 169 + 170 + -- 📰 171 + 172 + 173 + subscriptions : Model -> Sub Msg 174 + subscriptions _ = 175 + Time.every (60 * 1000) SetCurrentTime
+21
src/Library/Alien.elm
··· 23 23 | AuthEnclosedData 24 24 | AuthMethod 25 25 -- from UI 26 + | ProcessSources 26 27 | SaveEnclosedUserData 27 28 | SaveHypaethralUserData 28 29 | SignIn ··· 31 32 | HideLoadingScreen 32 33 | LoadEnclosedUserData 33 34 | LoadHypaethralUserData 35 + | ReportGenericError 36 + | ReportSourceProcessingError 34 37 35 38 36 39 ··· 76 79 ----------------------------------------- 77 80 -- From UI 78 81 ----------------------------------------- 82 + ProcessSources -> 83 + "PROCESS_SOURCES" 84 + 79 85 SaveEnclosedUserData -> 80 86 "SAVE_ENCLOSED_USER_DATA" 81 87 ··· 100 106 LoadHypaethralUserData -> 101 107 "LOAD_HYPAETHRAL_USER_DATA" 102 108 109 + ReportGenericError -> 110 + "REPORT_GENERIC_ERROR" 111 + 112 + ReportSourceProcessingError -> 113 + "REPORT_SOURCE_PROCESSING_ERROR" 114 + 103 115 104 116 tagFromString : String -> Maybe Tag 105 117 tagFromString string = ··· 116 128 ----------------------------------------- 117 129 -- From UI 118 130 ----------------------------------------- 131 + "PROCESS_SOURCES" -> 132 + Just ProcessSources 133 + 119 134 "SAVE_ENCLOSED_USER_DATA" -> 120 135 Just SaveEnclosedUserData 121 136 ··· 139 154 140 155 "LOAD_HYPAETHRAL_USER_DATA" -> 141 156 Just LoadHypaethralUserData 157 + 158 + "REPORT_GENERIC_ERROR" -> 159 + Just ReportGenericError 160 + 161 + "REPORT_SOURCE_PROCESSING_ERROR" -> 162 + Just ReportSourceProcessingError 142 163 143 164 _ -> 144 165 Nothing
+41 -2
src/Library/Sources/Processing.elm
··· 1 - module Sources.Processing exposing (HttpMethod(..), Marker(..), PrepationAnswer, TreeAnswer, httpMethod) 1 + module Sources.Processing exposing (Arguments, Context, ContextForTags, HttpMethod(..), Marker(..), PrepationAnswer, Status(..), TreeAnswer, httpMethod) 2 2 3 3 import Http 4 - import Sources exposing (SourceData) 4 + import Sources exposing (Source, SourceData) 5 + import Tracks exposing (Tags, Track) 6 + 7 + 8 + 9 + -- 🌳 10 + 11 + 12 + type Status 13 + = Processing (List ( Source, List Track )) 14 + | NotProcessing 15 + 16 + 17 + type alias Arguments = 18 + { origin : String 19 + , sources : List Source 20 + , tracks : List Track 21 + } 5 22 6 23 7 24 ··· 23 40 type alias TreeAnswer marker = 24 41 { filePaths : List String 25 42 , marker : marker 43 + } 44 + 45 + 46 + 47 + -- CONTEXTS 48 + 49 + 50 + type alias Context = 51 + { filePaths : List String 52 + , origin : String 53 + , preparationMarker : Marker 54 + , source : Source 55 + , treeMarker : Marker 56 + } 57 + 58 + 59 + type alias ContextForTags = 60 + { nextFilePaths : List String 61 + , receivedFilePaths : List String 62 + , receivedTags : List (Maybe Tags) 63 + , sourceId : String 64 + , urlsForTags : List String 26 65 } 27 66 28 67
+21
src/Library/Sources/Processing/Encoding.elm
··· 1 + module Sources.Processing.Encoding exposing (argumentsDecoder) 2 + 3 + {-| Encoding. 4 + -} 5 + 6 + import Json.Decode as Decode exposing (Decoder) 7 + import Sources.Encoding as Sources 8 + import Sources.Processing exposing (Arguments) 9 + import Tracks.Encoding as Tracks 10 + 11 + 12 + 13 + -- 🔱 14 + 15 + 16 + argumentsDecoder : Decoder Arguments 17 + argumentsDecoder = 18 + Decode.map3 Arguments 19 + (Decode.field "origin" Decode.string) 20 + (Decode.field "sources" <| Decode.list Sources.decoder) 21 + (Decode.field "tracks" <| Decode.list Tracks.trackDecoder)
+97
src/Library/Tracks/Encoding.elm
··· 1 + module Tracks.Encoding exposing (decodeFavourite, decodeTrack, encodeFavourite, encodeMaybe, encodeTags, encodeTrack, favouriteDecoder, tagsDecoder, trackDecoder) 2 + 3 + import Json.Decode as Decode 4 + import Json.Encode as Encode 5 + import Tracks exposing (..) 6 + 7 + 8 + 9 + -- ENCODE 10 + 11 + 12 + encodeTrack : Track -> Encode.Value 13 + encodeTrack track = 14 + Encode.object 15 + [ ( "id", Encode.string track.id ) 16 + , ( "path", Encode.string track.path ) 17 + , ( "sourceId", Encode.string track.sourceId ) 18 + , ( "tags", encodeTags track.tags ) 19 + ] 20 + 21 + 22 + encodeFavourite : Favourite -> Encode.Value 23 + encodeFavourite fav = 24 + Encode.object 25 + [ ( "artist", Encode.string fav.artist ) 26 + , ( "title", Encode.string fav.title ) 27 + ] 28 + 29 + 30 + encodeTags : Tags -> Encode.Value 31 + encodeTags tags = 32 + Encode.object 33 + [ ( "disc", Encode.int tags.disc ) 34 + , ( "nr", Encode.int tags.nr ) 35 + 36 + -- 37 + , ( "album", Encode.string tags.album ) 38 + , ( "artist", Encode.string tags.artist ) 39 + , ( "title", Encode.string tags.title ) 40 + 41 + -- 42 + , ( "genre", encodeMaybe tags.genre Encode.string ) 43 + , ( "picture", encodeMaybe tags.picture Encode.string ) 44 + , ( "year", encodeMaybe tags.year Encode.int ) 45 + ] 46 + 47 + 48 + encodeMaybe : Maybe a -> (a -> Encode.Value) -> Encode.Value 49 + encodeMaybe maybe encoder = 50 + maybe 51 + |> Maybe.map encoder 52 + |> Maybe.withDefault Encode.null 53 + 54 + 55 + 56 + -- DECODE 57 + 58 + 59 + decodeTrack : Decode.Value -> Maybe Track 60 + decodeTrack = 61 + Decode.decodeValue trackDecoder 62 + >> Result.toMaybe 63 + 64 + 65 + decodeFavourite : Decode.Value -> Maybe Favourite 66 + decodeFavourite = 67 + Decode.decodeValue favouriteDecoder 68 + >> Result.toMaybe 69 + 70 + 71 + trackDecoder : Decode.Decoder Track 72 + trackDecoder = 73 + Decode.map4 Track 74 + (Decode.field "id" Decode.string) 75 + (Decode.field "path" Decode.string) 76 + (Decode.field "sourceId" Decode.string) 77 + (Decode.field "tags" tagsDecoder) 78 + 79 + 80 + tagsDecoder : Decode.Decoder Tags 81 + tagsDecoder = 82 + Decode.map8 Tags 83 + (Decode.field "disc" Decode.int) 84 + (Decode.field "nr" Decode.int) 85 + (Decode.field "album" Decode.string) 86 + (Decode.field "artist" Decode.string) 87 + (Decode.field "title" Decode.string) 88 + (Decode.maybe <| Decode.field "genre" Decode.string) 89 + (Decode.maybe <| Decode.field "picture" Decode.string) 90 + (Decode.maybe <| Decode.field "year" Decode.int) 91 + 92 + 93 + favouriteDecoder : Decode.Decoder Favourite 94 + favouriteDecoder = 95 + Decode.map2 Favourite 96 + (Decode.field "artist" Decode.string) 97 + (Decode.field "title" Decode.string)