1module Tracks.Collection.Internal.Arrange exposing (arrange)
2
3import Conditional exposing (ifThenElse)
4import Dict exposing (Dict)
5import List.Extra as List
6import Maybe.Extra as Maybe
7import Playlists exposing (..)
8import Playlists.Matching
9import String.Ext as String
10import Time
11import Time.Ext as Time
12import Tracks exposing (..)
13import Tracks.Sorting as Sorting
14
15
16
17-- 🍯
18
19
20arrange : Parcel -> Parcel
21arrange ( deps, collection ) =
22 case deps.selectedPlaylist of
23 Just playlist ->
24 if playlist.collection then
25 case playlist.autoGenerated of
26 Just _ ->
27 arrangeByGroup ( deps, collection )
28
29 Nothing ->
30 arrangeByCollection ( deps, collection ) playlist
31
32 else
33 arrangeByPlaylist ( deps, collection ) playlist
34
35 Nothing ->
36 arrangeByGroup ( deps, collection )
37
38
39
40-- GROUPING
41
42
43arrangeByGroup : Parcel -> Parcel
44arrangeByGroup ( deps, collection ) =
45 case deps.grouping of
46 Just AddedOn ->
47 ( deps, groupByInsertedAt deps collection )
48
49 Just Directory ->
50 ( deps, groupByDirectory deps collection )
51
52 Just FirstAlphaCharacter ->
53 ( deps, groupByFirstAlphaCharacter deps collection )
54
55 Just TrackYear ->
56 ( deps, groupByYear deps collection )
57
58 Nothing ->
59 collection.identified
60 |> Sorting.sort deps.sortBy deps.sortDirection
61 |> (\x -> { collection | arranged = x })
62 |> (\x -> ( deps, x ))
63
64
65addToList : a -> Maybe (List a) -> Maybe (List a)
66addToList item maybeList =
67 case maybeList of
68 Just list ->
69 Just (item :: list)
70
71 Nothing ->
72 Just [ item ]
73
74
75groupBy : { reversed : Bool } -> (IdentifiedTrack -> Dict a (List IdentifiedTrack) -> Dict a (List IdentifiedTrack)) -> CollectionDependencies -> Collection -> Collection
76groupBy { reversed } folder deps collection =
77 collection.identified
78 |> List.foldl folder Dict.empty
79 |> Dict.values
80 |> ifThenElse reversed List.reverse identity
81 |> List.concatMap (Sorting.sort deps.sortBy deps.sortDirection)
82 |> (\arranged -> { collection | arranged = arranged })
83
84
85
86-- GROUPING ░░ ADDED ON
87
88
89groupByInsertedAt : CollectionDependencies -> Collection -> Collection
90groupByInsertedAt =
91 groupBy { reversed = True } groupByInsertedAtFolder
92
93
94groupByInsertedAtFolder : IdentifiedTrack -> Dict Int (List IdentifiedTrack) -> Dict Int (List IdentifiedTrack)
95groupByInsertedAtFolder ( i, t ) =
96 let
97 ( year, month ) =
98 ( Time.toYear Time.utc t.insertedAt
99 , Time.toMonth Time.utc t.insertedAt
100 )
101
102 group =
103 { name = insertedAtGroupName year month
104 , firstInGroup = False
105 }
106
107 item =
108 ( { i | group = Just group }
109 , t
110 )
111 in
112 Dict.update
113 (year * 1000 + Time.monthNumber month)
114 (addToList item)
115
116
117insertedAtGroupName : Int -> Time.Month -> String
118insertedAtGroupName year month =
119 if year == 1970 then
120 "MANY MOONS AGO"
121
122 else
123 Time.monthName month ++ " " ++ String.fromInt year
124
125
126
127-- GROUPING ░░ DIRECTORY
128
129
130groupByDirectory : CollectionDependencies -> Collection -> Collection
131groupByDirectory deps =
132 groupBy { reversed = False } (groupByDirectoryFolder deps) deps
133
134
135groupByDirectoryFolder : CollectionDependencies -> IdentifiedTrack -> Dict String (List IdentifiedTrack) -> Dict String (List IdentifiedTrack)
136groupByDirectoryFolder deps ( i, t ) =
137 let
138 directory =
139 t.path
140 |> String.chopStart "/"
141 |> String.split "/"
142 |> (case Maybe.andThen .autoGenerated deps.selectedPlaylist of
143 Just { level } ->
144 List.drop (max 0 (level - 1) + 1)
145
146 Nothing ->
147 identity
148 )
149 |> List.init
150 |> Maybe.map (String.join " / ")
151 |> Maybe.withDefault t.path
152
153 group =
154 { name = directory
155 , firstInGroup = False
156 }
157
158 item =
159 ( { i | group = Just group }
160 , t
161 )
162 in
163 Dict.update
164 directory
165 (addToList item)
166
167
168
169-- GROUPING ░░ FIRST LETTER
170
171
172groupByFirstAlphaCharacter : CollectionDependencies -> Collection -> Collection
173groupByFirstAlphaCharacter deps =
174 groupBy { reversed = False } (groupByFirstAlphaCharacterFolder deps) deps
175
176
177groupByFirstAlphaCharacterFolder : CollectionDependencies -> IdentifiedTrack -> Dict String (List IdentifiedTrack) -> Dict String (List IdentifiedTrack)
178groupByFirstAlphaCharacterFolder deps ( i, t ) =
179 let
180 tag =
181 case deps.sortBy of
182 Artist ->
183 Maybe.withDefault fallbackArtist t.tags.artist
184
185 Album ->
186 Maybe.withDefault fallbackAlbum t.tags.album
187
188 PlaylistIndex ->
189 ""
190
191 Title ->
192 t.tags.title
193
194 group =
195 { name =
196 tag
197 |> String.toList
198 |> List.head
199 |> Maybe.andThen
200 (\char ->
201 if Char.isAlpha char then
202 Just (String.fromList [ Char.toUpper char ])
203
204 else
205 Nothing
206 )
207 |> Maybe.withDefault "#"
208 , firstInGroup = False
209 }
210
211 item =
212 ( { i | group = Just group }
213 , t
214 )
215 in
216 Dict.update
217 group.name
218 (addToList item)
219
220
221
222-- GROUPING ░░ YEAR
223
224
225groupByYear : CollectionDependencies -> Collection -> Collection
226groupByYear =
227 groupBy { reversed = True } groupByYearFolder
228
229
230groupByYearFolder : IdentifiedTrack -> Dict Int (List IdentifiedTrack) -> Dict Int (List IdentifiedTrack)
231groupByYearFolder ( i, t ) =
232 let
233 group =
234 { name = Maybe.unwrap "0000 - Unknown" String.fromInt t.tags.year
235 , firstInGroup = False
236 }
237
238 item =
239 ( { i | group = Just group }
240 , t
241 )
242 in
243 Dict.update
244 (Maybe.withDefault 0 t.tags.year)
245 (addToList item)
246
247
248
249-- PLAYLISTS
250
251
252arrangeByCollection : Parcel -> Playlist -> Parcel
253arrangeByCollection ( deps, collection ) playlist =
254 collection.identified
255 |> Playlists.Matching.match playlist
256 |> dealWithMissingPlaylistTracks
257 |> Sorting.sort deps.sortBy deps.sortDirection
258 |> (\x -> { collection | arranged = x })
259 |> (\x -> ( deps, x ))
260
261
262arrangeByPlaylist : Parcel -> Playlist -> Parcel
263arrangeByPlaylist ( deps, collection ) playlist =
264 collection.identified
265 |> Playlists.Matching.match playlist
266 |> dealWithMissingPlaylistTracks
267 |> Sorting.sort PlaylistIndex Asc
268 |> (\x -> { collection | arranged = x })
269 |> (\x -> ( deps, x ))
270
271
272dealWithMissingPlaylistTracks : ( List IdentifiedTrack, List IdentifiedPlaylistTrack ) -> List IdentifiedTrack
273dealWithMissingPlaylistTracks ( identifiedTracks, remainingPlaylistTracks ) =
274 identifiedTracks ++ List.map makeMissingPlaylistTrack remainingPlaylistTracks
275
276
277makeMissingPlaylistTrack : IdentifiedPlaylistTrack -> IdentifiedTrack
278makeMissingPlaylistTrack ( identifiers, playlistTrack ) =
279 let
280 tags =
281 { disc = 1
282 , nr = 0
283 , artist = playlistTrack.artist
284 , title = playlistTrack.title
285 , album = playlistTrack.album
286 , genre = Nothing
287 , picture = Nothing
288 , year = Nothing
289 }
290 in
291 Tuple.pair
292 { filename = ""
293 , group = Nothing
294 , indexInList = 0
295 , indexInPlaylist = Just identifiers.index
296 , isFavourite = False
297 , isMissing = True
298 , parentDirectory = ""
299 }
300 { tags = tags
301 , id = missingId
302 , insertedAt = Time.default
303 , path = missingId
304 , sourceId = missingId
305 }