@@ -54,6 +54,92 @@ List onlinePlaylists = [];
5454final currentLikedPlaylistsLength = ValueNotifier <int >(
5555 userLikedPlaylists.length,
5656);
57+ bool _didSanitizePlaylistIdentifiers = false ;
58+
59+ void _ensurePlaylistIdentifiersSanitized () {
60+ if (_didSanitizePlaylistIdentifiers) return ;
61+ _didSanitizePlaylistIdentifiers = true ;
62+ _sanitizePlaylistIdentifiers ();
63+ }
64+
65+ bool _isMissingPlaylistId (dynamic playlistId) {
66+ final normalized = playlistId? .toString ().trim ();
67+ return normalized == null || normalized.isEmpty || normalized == 'null' ;
68+ }
69+
70+ /// Repairs persisted custom playlist entries that have missing/null ids.
71+ ///
72+ /// This prevents navigation paths like `/home/playlist/null` from stale,
73+ /// previously corrupted data.
74+ void _sanitizePlaylistIdentifiers () {
75+ var customChanged = false ;
76+ final normalizedCustom = userCustomPlaylists.value.map ((item) {
77+ if (item is ! Map ) return item;
78+ final source = item['source' ]? .toString ();
79+ final shouldRepair =
80+ source == null || source == 'user-created' || source == 'custom' ;
81+ if (! shouldRepair || ! _isMissingPlaylistId (item['ytid' ])) {
82+ return item;
83+ }
84+
85+ customChanged = true ;
86+ return < String , dynamic > {
87+ ...Map <String , dynamic >.from (item.cast <dynamic , dynamic >()),
88+ 'ytid' : generateCustomPlaylistId (),
89+ 'source' : source ?? 'user-created' ,
90+ };
91+ }).toList ();
92+
93+ if (customChanged) {
94+ userCustomPlaylists.value = normalizedCustom;
95+ unawaited (addOrUpdateData ('user' , 'customPlaylists' , normalizedCustom));
96+ }
97+
98+ var foldersChanged = false ;
99+ final normalizedFolders = userPlaylistFolders.value.map ((folder) {
100+ if (folder is ! Map ) return folder;
101+
102+ final folderMap = Map <String , dynamic >.from (
103+ folder.cast <dynamic , dynamic >(),
104+ );
105+ final folderPlaylists = folderMap['playlists' ] as List <dynamic >? ?? [];
106+ var folderChanged = false ;
107+
108+ final normalizedPlaylists = folderPlaylists.map ((playlist) {
109+ if (playlist is ! Map ) return playlist;
110+
111+ final playlistMap = Map <String , dynamic >.from (
112+ playlist.cast <dynamic , dynamic >(),
113+ );
114+ final source = playlistMap['source' ]? .toString ();
115+ final shouldRepair =
116+ source == null || source == 'user-created' || source == 'custom' ;
117+
118+ if (! shouldRepair || ! _isMissingPlaylistId (playlistMap['ytid' ])) {
119+ return playlistMap;
120+ }
121+
122+ folderChanged = true ;
123+ return < String , dynamic > {
124+ ...playlistMap,
125+ 'ytid' : generateCustomPlaylistId (),
126+ 'source' : source ?? 'user-created' ,
127+ };
128+ }).toList ();
129+
130+ if (folderChanged) {
131+ foldersChanged = true ;
132+ folderMap['playlists' ] = normalizedPlaylists;
133+ }
134+
135+ return folderMap;
136+ }).toList ();
137+
138+ if (foldersChanged) {
139+ userPlaylistFolders.value = normalizedFolders;
140+ unawaited (addOrUpdateData ('user' , 'playlistFolders' , normalizedFolders));
141+ }
142+ }
57143
58144String generateCustomPlaylistId () {
59145 final timestamp = DateTime .now ().microsecondsSinceEpoch;
@@ -503,6 +589,7 @@ String deletePlaylistFolder(String folderId, [BuildContext? context]) {
503589}
504590
505591List <Map > getPlaylistsInFolder (String folderId) {
592+ _ensurePlaylistIdentifiersSanitized ();
506593 try {
507594 final folder = userPlaylistFolders.value.firstWhere (
508595 (folder) => folder['id' ] == folderId,
@@ -520,6 +607,7 @@ List<Map> getPlaylistsInFolder(String folderId) {
520607}
521608
522609List <Map > getPlaylistsNotInFolders () {
610+ _ensurePlaylistIdentifiersSanitized ();
523611 final playlistsInFolders = < String > {};
524612 for (final folder in userPlaylistFolders.value) {
525613 final folderPlaylists = folder['playlists' ] as List <dynamic >? ?? [];
@@ -545,6 +633,7 @@ Future<List> getPlaylists({
545633 bool onlyLiked = false ,
546634 String type = 'all' ,
547635}) async {
636+ _ensurePlaylistIdentifiersSanitized ();
548637 if (onlyLiked) {
549638 if (playlistsNum != null ) {
550639 return userLikedPlaylists.take (playlistsNum).toList ();
@@ -715,12 +804,15 @@ Future<Map?> getPlaylistInfoForWidget(
715804 dynamic id, {
716805 bool isArtist = false ,
717806}) async {
807+ _ensurePlaylistIdentifiersSanitized ();
718808 if (id == null ) return null ;
719- if (isArtist) return _fetchArtistPlaylist (id.toString ());
720- if (id.toString ().startsWith ('customId-' )) {
721- return _findCustomPlaylist (id.toString ());
809+ final normalizedId = id.toString ().trim ();
810+ if (normalizedId.isEmpty || normalizedId == 'null' ) return null ;
811+ if (isArtist) return _fetchArtistPlaylist (normalizedId);
812+ if (normalizedId.startsWith ('customId-' )) {
813+ return _findCustomPlaylist (normalizedId);
722814 }
723- return _fetchYouTubePlaylist (id. toString () );
815+ return _fetchYouTubePlaylist (normalizedId );
724816}
725817
726818Future <Map > _fetchArtistPlaylist (String artistName) async {
0 commit comments