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

Configure Feed

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

Audio model

+178 -74
+1
elm.json
··· 14 14 "Herteby/enum": "1.0.0", 15 15 "NoRedInk/elm-json-decode-pipeline": "1.0.0", 16 16 "Skinney/murmur3": "2.0.8", 17 + "arturopala/elm-monocle": "2.1.0", 17 18 "avh4/elm-color": "1.0.0", 18 19 "elm/browser": "1.0.2", 19 20 "elm/bytes": "1.0.8",
+35 -31
src/Applications/UI.elm
··· 30 30 import List.Ext as List 31 31 import List.Extra as List 32 32 import Maybe.Extra as Maybe 33 + import Monocle.Lens as Lens 33 34 import Notifications exposing (Notification) 34 35 import Playlists.Encoding as Playlists 35 36 import Process ··· 137 138 , url = url 138 139 , viewport = flags.viewport 139 140 140 - -- Audio 141 - -------- 142 - , audioDuration = 0 143 - , audioHasStalled = False 144 - , audioIsLoading = False 145 - , audioIsPlaying = False 146 - , audioPosition = 0 147 - 148 - -- 149 - , progress = Dict.empty 150 - , rememberProgress = True 151 - 152 141 -- Children 153 142 ----------- 154 143 , alfred = Alfred.initialModel ··· 159 148 , queue = Queue.initialModel 160 149 , sources = Sources.initialModel 161 150 , tracks = Tracks.initialModel 151 + 152 + -- Pieces 153 + --------- 154 + , audio = Audio.initialModel 162 155 163 156 -- Debouncing 164 157 ------------- ··· 255 248 translateReply ShiftQueue m 256 249 257 250 [ Keyboard.ArrowUp ] -> 258 - translateReply (Seek <| (m.audioPosition - 10) / m.audioDuration) m 251 + translateReply (Seek <| (m.audio.position - 10) / m.audio.duration) m 259 252 260 253 [ Keyboard.ArrowDown ] -> 261 - translateReply (Seek <| (m.audioPosition + 10) / m.audioDuration) m 254 + translateReply (Seek <| (m.audio.position + 10) / m.audio.duration) m 262 255 263 256 [ Keyboard.Character "N" ] -> 264 257 translateReply ScrollToNowPlaying m ··· 845 838 Audio.playPause model 846 839 847 840 ToggleRememberProgress -> 848 - translateReply SaveSettings { model | rememberProgress = not model.rememberProgress } 841 + model 842 + |> Lens.modify 843 + Audio.lens 844 + (\a -> { a | rememberProgress = not a.rememberProgress }) 845 + |> translateReply 846 + SaveSettings 849 847 850 848 ----------------------------------------- 851 849 -- Authentication ··· 1217 1215 model.currentTime 1218 1216 model.sources.collection 1219 1217 model.tracks.cached 1220 - (if model.rememberProgress then 1221 - model.progress 1218 + (if model.audio.rememberProgress then 1219 + model.audio.progress 1222 1220 1223 1221 else 1224 1222 Dict.empty ··· 1353 1351 model.currentTime 1354 1352 model.sources.collection 1355 1353 model.tracks.cached 1356 - (if model.rememberProgress then 1357 - model.progress 1354 + (if model.audio.rememberProgress then 1355 + model.audio.progress 1358 1356 1359 1357 else 1360 1358 Dict.empty ··· 1531 1529 Export -> 1532 1530 { favourites = model.tracks.favourites 1533 1531 , playlists = List.filterNot .autoGenerated model.playlists.collection 1534 - , progress = model.progress 1532 + , progress = model.audio.progress 1535 1533 , settings = Just (gatherSettings model) 1536 1534 , sources = model.sources.collection 1537 1535 , tracks = model.tracks.collection.untouched ··· 1581 1579 |> returnWithModel model 1582 1580 1583 1581 SaveProgress -> 1584 - model.progress 1582 + model.audio.progress 1585 1583 |> Json.Encode.dict identity Json.Encode.float 1586 1584 |> Alien.broadcast Alien.SaveProgress 1587 1585 |> Ports.toBrain ··· 2075 2073 , hideDuplicateTracks = model.tracks.hideDuplicates 2076 2074 , lastFm = model.lastFm 2077 2075 , processAutomatically = model.processAutomatically 2078 - , rememberProgress = model.rememberProgress 2076 + , rememberProgress = model.audio.rememberProgress 2079 2077 } 2080 2078 |> Lazy.lazy2 Settings.view subPage 2081 2079 |> Html.map Reply ··· 2098 2096 model.queue.activeItem 2099 2097 model.queue.repeat 2100 2098 model.queue.shuffle 2101 - { stalled = model.audioHasStalled 2102 - , loading = model.audioIsLoading 2103 - , playing = model.audioIsPlaying 2099 + { stalled = model.audio.hasStalled 2100 + , loading = model.audio.isLoading 2101 + , playing = model.audio.isPlaying 2104 2102 } 2105 - ( model.audioPosition 2106 - , model.audioDuration 2103 + ( model.audio.position 2104 + , model.audio.duration 2107 2105 ) 2108 2106 ) 2109 2107 ] ··· 2232 2230 2233 2231 2234 2232 gatherSettings : Model -> Settings.Settings 2235 - gatherSettings { backdrop, lastFm, processAutomatically, rememberProgress, tracks } = 2233 + gatherSettings { audio, backdrop, lastFm, processAutomatically, tracks } = 2236 2234 { backgroundImage = backdrop.chosen 2237 2235 , hideDuplicates = tracks.hideDuplicates 2238 2236 , lastFm = lastFm.sessionKey 2239 2237 , processAutomatically = processAutomatically 2240 - , rememberProgress = rememberProgress 2238 + , rememberProgress = audio.rememberProgress 2241 2239 } 2242 2240 2243 2241 ··· 2276 2274 ( { model 2277 2275 | backdrop = backdropModel 2278 2276 , playlists = playlistsModel 2279 - , progress = data.progress 2280 2277 , sources = sourcesModel 2281 2278 , tracks = tracksModel 2282 2279 2283 2280 -- 2284 2281 , lastFm = { lastFmModel | sessionKey = Maybe.andThen .lastFm data.settings } 2285 2282 , processAutomatically = Maybe.unwrap True .processAutomatically data.settings 2286 - , rememberProgress = Maybe.unwrap True .rememberProgress data.settings 2287 2283 } 2284 + |> Lens.modify 2285 + Audio.lens 2286 + (\a -> 2287 + { a 2288 + | progress = data.progress 2289 + , rememberProgress = Maybe.unwrap True .rememberProgress data.settings 2290 + } 2291 + ) 2288 2292 -- 2289 2293 , Cmd.batch 2290 2294 [ Cmd.map PlaylistsMsg playlistsCmd
+64 -29
src/Applications/UI/Audio/State.elm
··· 2 2 3 3 import Dict 4 4 import LastFm 5 + import Management 5 6 import Maybe.Extra as Maybe 7 + import Monocle.Lens as Lens exposing (Lens) 6 8 import Return exposing (return) 7 9 import Return.Ext as Return exposing (communicate) 8 - import UI.Audio.Types as Audio exposing (..) 10 + import UI.Audio.Types as Audio exposing (Msg(..)) 9 11 import UI.Ports as Ports 10 12 import UI.Queue as Queue 11 13 import UI.Reply as Reply 12 - import UI.Types as UI exposing (Manager) 14 + import UI.Types as UI exposing (Manager, Organizer) 15 + 16 + 17 + 18 + -- 🌳 19 + 20 + 21 + initialModel : Audio.Model 22 + initialModel = 23 + { duration = 0 24 + , hasStalled = False 25 + , isLoading = False 26 + , isPlaying = False 27 + , position = 0 28 + 29 + -- 30 + , progress = Dict.empty 31 + , rememberProgress = True 32 + } 33 + 13 34 35 + lens : Lens UI.Model Audio.Model 36 + lens = 37 + { get = .audio 38 + , set = \audio ui -> { ui | audio = audio } 39 + } 14 40 15 41 42 + 16 43 -- 📣 17 44 18 45 ··· 20 47 update msg = 21 48 case msg of 22 49 NoteProgress a -> 23 - noteProgress a 50 + organize (noteProgress a) 24 51 25 52 PlayPause -> 26 53 playPause ··· 29 56 setDuration a 30 57 31 58 SetHasStalled a -> 32 - setHasStalled a 59 + organize (setHasStalled a) 33 60 34 61 SetIsLoading a -> 35 - setIsLoading a 62 + organize (setIsLoading a) 36 63 37 64 SetIsPlaying a -> 38 - setIsPlaying a 65 + organize (setIsPlaying a) 39 66 40 67 SetPosition a -> 41 - setPosition a 68 + organize (setPosition a) 42 69 43 70 Stop -> 44 71 stop 45 72 46 73 74 + organize : Organizer Audio.Model -> Manager 75 + organize = 76 + Management.organize lens 77 + 78 + 47 79 48 80 -- 🔱 49 81 50 82 51 - noteProgress : { trackId : String, progress : Float } -> Manager 83 + noteProgress : { trackId : String, progress : Float } -> Organizer Audio.Model 52 84 noteProgress { trackId, progress } model = 53 85 let 54 86 updatedProgressTable = ··· 77 109 -- TODO! 78 110 Return.performance (UI.QueueMsg Queue.Shift) model 79 111 80 - else if model.audioIsPlaying then 112 + else if model.audio.isPlaying then 81 113 communicate (Ports.pause ()) model 82 114 83 115 else ··· 86 118 87 119 setDuration : Float -> Manager 88 120 setDuration duration model = 89 - (case model.tracks.nowPlaying of 90 - Just ( _, track ) -> 91 - { duration = round duration 92 - , msg = UI.Bypass 93 - , track = track 94 - } 95 - |> LastFm.nowPlaying model.lastFm 96 - |> communicate 121 + let 122 + cmd = 123 + case model.tracks.nowPlaying of 124 + Just ( _, track ) -> 125 + LastFm.nowPlaying model.lastFm 126 + { duration = round duration 127 + , msg = UI.Bypass 128 + , track = track 129 + } 97 130 98 - Nothing -> 99 - Return.singleton 100 - ) 101 - { model | audioDuration = duration } 131 + Nothing -> 132 + Cmd.none 133 + in 134 + model 135 + |> Lens.modify lens (\audio -> { audio | duration = duration }) 136 + |> Return.communicate cmd 102 137 103 138 104 - setHasStalled : Bool -> Manager 139 + setHasStalled : Bool -> Organizer Audio.Model 105 140 setHasStalled hasStalled model = 106 - Return.singleton { model | audioHasStalled = hasStalled } 141 + Return.singleton { model | hasStalled = hasStalled } 107 142 108 143 109 - setIsLoading : Bool -> Manager 144 + setIsLoading : Bool -> Organizer Audio.Model 110 145 setIsLoading isLoading model = 111 - Return.singleton { model | audioIsLoading = isLoading } 146 + Return.singleton { model | isLoading = isLoading } 112 147 113 148 114 - setIsPlaying : Bool -> Manager 149 + setIsPlaying : Bool -> Organizer Audio.Model 115 150 setIsPlaying isPlaying model = 116 - Return.singleton { model | audioIsPlaying = isPlaying } 151 + Return.singleton { model | isPlaying = isPlaying } 117 152 118 153 119 - setPosition : Float -> Manager 154 + setPosition : Float -> Organizer Audio.Model 120 155 setPosition position model = 121 - Return.singleton { model | audioPosition = position } 156 + Return.singleton { model | position = position } 122 157 123 158 124 159 stop : Manager
+24
src/Applications/UI/Audio/Types.elm
··· 1 1 module UI.Audio.Types exposing (..) 2 2 3 + import Dict exposing (Dict) 4 + import Management 5 + 6 + 7 + 8 + -- 🌳 9 + 10 + 11 + type alias Model = 12 + { duration : Float 13 + , hasStalled : Bool 14 + , isLoading : Bool 15 + , isPlaying : Bool 16 + , position : Float 17 + 18 + ----------------------------------------- 19 + -- Progress 20 + ----------------------------------------- 21 + , progress : Dict String Float 22 + , rememberProgress : Bool 23 + } 24 + 25 + 26 + 3 27 -- 📣 4 28 5 29
+11 -14
src/Applications/UI/Types.elm
··· 31 31 import LastFm 32 32 import List.Ext as List 33 33 import List.Extra as List 34 + import Management 34 35 import Maybe.Extra as Maybe 35 36 import Notifications exposing (Notification) 36 37 import Playlists.Encoding as Playlists ··· 125 126 , viewport : Viewport 126 127 127 128 ----------------------------------------- 128 - -- Audio 129 - ----------------------------------------- 130 - , audioDuration : Float 131 - , audioHasStalled : Bool 132 - , audioIsLoading : Bool 133 - , audioIsPlaying : Bool 134 - , audioPosition : Float 135 - 136 - -- 137 - , progress : Dict String Float 138 - , rememberProgress : Bool 139 - 140 - ----------------------------------------- 141 129 -- Children 142 130 ----------------------------------------- 143 131 , alfred : Alfred.Model ··· 148 136 , playlists : Playlists.Model 149 137 , sources : Sources.Model 150 138 , tracks : Tracks.Model 139 + 140 + ----------------------------------------- 141 + -- Pieces 142 + ----------------------------------------- 143 + , audio : Audio.Model 151 144 } 152 145 153 146 ··· 226 219 | Audio Audio.Msg 227 220 228 221 222 + type alias Organizer model = 223 + Management.Manager Msg model 224 + 225 + 229 226 type alias Manager = 230 - Model -> ( Model, Cmd Msg ) 227 + Organizer Model
+43
src/Library/Management.elm
··· 1 + module Management exposing (..) 2 + 3 + import Monocle.Lens as Lens exposing (Lens) 4 + 5 + 6 + 7 + -- 📣 8 + 9 + 10 + type alias Manager msg model = 11 + model -> ( model, Cmd msg ) 12 + 13 + 14 + 15 + -- 🔱 16 + 17 + 18 + {-| For working with nested models. 19 + 20 + organize : Manager Msg NestedModel -> Manager Msg Model 21 + organize = 22 + { get = .nested 23 + , set = \nested ui -> { ui | nested = nested } 24 + } 25 + |> Monocle.Lens.Lens 26 + |> Management.organize 27 + 28 + update : Nested.Msg -> Manager Msg Model 29 + update msg = 30 + case msg of 31 + NestedMsg -> 32 + organize handleNestedMsg 33 + 34 + -} 35 + organize : 36 + Lens parent nested 37 + -> Manager msg nested 38 + -> Manager msg parent 39 + organize lens manager parent = 40 + parent 41 + |> lens.get 42 + |> manager 43 + |> Tuple.mapFirst (\nested -> lens.set nested parent)