1module UI.Queue.Fill exposing (State, cleanAutoGenerated, ordered, queueLength, shuffled)
2
3{-| These functions will return a new list for the `future` property.
4-}
5
6import Array
7import List.Extra as List
8import Maybe.Ext as Maybe
9import Maybe.Extra as Maybe
10import Queue exposing (Item, makeItem)
11import Random exposing (Generator, Seed)
12import Time
13import Tracks exposing (IdentifiedTrack)
14
15
16
17-- ⛩
18
19
20queueLength : Int
21queueLength =
22 30
23
24
25
26-- 🌳
27
28
29type alias State =
30 { activeItem : Maybe Item
31 , future : List Item
32 , ignored : List Item
33 , past : List Item
34 }
35
36
37
38-- 🔱 ░░ ORDERED
39
40
41ordered : Time.Posix -> List IdentifiedTrack -> State -> List Item
42ordered _ unfilteredTracks state =
43 let
44 tracks =
45 state.ignored
46 |> List.map itemTrackId
47 |> Tuple.pair []
48 |> purifier unfilteredTracks
49 |> Tuple.first
50
51 manualEntries =
52 List.filter (.manualEntry >> (==) True) state.future
53
54 remaining =
55 max (queueLength - List.length manualEntries) 0
56
57 focus =
58 Maybe.preferFirst (List.last manualEntries) state.activeItem
59 in
60 case focus of
61 Just item ->
62 let
63 maybeNowPlayingIndex =
64 List.findIndex
65 (Tracks.isNowPlaying item.identifiedTrack)
66 tracks
67 in
68 maybeNowPlayingIndex
69 |> Maybe.map (\idx -> List.drop (idx + 1) tracks)
70 |> Maybe.withDefault tracks
71 |> List.take remaining
72 |> (\a ->
73 let
74 actualRemaining =
75 remaining - List.length a
76
77 n =
78 Maybe.withDefault (List.length tracks) maybeNowPlayingIndex
79 in
80 a ++ List.take (min n actualRemaining) tracks
81 )
82 |> List.map (makeItem False)
83 |> List.append manualEntries
84
85 Nothing ->
86 tracks
87 |> List.take remaining
88 |> List.map (makeItem False)
89 |> List.append manualEntries
90
91
92
93-- 🔱 ░░ SHUFFLED
94
95
96shuffled : Time.Posix -> List IdentifiedTrack -> State -> List Item
97shuffled timestamp unfilteredTracks state =
98 let
99 idsToIgnoreWithoutPast =
100 [ state.ignored
101 , Maybe.unwrap [] List.singleton state.activeItem
102 ]
103 |> List.map (List.map itemTrackId)
104 |> List.concat
105 |> List.unique
106
107 ( tracksWithPast, idsToIgnoreWithoutPastAfterFilter ) =
108 purifier unfilteredTracks ( [], idsToIgnoreWithoutPast )
109
110 idsToIgnoreWithPast =
111 List.unique (idsToIgnoreWithoutPastAfterFilter ++ List.map itemTrackId state.past)
112
113 ( tracksWithoutPast, idsToIgnoreWithPastAfterFilter ) =
114 purifier tracksWithPast ( [], idsToIgnoreWithPast )
115
116 idsToIgnoreWithFuture =
117 List.unique (idsToIgnoreWithoutPastAfterFilter ++ List.map itemTrackId state.future)
118
119 ( tracksList, _ ) =
120 purifier
121 (case tracksWithoutPast of
122 [] ->
123 tracksWithPast
124
125 t ->
126 t
127 )
128 ( [], idsToIgnoreWithFuture )
129
130 tracks =
131 Array.fromList tracksList
132
133 amountOfTracks =
134 Array.length tracks
135
136 generator =
137 Random.int 0 (amountOfTracks - 1)
138
139 toAmount =
140 max (queueLength - List.length state.future) 0
141
142 howMany =
143 min toAmount amountOfTracks
144 in
145 if howMany > 0 then
146 timestamp
147 |> Time.posixToMillis
148 |> Random.initialSeed
149 |> generateIndexes generator howMany []
150 |> List.foldl
151 (\idx acc ->
152 case Array.get idx tracks of
153 Just track ->
154 makeItem False track :: acc
155
156 Nothing ->
157 acc
158 )
159 []
160 |> List.append state.future
161
162 else
163 state.future
164
165
166
167-- 🔱
168
169
170cleanAutoGenerated : Bool -> String -> List Item -> List Item
171cleanAutoGenerated shuffle trackId future =
172 if shuffle then
173 List.filterNot
174 (\i -> i.manualEntry == False && itemTrackId i == trackId)
175 future
176
177 else
178 future
179
180
181
182-- ㊙️
183
184
185{-| Generated random indexes.
186
187 `squirrel` = accumulator, ie. collected indexes
188
189-}
190generateIndexes : Generator Int -> Int -> List Int -> Seed -> List Int
191generateIndexes generator howMany squirrel seed =
192 let
193 ( index, newSeed ) =
194 Random.step generator seed
195 in
196 if List.member index squirrel then
197 generateIndexes generator howMany squirrel newSeed
198
199 else if howMany - 1 > 0 then
200 generateIndexes generator (howMany - 1) (index :: squirrel) newSeed
201
202 else
203 index :: squirrel
204
205
206
207-- PURIFY
208
209
210purifier :
211 List IdentifiedTrack
212 -> ( List IdentifiedTrack, List String )
213 -> ( List IdentifiedTrack, List String )
214purifier tracks ( acc, idsToIgnore ) =
215 case idsToIgnore of
216 [] ->
217 -- Nothing more to ignore,
218 -- stop here.
219 ( acc ++ tracks, [] )
220
221 _ ->
222 case tracks of
223 [] ->
224 -- No more tracks left,
225 -- end of the road.
226 ( acc, idsToIgnore )
227
228 (( _, track ) as identifiedTrack) :: rest ->
229 case List.elemIndex track.id idsToIgnore of
230 Just ignoreIdx ->
231 -- It's a track to ignore,
232 -- remove it from the ignore list and carry on.
233 purifier
234 rest
235 ( acc, List.removeAt ignoreIdx idsToIgnore )
236
237 Nothing ->
238 -- It's not a track to ignore,
239 -- add it to the to-keep list and carry on.
240 purifier
241 rest
242 ( identifiedTrack :: acc, idsToIgnore )
243
244
245
246-- COMMON
247
248
249itemTrackId : Item -> String
250itemTrackId =
251 .identifiedTrack >> Tuple.second >> .id