@@ -91,10 +91,13 @@ enum PlaylistParser {
9191
9292 private static func parsePlaylistHeader( _ data: [ String : Any ] ) -> HeaderData {
9393 var header = HeaderData ( )
94- // Try musicDetailHeaderRenderer
95- if let headerDict = data [ " header " ] as? [ String : Any ] ,
96- let headerRenderer = headerDict [ " musicDetailHeaderRenderer " ] as? [ String : Any ]
97- {
94+
95+ guard let headerDict = data [ " header " ] as? [ String : Any ] else {
96+ return header
97+ }
98+
99+ // Try musicDetailHeaderRenderer (most common for playlists)
100+ if let headerRenderer = headerDict [ " musicDetailHeaderRenderer " ] as? [ String : Any ] {
98101 if let text = ParsingHelpers . extractTitle ( from: headerRenderer) {
99102 header. title = text
100103 }
@@ -123,40 +126,69 @@ enum PlaylistParser {
123126 }
124127 }
125128
126- // Try musicImmersiveHeaderRenderer
127- if header. title == " Unknown Playlist " ,
128- let headerDict = data [ " header " ] as? [ String : Any ] ,
129- let immersiveHeader = headerDict [ " musicImmersiveHeaderRenderer " ] as? [ String : Any ]
130- {
131- if let text = ParsingHelpers . extractTitle ( from: immersiveHeader) {
129+ // Try musicImmersiveHeaderRenderer (used by some albums)
130+ if let immersiveHeader = headerDict [ " musicImmersiveHeaderRenderer " ] as? [ String : Any ] {
131+ if header. title == " Unknown Playlist " ,
132+ let text = ParsingHelpers . extractTitle ( from: immersiveHeader)
133+ {
132134 header. title = text
133135 }
134136
135- let thumbnails = ParsingHelpers . extractThumbnails ( from: immersiveHeader)
136- header. thumbnailURL = thumbnails. last. flatMap { URL ( string: $0) }
137+ // Always try to get thumbnail from immersive header if we don't have one
138+ if header. thumbnailURL == nil {
139+ let thumbnails = ParsingHelpers . extractThumbnails ( from: immersiveHeader)
140+ header. thumbnailURL = thumbnails. last. flatMap { URL ( string: $0) }
141+ }
137142
138- if let descData = immersiveHeader [ " description " ] as? [ String : Any ] ,
143+ if header. description == nil ,
144+ let descData = immersiveHeader [ " description " ] as? [ String : Any ] ,
139145 let runs = descData [ " runs " ] as? [ [ String : Any ] ]
140146 {
141147 header. description = runs. compactMap { $0 [ " text " ] as? String } . joined ( )
142148 }
149+
150+ // Try subtitle for author if not set
151+ if header. author == nil ,
152+ let subtitleData = immersiveHeader [ " subtitle " ] as? [ String : Any ] ,
153+ let runs = subtitleData [ " runs " ] as? [ [ String : Any ] ]
154+ {
155+ let texts = runs. compactMap { $0 [ " text " ] as? String }
156+ header. author = texts. first
157+ }
158+ }
159+
160+ // Try musicVisualHeaderRenderer (alternative format for some albums/artists)
161+ if let visualHeader = headerDict [ " musicVisualHeaderRenderer " ] as? [ String : Any ] {
162+ if header. title == " Unknown Playlist " ,
163+ let text = ParsingHelpers . extractTitle ( from: visualHeader)
164+ {
165+ header. title = text
166+ }
167+
168+ if header. thumbnailURL == nil {
169+ let thumbnails = ParsingHelpers . extractThumbnails ( from: visualHeader)
170+ header. thumbnailURL = thumbnails. last. flatMap { URL ( string: $0) }
171+ }
143172 }
144173
145- // Try musicEditablePlaylistDetailHeaderRenderer
146- if header. title == " Unknown Playlist " ,
147- let headerDict = data [ " header " ] as? [ String : Any ] ,
148- let editableHeader = headerDict [ " musicEditablePlaylistDetailHeaderRenderer " ] as? [ String : Any ] ,
174+ // Try musicEditablePlaylistDetailHeaderRenderer (for user-editable playlists)
175+ if let editableHeader = headerDict [ " musicEditablePlaylistDetailHeaderRenderer " ] as? [ String : Any ] ,
149176 let nestedHeaderData = editableHeader [ " header " ] as? [ String : Any ] ,
150177 let detailHeader = nestedHeaderData [ " musicDetailHeaderRenderer " ] as? [ String : Any ]
151178 {
152- if let text = ParsingHelpers . extractTitle ( from: detailHeader) {
179+ if header. title == " Unknown Playlist " ,
180+ let text = ParsingHelpers . extractTitle ( from: detailHeader)
181+ {
153182 header. title = text
154183 }
155184
156- let thumbnails = ParsingHelpers . extractThumbnails ( from: detailHeader)
157- header. thumbnailURL = thumbnails. last. flatMap { URL ( string: $0) }
185+ if header. thumbnailURL == nil {
186+ let thumbnails = ParsingHelpers . extractThumbnails ( from: detailHeader)
187+ header. thumbnailURL = thumbnails. last. flatMap { URL ( string: $0) }
188+ }
158189
159- if let subtitleData = detailHeader [ " subtitle " ] as? [ String : Any ] ,
190+ if header. author == nil ,
191+ let subtitleData = detailHeader [ " subtitle " ] as? [ String : Any ] ,
160192 let runs = subtitleData [ " runs " ] as? [ [ String : Any ] ]
161193 {
162194 let texts = runs. compactMap { $0 [ " text " ] as? String }
@@ -268,12 +300,13 @@ enum PlaylistParser {
268300 let thumbnails = ParsingHelpers . extractThumbnails ( from: responsiveRenderer)
269301 let thumbnailURL = thumbnails. last. flatMap { URL ( string: $0) } ?? fallbackThumbnailURL
270302 let duration = ParsingHelpers . extractDurationFromFlexColumns ( responsiveRenderer)
303+ let album = ParsingHelpers . extractAlbumFromFlexColumns ( responsiveRenderer)
271304
272305 return Song (
273306 id: videoId,
274307 title: title,
275308 artists: artists,
276- album: nil ,
309+ album: album ,
277310 duration: duration,
278311 thumbnailURL: thumbnailURL,
279312 videoId: videoId
0 commit comments