Skip to content

Commit 80e0c6a

Browse files
authored
Merge pull request #9755 from Jared234/9458_faulty_playlist_thumbnail_update
Fixed a bug that caused erroneous updates of the playlist thumbnails
2 parents f1a071b + 9067c77 commit 80e0c6a

11 files changed

Lines changed: 893 additions & 72 deletions

File tree

app/schemas/org.schabi.newpipe.database.AppDatabase/7.json

Lines changed: 737 additions & 0 deletions
Large diffs are not rendered by default.

app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ class DatabaseMigrationTest {
101101
Migrations.MIGRATION_5_6
102102
)
103103

104+
testHelper.runMigrationsAndValidate(
105+
AppDatabase.DATABASE_NAME,
106+
Migrations.DB_VER_7,
107+
true,
108+
Migrations.MIGRATION_6_7
109+
)
110+
104111
val migratedDatabaseV3 = getMigratedDatabase()
105112
val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst()
106113

app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.schabi.newpipe.database.Migrations.MIGRATION_3_4;
77
import static org.schabi.newpipe.database.Migrations.MIGRATION_4_5;
88
import static org.schabi.newpipe.database.Migrations.MIGRATION_5_6;
9+
import static org.schabi.newpipe.database.Migrations.MIGRATION_6_7;
910

1011
import android.content.Context;
1112
import android.database.Cursor;
@@ -26,7 +27,7 @@ private static AppDatabase getDatabase(final Context context) {
2627
return Room
2728
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
2829
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5,
29-
MIGRATION_5_6)
30+
MIGRATION_5_6, MIGRATION_6_7)
3031
.build();
3132
}
3233

app/src/main/java/org/schabi/newpipe/database/AppDatabase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.schabi.newpipe.database;
22

3-
import static org.schabi.newpipe.database.Migrations.DB_VER_6;
3+
import static org.schabi.newpipe.database.Migrations.DB_VER_7;
44

55
import androidx.room.Database;
66
import androidx.room.RoomDatabase;
@@ -38,7 +38,7 @@
3838
FeedEntity.class, FeedGroupEntity.class, FeedGroupSubscriptionEntity.class,
3939
FeedLastUpdatedEntity.class
4040
},
41-
version = DB_VER_6
41+
version = DB_VER_7
4242
)
4343
public abstract class AppDatabase extends RoomDatabase {
4444
public static final String DATABASE_NAME = "newpipe.db";

app/src/main/java/org/schabi/newpipe/database/Migrations.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public final class Migrations {
2424
public static final int DB_VER_4 = 4;
2525
public static final int DB_VER_5 = 5;
2626
public static final int DB_VER_6 = 6;
27+
public static final int DB_VER_7 = 7;
2728

2829
private static final String TAG = Migrations.class.getName();
2930
public static final boolean DEBUG = MainActivity.DEBUG;
@@ -197,6 +198,43 @@ public void migrate(@NonNull final SupportSQLiteDatabase database) {
197198
}
198199
};
199200

201+
public static final Migration MIGRATION_6_7 = new Migration(DB_VER_6, DB_VER_7) {
202+
@Override
203+
public void migrate(@NonNull final SupportSQLiteDatabase database) {
204+
// Create a new column thumbnail_stream_id
205+
database.execSQL("ALTER TABLE `playlists` ADD COLUMN `thumbnail_stream_id` "
206+
+ "INTEGER NOT NULL DEFAULT -1");
207+
208+
// Migrate the thumbnail_url to the thumbnail_stream_id
209+
database.execSQL("UPDATE playlists SET thumbnail_stream_id = ("
210+
+ " SELECT CASE WHEN COUNT(*) != 0 then stream_uid ELSE -1 END"
211+
+ " FROM ("
212+
+ " SELECT p.uid AS playlist_uid, s.uid AS stream_uid"
213+
+ " FROM playlists p"
214+
+ " LEFT JOIN playlist_stream_join ps ON p.uid = ps.playlist_id"
215+
+ " LEFT JOIN streams s ON s.uid = ps.stream_id"
216+
+ " WHERE s.thumbnail_url = p.thumbnail_url) AS temporary_table"
217+
+ " WHERE playlist_uid = playlists.uid)");
218+
219+
// Remove the thumbnail_url field in the playlist table
220+
database.execSQL("CREATE TABLE IF NOT EXISTS `playlists_new`"
221+
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
222+
+ "name TEXT, "
223+
+ "is_thumbnail_permanent INTEGER NOT NULL, "
224+
+ "thumbnail_stream_id INTEGER NOT NULL)");
225+
226+
database.execSQL("INSERT INTO playlists_new"
227+
+ " SELECT uid, name, is_thumbnail_permanent, thumbnail_stream_id "
228+
+ " FROM playlists");
229+
230+
231+
database.execSQL("DROP TABLE playlists");
232+
database.execSQL("ALTER TABLE playlists_new RENAME TO playlists");
233+
database.execSQL("CREATE INDEX IF NOT EXISTS "
234+
+ "`index_playlists_name` ON `playlists` (`name`)");
235+
}
236+
};
237+
200238
private Migrations() {
201239
}
202240
}

app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
1010
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
1111
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
12+
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
1213
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
1314

1415
import java.util.List;
@@ -17,9 +18,11 @@
1718

1819
import static org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry.PLAYLIST_TIMES_STREAM_IS_CONTAINED;
1920
import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT;
21+
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.DEFAULT_THUMBNAIL;
2022
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
2123
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
2224
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;
25+
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_STREAM_ID;
2326
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
2427
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX;
2528
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID;
@@ -57,14 +60,15 @@ default Flowable<List<PlaylistStreamEntity>> listByService(final int serviceId)
5760
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId")
5861
Flowable<Integer> getMaximumIndexOf(long playlistId);
5962

60-
@Query("SELECT CASE WHEN COUNT(*) != 0 then " + STREAM_THUMBNAIL_URL + " ELSE :defaultUrl END"
63+
@Query("SELECT CASE WHEN COUNT(*) != 0 then " + STREAM_ID
64+
+ " ELSE " + PlaylistEntity.DEFAULT_THUMBNAIL_ID + " END"
6165
+ " FROM " + STREAM_TABLE
6266
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
6367
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
6468
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId "
6569
+ " LIMIT 1"
6670
)
67-
Flowable<String> getAutomaticThumbnailUrl(long playlistId, String defaultUrl);
71+
Flowable<Long> getAutomaticThumbnailStreamId(long playlistId);
6872

6973
@RewriteQueriesToDropUnusedColumns
7074
@Transaction
@@ -87,20 +91,34 @@ default Flowable<List<PlaylistStreamEntity>> listByService(final int serviceId)
8791
Flowable<List<PlaylistStreamEntry>> getOrderedStreamsOf(long playlistId);
8892

8993
@Transaction
90-
@Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + PLAYLIST_THUMBNAIL_URL + ", "
91-
+ "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT
94+
@Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ","
95+
96+
+ " CASE WHEN " + PLAYLIST_THUMBNAIL_STREAM_ID + " = "
97+
+ PlaylistEntity.DEFAULT_THUMBNAIL_ID + " THEN " + "'" + DEFAULT_THUMBNAIL + "'"
98+
+ " ELSE (SELECT " + STREAM_THUMBNAIL_URL
99+
+ " FROM " + STREAM_TABLE
100+
+ " WHERE " + STREAM_TABLE + "." + STREAM_ID + " = " + PLAYLIST_THUMBNAIL_STREAM_ID
101+
+ " ) END AS " + PLAYLIST_THUMBNAIL_URL + ", "
92102

103+
+ "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT
93104
+ " FROM " + PLAYLIST_TABLE
94105
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
95-
+ " ON " + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID
106+
+ " ON " + PLAYLIST_TABLE + "." + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID
96107
+ " GROUP BY " + PLAYLIST_ID
97108
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
98109
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
99110

100111
@Transaction
101112
@Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", "
102113
+ PLAYLIST_NAME + ", "
103-
+ PLAYLIST_TABLE + "." + PLAYLIST_THUMBNAIL_URL + ", "
114+
115+
+ " CASE WHEN " + PLAYLIST_THUMBNAIL_STREAM_ID + " = "
116+
+ PlaylistEntity.DEFAULT_THUMBNAIL_ID + " THEN " + "'" + DEFAULT_THUMBNAIL + "'"
117+
+ " ELSE (SELECT " + STREAM_THUMBNAIL_URL
118+
+ " FROM " + STREAM_TABLE
119+
+ " WHERE " + STREAM_TABLE + "." + STREAM_ID + " = " + PLAYLIST_THUMBNAIL_STREAM_ID
120+
+ " ) END AS " + PLAYLIST_THUMBNAIL_URL + ", "
121+
104122
+ "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT + ", "
105123
+ "COALESCE(SUM(" + STREAM_URL + " = :streamUrl), 0) AS "
106124
+ PLAYLIST_TIMES_STREAM_IS_CONTAINED

app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@
88
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
99
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;
1010

11+
import org.schabi.newpipe.R;
12+
1113
@Entity(tableName = PLAYLIST_TABLE,
1214
indices = {@Index(value = {PLAYLIST_NAME})})
1315
public class PlaylistEntity {
16+
17+
public static final String DEFAULT_THUMBNAIL = "drawable://"
18+
+ R.drawable.placeholder_thumbnail_playlist;
19+
public static final long DEFAULT_THUMBNAIL_ID = -1;
20+
1421
public static final String PLAYLIST_TABLE = "playlists";
1522
public static final String PLAYLIST_ID = "uid";
1623
public static final String PLAYLIST_NAME = "name";
1724
public static final String PLAYLIST_THUMBNAIL_URL = "thumbnail_url";
1825
public static final String PLAYLIST_THUMBNAIL_PERMANENT = "is_thumbnail_permanent";
26+
public static final String PLAYLIST_THUMBNAIL_STREAM_ID = "thumbnail_stream_id";
1927

2028
@PrimaryKey(autoGenerate = true)
2129
@ColumnInfo(name = PLAYLIST_ID)
@@ -24,17 +32,17 @@ public class PlaylistEntity {
2432
@ColumnInfo(name = PLAYLIST_NAME)
2533
private String name;
2634

27-
@ColumnInfo(name = PLAYLIST_THUMBNAIL_URL)
28-
private String thumbnailUrl;
29-
3035
@ColumnInfo(name = PLAYLIST_THUMBNAIL_PERMANENT)
3136
private boolean isThumbnailPermanent;
3237

33-
public PlaylistEntity(final String name, final String thumbnailUrl,
34-
final boolean isThumbnailPermanent) {
38+
@ColumnInfo(name = PLAYLIST_THUMBNAIL_STREAM_ID)
39+
private long thumbnailStreamId;
40+
41+
public PlaylistEntity(final String name, final boolean isThumbnailPermanent,
42+
final long thumbnailStreamId) {
3543
this.name = name;
36-
this.thumbnailUrl = thumbnailUrl;
3744
this.isThumbnailPermanent = isThumbnailPermanent;
45+
this.thumbnailStreamId = thumbnailStreamId;
3846
}
3947

4048
public long getUid() {
@@ -53,12 +61,12 @@ public void setName(final String name) {
5361
this.name = name;
5462
}
5563

56-
public String getThumbnailUrl() {
57-
return thumbnailUrl;
64+
public long getThumbnailStreamId() {
65+
return thumbnailStreamId;
5866
}
5967

60-
public void setThumbnailUrl(final String thumbnailUrl) {
61-
this.thumbnailUrl = thumbnailUrl;
68+
public void setThumbnailStreamId(final long thumbnailStreamId) {
69+
this.thumbnailStreamId = thumbnailStreamId;
6270
}
6371

6472
public boolean getIsThumbnailPermanent() {

app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,10 @@ private void showLocalDialog(final PlaylistMetadataEntry selectedItem) {
280280
showDeleteDialog(selectedItem.name,
281281
localPlaylistManager.deletePlaylist(selectedItem.uid));
282282
} else if (isThumbnailPermanent && items.get(index).equals(unsetThumbnail)) {
283-
final String thumbnailUrl = localPlaylistManager
284-
.getAutomaticPlaylistThumbnail(selectedItem.uid);
283+
final long thumbnailStreamId = localPlaylistManager
284+
.getAutomaticPlaylistThumbnailStreamId(selectedItem.uid);
285285
localPlaylistManager
286-
.changePlaylistThumbnail(selectedItem.uid, thumbnailUrl, false)
286+
.changePlaylistThumbnail(selectedItem.uid, thumbnailStreamId, false)
287287
.observeOn(AndroidSchedulers.mainThread())
288288
.subscribe();
289289
}

app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import org.schabi.newpipe.NewPipeDatabase;
1616
import org.schabi.newpipe.R;
17+
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
1718
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
1819
import org.schabi.newpipe.database.stream.model.StreamEntity;
1920
import org.schabi.newpipe.local.LocalItemListAdapter;
@@ -154,17 +155,19 @@ private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager,
154155

155156
final Toast successToast = Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT);
156157

157-
if (playlist.thumbnailUrl
158-
.equals("drawable://" + R.drawable.placeholder_thumbnail_playlist)) {
159-
playlistDisposables.add(manager
160-
.changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl(), false)
161-
.observeOn(AndroidSchedulers.mainThread())
162-
.subscribe(ignored -> successToast.show()));
163-
}
164-
165158
playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams)
166159
.observeOn(AndroidSchedulers.mainThread())
167-
.subscribe(ignored -> successToast.show()));
160+
.subscribe(ignored -> {
161+
successToast.show();
162+
163+
if (playlist.thumbnailUrl.equals(PlaylistEntity.DEFAULT_THUMBNAIL)) {
164+
playlistDisposables.add(manager
165+
.changePlaylistThumbnail(playlist.uid, streams.get(0).getUid(),
166+
false)
167+
.observeOn(AndroidSchedulers.mainThread())
168+
.subscribe(ignore -> successToast.show()));
169+
}
170+
}));
168171

169172
requireDialog().dismiss();
170173
}

app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.schabi.newpipe.database.LocalItem;
3636
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
3737
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
38+
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
3839
import org.schabi.newpipe.database.stream.model.StreamEntity;
3940
import org.schabi.newpipe.databinding.DialogEditTextBinding;
4041
import org.schabi.newpipe.databinding.LocalPlaylistHeaderBinding;
@@ -417,8 +418,8 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
417418
if (indexInHistory < 0) {
418419
itemsToKeep.add(playlistItem);
419420
} else if (!isThumbnailPermanent && !thumbnailVideoRemoved
420-
&& playlistManager.getPlaylistThumbnail(playlistId)
421-
.equals(playlistItem.getStreamEntity().getThumbnailUrl())) {
421+
&& playlistManager.getPlaylistThumbnailStreamId(playlistId)
422+
== playlistItem.getStreamEntity().getUid()) {
422423
thumbnailVideoRemoved = true;
423424
}
424425
}
@@ -438,8 +439,8 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
438439
&& !streamStateEntity.isFinished(duration))) {
439440
itemsToKeep.add(playlistItem);
440441
} else if (!isThumbnailPermanent && !thumbnailVideoRemoved
441-
&& playlistManager.getPlaylistThumbnail(playlistId)
442-
.equals(playlistItem.getStreamEntity().getThumbnailUrl())) {
442+
&& playlistManager.getPlaylistThumbnailStreamId(playlistId)
443+
== playlistItem.getStreamEntity().getUid()) {
443444
thumbnailVideoRemoved = true;
444445
}
445446
}
@@ -587,7 +588,7 @@ private void changePlaylistName(final String title) {
587588
disposables.add(disposable);
588589
}
589590

590-
private void changeThumbnailUrl(final String thumbnailUrl, final boolean isPermanent) {
591+
private void changeThumbnailStreamId(final long thumbnailStreamId, final boolean isPermanent) {
591592
if (playlistManager == null || (!isPermanent && playlistManager
592593
.getIsPlaylistThumbnailPermanent(playlistId))) {
593594
return;
@@ -599,11 +600,11 @@ private void changeThumbnailUrl(final String thumbnailUrl, final boolean isPerma
599600

600601
if (DEBUG) {
601602
Log.d(TAG, "Updating playlist id=[" + playlistId + "] "
602-
+ "with new thumbnail url=[" + thumbnailUrl + "]");
603+
+ "with new thumbnail stream id=[" + thumbnailStreamId + "]");
603604
}
604605

605606
final Disposable disposable = playlistManager
606-
.changePlaylistThumbnail(playlistId, thumbnailUrl, isPermanent)
607+
.changePlaylistThumbnail(playlistId, thumbnailStreamId, isPermanent)
607608
.observeOn(AndroidSchedulers.mainThread())
608609
.subscribe(ignore -> successToast.show(), throwable ->
609610
showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
@@ -616,16 +617,16 @@ private void updateThumbnailUrl() {
616617
return;
617618
}
618619

619-
final String newThumbnailUrl;
620+
final long thumbnailStreamId;
620621

621622
if (!itemListAdapter.getItemsList().isEmpty()) {
622-
newThumbnailUrl = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0))
623-
.getStreamEntity().getThumbnailUrl();
623+
thumbnailStreamId = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0))
624+
.getStreamEntity().getUid();
624625
} else {
625-
newThumbnailUrl = "drawable://" + R.drawable.placeholder_thumbnail_playlist;
626+
thumbnailStreamId = PlaylistEntity.DEFAULT_THUMBNAIL_ID;
626627
}
627628

628-
changeThumbnailUrl(newThumbnailUrl, false);
629+
changeThumbnailStreamId(thumbnailStreamId, false);
629630
}
630631

631632
private void deleteItem(final PlaylistStreamEntry item) {
@@ -634,8 +635,7 @@ private void deleteItem(final PlaylistStreamEntry item) {
634635
}
635636

636637
itemListAdapter.removeItem(item);
637-
if (playlistManager.getPlaylistThumbnail(playlistId)
638-
.equals(item.getStreamEntity().getThumbnailUrl())) {
638+
if (playlistManager.getPlaylistThumbnailStreamId(playlistId) == item.getStreamId()) {
639639
updateThumbnailUrl();
640640
}
641641

@@ -793,7 +793,7 @@ context, getPlayQueueStartingAt(item), true))
793793
.setAction(
794794
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
795795
(f, i) ->
796-
changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl(),
796+
changeThumbnailStreamId(item.getStreamEntity().getUid(),
797797
true))
798798
.setAction(
799799
StreamDialogDefaultEntry.DELETE,

0 commit comments

Comments
 (0)