forked from
tokono.ma/diffuse
A music player that connects to your cloud/distributed storage.
1module UI.Tracks.Covers exposing (..)
2
3import Base64
4import Conditional exposing (ifThenElse)
5import Dict
6import Maybe.Extra as Maybe
7import Tracks exposing (..)
8
9
10
11-- 🔱
12
13
14generate :
15 SortBy
16 -> Tracks.Collection
17 -> CoverCollection
18generate sortBy tracks =
19 let
20 groupFn =
21 coverGroup sortBy
22
23 makeCoverFn =
24 makeCover sortBy
25 in
26 tracks.arranged
27 |> List.foldr
28 (\identifiedTrack { covers, gathering } ->
29 let
30 group =
31 groupFn identifiedTrack
32
33 ( identifiers, track ) =
34 identifiedTrack
35
36 { artist, album } =
37 track.tags
38 in
39 if group /= gathering.previousGroup then
40 -- New group, make cover for previous group
41 let
42 collection =
43 makeCoverFn gathering covers
44 in
45 { gathering =
46 { acc = [ identifiedTrack ]
47 , accIds = [ track.id ]
48 , previousGroup = group
49 , previousTrack = track
50
51 --
52 , currentAlbumSequence = Just ( identifiedTrack, 1 )
53 , largestAlbumSequence = Nothing
54
55 --
56 , currentAlbumFavsSequence = Just ( identifiedTrack, ifThenElse identifiers.isFavourite 1 0 )
57 , largestAlbumFavsSequence = Nothing
58
59 --
60 , currentArtistSequence = Just ( identifiedTrack, 1 )
61 , largestArtistSequence = Nothing
62 }
63 , covers =
64 collection
65 }
66
67 else
68 -- Same group
69 { gathering =
70 { acc = identifiedTrack :: gathering.acc
71 , accIds = track.id :: gathering.accIds
72 , previousGroup = group
73 , previousTrack = track
74
75 -- Album sequence
76 -----------------
77 , currentAlbumSequence =
78 if album /= gathering.previousTrack.tags.album then
79 Just ( identifiedTrack, 1 )
80
81 else
82 increaseSequence gathering.currentAlbumSequence
83
84 --
85 , largestAlbumSequence =
86 if album /= gathering.previousTrack.tags.album then
87 resolveLargestSequence
88 gathering.currentAlbumSequence
89 gathering.largestAlbumSequence
90
91 else
92 gathering.largestAlbumSequence
93
94 -- Album favourites sequence
95 ----------------------------
96 , currentAlbumFavsSequence =
97 if album /= gathering.previousTrack.tags.album then
98 Just ( identifiedTrack, ifThenElse identifiers.isFavourite 1 0 )
99
100 else if identifiers.isFavourite then
101 increaseSequence gathering.currentAlbumFavsSequence
102
103 else
104 gathering.currentAlbumFavsSequence
105
106 --
107 , largestAlbumFavsSequence =
108 if album /= gathering.previousTrack.tags.album then
109 resolveLargestSequence
110 gathering.currentAlbumFavsSequence
111 gathering.largestAlbumFavsSequence
112
113 else
114 gathering.largestAlbumFavsSequence
115
116 -- Artist sequence
117 ------------------
118 , currentArtistSequence =
119 if artist /= gathering.previousTrack.tags.artist then
120 Just ( identifiedTrack, 1 )
121
122 else
123 increaseSequence gathering.currentArtistSequence
124
125 --
126 , largestArtistSequence =
127 if artist /= gathering.previousTrack.tags.artist then
128 resolveLargestSequence
129 gathering.currentArtistSequence
130 gathering.largestArtistSequence
131
132 else
133 gathering.largestArtistSequence
134 }
135 , covers =
136 covers
137 }
138 )
139 { covers =
140 []
141 , gathering =
142 { acc = []
143 , accIds = []
144 , previousGroup = ""
145 , previousTrack = emptyTrack
146
147 --
148 , currentAlbumSequence = Nothing
149 , largestAlbumSequence = Nothing
150 , currentAlbumFavsSequence = Nothing
151 , largestAlbumFavsSequence = Nothing
152 , currentArtistSequence = Nothing
153 , largestArtistSequence = Nothing
154 }
155 }
156 |> (\{ covers, gathering } ->
157 makeCoverFn gathering covers
158 )
159 |> (\collection ->
160 { arranged = collection, harvested = [] }
161 )
162
163
164harvest :
165 Maybe Cover
166 -> SortBy
167 -> Tracks.Collection
168 -> CoverCollection
169 -> ( CoverCollection, Maybe Cover )
170harvest previouslySelectedCover sortBy tracks covers =
171 let
172 groupFn =
173 coverGroup sortBy
174
175 ( groups, tracksPerGroup ) =
176 List.foldr
177 (\identifiedTrack ( acc, dict ) ->
178 let
179 group =
180 groupFn identifiedTrack
181 in
182 ( if Dict.member group dict == False then
183 group :: acc
184
185 else
186 acc
187 --
188 , Dict.update group
189 (Maybe.unwrap [ identifiedTrack ] ((::) identifiedTrack) >> Just)
190 dict
191 )
192 )
193 ( [], Dict.empty )
194 tracks.harvested
195 in
196 covers.arranged
197 |> List.foldr
198 (\cover ( acc, sel ) ->
199 if List.member cover.group groups then
200 let
201 groupTracks =
202 Maybe.withDefault [] (Dict.get cover.group tracksPerGroup)
203
204 trackIds =
205 List.map (Tuple.second >> .id) groupTracks
206
207 harvestedCover =
208 { cover | tracks = groupTracks, trackIds = trackIds }
209 in
210 case ( previouslySelectedCover, sel ) of
211 ( Just pre, Nothing ) ->
212 ( harvestedCover :: acc
213 , if pre.key == harvestedCover.key then
214 Just harvestedCover
215
216 else
217 Nothing
218 )
219
220 ( Just _, Just s ) ->
221 ( harvestedCover :: acc
222 , Just s
223 )
224
225 ( Nothing, _ ) ->
226 ( harvestedCover :: acc
227 , Nothing
228 )
229
230 else
231 ( acc
232 , sel
233 )
234 )
235 ( []
236 , Nothing
237 )
238 |> Tuple.mapFirst
239 (\h -> { covers | harvested = h })
240
241
242
243-- ⚗️
244
245
246makeCover sortBy_ gathering collection =
247 let
248 closedGathering =
249 { gathering
250 | largestAlbumSequence =
251 resolveLargestSequence
252 gathering.currentAlbumSequence
253 gathering.largestAlbumSequence
254
255 --
256 , largestAlbumFavsSequence =
257 resolveLargestSequence
258 gathering.currentAlbumFavsSequence
259 gathering.largestAlbumFavsSequence
260
261 --
262 , largestArtistSequence =
263 resolveLargestSequence
264 gathering.currentArtistSequence
265 gathering.largestArtistSequence
266 }
267 in
268 case closedGathering.acc of
269 [] ->
270 collection
271
272 fallback :: _ ->
273 makeCoverWithFallback sortBy_ closedGathering fallback :: collection
274
275
276makeCoverWithFallback _ gathering fallback =
277 let
278 amountOfTracks =
279 List.length gathering.accIds
280
281 group =
282 gathering.previousGroup
283
284 identifiedTrack : IdentifiedTrack
285 identifiedTrack =
286 gathering.largestAlbumFavsSequence
287 |> Maybe.orElse gathering.largestAlbumSequence
288 |> Maybe.map Tuple.first
289 |> Maybe.withDefault fallback
290
291 ( _, track ) =
292 identifiedTrack
293
294 ( largestAlbumSequence, largestArtistSequence ) =
295 ( Maybe.unwrap 0 Tuple.second gathering.largestAlbumSequence
296 , Maybe.unwrap 0 Tuple.second gathering.largestArtistSequence
297 )
298
299 ( sameAlbum, sameArtist ) =
300 ( largestAlbumSequence == amountOfTracks
301 , largestArtistSequence == amountOfTracks
302 )
303
304 isVariousArtists =
305 False
306 || (amountOfTracks > 4 && largestArtistSequence < 3)
307 || (Maybe.map String.toLower track.tags.artist == Just "va")
308 in
309 { key = Base64.encode (coverKey isVariousArtists track)
310 , identifiedTrackCover = identifiedTrack
311
312 --
313 , group = group
314 , sameAlbum = sameAlbum
315 , sameArtist = sameArtist
316
317 --
318 , trackIds = gathering.accIds
319 , tracks = gathering.acc
320 , variousArtists = isVariousArtists
321 }
322
323
324
325-- ⚗️ ░░ SEQUENCES
326
327
328increaseSequence =
329 Maybe.map (Tuple.mapSecond ((+) 1))
330
331
332resolveLargestSequence curr state =
333 case ( curr, state ) of
334 ( Just ( _, c ), Just ( _, s ) ) ->
335 ifThenElse (c > s) curr state
336
337 ( Just _, Nothing ) ->
338 curr
339
340 ( Nothing, Just _ ) ->
341 state
342
343 ( Nothing, Nothing ) ->
344 Nothing