mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	Merge pull request #9523 from Jared234/9468_permanently_set_thumbnail
Allow the user to permanently set a thumbnail
This commit is contained in:
		
							
								
								
									
										737
									
								
								app/schemas/org.schabi.newpipe.database.AppDatabase/6.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										737
									
								
								app/schemas/org.schabi.newpipe.database.AppDatabase/6.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,737 @@ | ||||
| { | ||||
|   "formatVersion": 1, | ||||
|   "database": { | ||||
|     "version": 6, | ||||
|     "identityHash": "4084aa342aef315dc7b558770a7755a9", | ||||
|     "entities": [ | ||||
|       { | ||||
|         "tableName": "subscriptions", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT, `name` TEXT, `avatar_url` TEXT, `subscriber_count` INTEGER, `description` TEXT, `notification_mode` INTEGER NOT NULL)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "serviceId", | ||||
|             "columnName": "service_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "url", | ||||
|             "columnName": "url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "name", | ||||
|             "columnName": "name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "avatarUrl", | ||||
|             "columnName": "avatar_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "subscriberCount", | ||||
|             "columnName": "subscriber_count", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "description", | ||||
|             "columnName": "description", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "notificationMode", | ||||
|             "columnName": "notification_mode", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_subscriptions_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_subscriptions_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "search_history", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`creation_date` INTEGER, `service_id` INTEGER NOT NULL, `search` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "creationDate", | ||||
|             "columnName": "creation_date", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "serviceId", | ||||
|             "columnName": "service_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "search", | ||||
|             "columnName": "search", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "id", | ||||
|             "columnName": "id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "id" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_search_history_search", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "search" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_search_history_search` ON `${TABLE_NAME}` (`search`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "streams", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `stream_type` TEXT NOT NULL, `duration` INTEGER NOT NULL, `uploader` TEXT NOT NULL, `uploader_url` TEXT, `thumbnail_url` TEXT, `view_count` INTEGER, `textual_upload_date` TEXT, `upload_date` INTEGER, `is_upload_date_approximation` INTEGER)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "serviceId", | ||||
|             "columnName": "service_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "url", | ||||
|             "columnName": "url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "title", | ||||
|             "columnName": "title", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "streamType", | ||||
|             "columnName": "stream_type", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "duration", | ||||
|             "columnName": "duration", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "uploader", | ||||
|             "columnName": "uploader", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "uploaderUrl", | ||||
|             "columnName": "uploader_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "thumbnailUrl", | ||||
|             "columnName": "thumbnail_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "viewCount", | ||||
|             "columnName": "view_count", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "textualUploadDate", | ||||
|             "columnName": "textual_upload_date", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "uploadDate", | ||||
|             "columnName": "upload_date", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "isUploadDateApproximation", | ||||
|             "columnName": "is_upload_date_approximation", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_streams_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_streams_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "stream_history", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, `repeat_count` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "streamUid", | ||||
|             "columnName": "stream_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "accessDate", | ||||
|             "columnName": "access_date", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "repeatCount", | ||||
|             "columnName": "repeat_count", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "stream_id", | ||||
|             "access_date" | ||||
|           ], | ||||
|           "autoGenerate": false | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_stream_history_stream_id", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "stream_id" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_stream_history_stream_id` ON `${TABLE_NAME}` (`stream_id`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [ | ||||
|           { | ||||
|             "table": "streams", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "stream_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "stream_state", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "streamUid", | ||||
|             "columnName": "stream_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "progressMillis", | ||||
|             "columnName": "progress_time", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "stream_id" | ||||
|           ], | ||||
|           "autoGenerate": false | ||||
|         }, | ||||
|         "indices": [], | ||||
|         "foreignKeys": [ | ||||
|           { | ||||
|             "table": "streams", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "stream_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "playlists", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `thumbnail_url` TEXT, `is_thumbnail_permanent` INTEGER NOT NULL)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "name", | ||||
|             "columnName": "name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "thumbnailUrl", | ||||
|             "columnName": "thumbnail_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "isThumbnailPermanent", | ||||
|             "columnName": "is_thumbnail_permanent", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_playlists_name", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "name" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_playlists_name` ON `${TABLE_NAME}` (`name`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "playlist_stream_join", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER NOT NULL, `stream_id` INTEGER NOT NULL, `join_index` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `join_index`), FOREIGN KEY(`playlist_id`) REFERENCES `playlists`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "playlistUid", | ||||
|             "columnName": "playlist_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "streamUid", | ||||
|             "columnName": "stream_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "index", | ||||
|             "columnName": "join_index", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "playlist_id", | ||||
|             "join_index" | ||||
|           ], | ||||
|           "autoGenerate": false | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_playlist_stream_join_playlist_id_join_index", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "playlist_id", | ||||
|               "join_index" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_playlist_stream_join_playlist_id_join_index` ON `${TABLE_NAME}` (`playlist_id`, `join_index`)" | ||||
|           }, | ||||
|           { | ||||
|             "name": "index_playlist_stream_join_stream_id", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "stream_id" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_playlist_stream_join_stream_id` ON `${TABLE_NAME}` (`stream_id`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [ | ||||
|           { | ||||
|             "table": "playlists", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "playlist_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           }, | ||||
|           { | ||||
|             "table": "streams", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "stream_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "remote_playlists", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, `thumbnail_url` TEXT, `uploader` TEXT, `stream_count` INTEGER)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "serviceId", | ||||
|             "columnName": "service_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "name", | ||||
|             "columnName": "name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "url", | ||||
|             "columnName": "url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "thumbnailUrl", | ||||
|             "columnName": "thumbnail_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "uploader", | ||||
|             "columnName": "uploader", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "streamCount", | ||||
|             "columnName": "stream_count", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_remote_playlists_name", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "name" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_remote_playlists_name` ON `${TABLE_NAME}` (`name`)" | ||||
|           }, | ||||
|           { | ||||
|             "name": "index_remote_playlists_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_remote_playlists_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "feed", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `subscription_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "streamId", | ||||
|             "columnName": "stream_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "subscriptionId", | ||||
|             "columnName": "subscription_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "stream_id", | ||||
|             "subscription_id" | ||||
|           ], | ||||
|           "autoGenerate": false | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_feed_subscription_id", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "subscription_id" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [ | ||||
|           { | ||||
|             "table": "streams", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "stream_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           }, | ||||
|           { | ||||
|             "table": "subscriptions", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "subscription_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "feed_group", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon_id` INTEGER NOT NULL, `sort_order` INTEGER NOT NULL)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "name", | ||||
|             "columnName": "name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "icon", | ||||
|             "columnName": "icon_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "sortOrder", | ||||
|             "columnName": "sort_order", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_feed_group_sort_order", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "sort_order" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_sort_order` ON `${TABLE_NAME}` (`sort_order`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "feed_group_subscription_join", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`group_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`group_id`, `subscription_id`), FOREIGN KEY(`group_id`) REFERENCES `feed_group`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "feedGroupId", | ||||
|             "columnName": "group_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "subscriptionId", | ||||
|             "columnName": "subscription_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "group_id", | ||||
|             "subscription_id" | ||||
|           ], | ||||
|           "autoGenerate": false | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_feed_group_subscription_join_subscription_id", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "subscription_id" | ||||
|             ], | ||||
|             "orders": [], | ||||
|             "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_subscription_join_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [ | ||||
|           { | ||||
|             "table": "feed_group", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "group_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           }, | ||||
|           { | ||||
|             "table": "subscriptions", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "subscription_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "feed_last_updated", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subscription_id` INTEGER NOT NULL, `last_updated` INTEGER, PRIMARY KEY(`subscription_id`), FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "subscriptionId", | ||||
|             "columnName": "subscription_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "lastUpdated", | ||||
|             "columnName": "last_updated", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "subscription_id" | ||||
|           ], | ||||
|           "autoGenerate": false | ||||
|         }, | ||||
|         "indices": [], | ||||
|         "foreignKeys": [ | ||||
|           { | ||||
|             "table": "subscriptions", | ||||
|             "onDelete": "CASCADE", | ||||
|             "onUpdate": "CASCADE", | ||||
|             "columns": [ | ||||
|               "subscription_id" | ||||
|             ], | ||||
|             "referencedColumns": [ | ||||
|               "uid" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ], | ||||
|     "views": [], | ||||
|     "setupQueries": [ | ||||
|       "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", | ||||
|       "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4084aa342aef315dc7b558770a7755a9')" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -33,7 +33,8 @@ class DatabaseMigrationTest { | ||||
|     @get:Rule | ||||
|     val testHelper = MigrationTestHelper( | ||||
|         InstrumentationRegistry.getInstrumentation(), | ||||
|         AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory() | ||||
|         AppDatabase::class.java.canonicalName, | ||||
|         FrameworkSQLiteOpenHelperFactory() | ||||
|     ) | ||||
|  | ||||
|     @Test | ||||
| @@ -42,7 +43,8 @@ class DatabaseMigrationTest { | ||||
|  | ||||
|         databaseInV2.run { | ||||
|             insert( | ||||
|                 "streams", SQLiteDatabase.CONFLICT_FAIL, | ||||
|                 "streams", | ||||
|                 SQLiteDatabase.CONFLICT_FAIL, | ||||
|                 ContentValues().apply { | ||||
|                     put("service_id", DEFAULT_SERVICE_ID) | ||||
|                     put("url", DEFAULT_URL) | ||||
| @@ -54,14 +56,16 @@ class DatabaseMigrationTest { | ||||
|                 } | ||||
|             ) | ||||
|             insert( | ||||
|                 "streams", SQLiteDatabase.CONFLICT_FAIL, | ||||
|                 "streams", | ||||
|                 SQLiteDatabase.CONFLICT_FAIL, | ||||
|                 ContentValues().apply { | ||||
|                     put("service_id", DEFAULT_SECOND_SERVICE_ID) | ||||
|                     put("url", DEFAULT_SECOND_URL) | ||||
|                 } | ||||
|             ) | ||||
|             insert( | ||||
|                 "streams", SQLiteDatabase.CONFLICT_FAIL, | ||||
|                 "streams", | ||||
|                 SQLiteDatabase.CONFLICT_FAIL, | ||||
|                 ContentValues().apply { | ||||
|                     put("service_id", DEFAULT_SERVICE_ID) | ||||
|                 } | ||||
| @@ -70,18 +74,31 @@ class DatabaseMigrationTest { | ||||
|         } | ||||
|  | ||||
|         testHelper.runMigrationsAndValidate( | ||||
|             AppDatabase.DATABASE_NAME, Migrations.DB_VER_3, | ||||
|             true, Migrations.MIGRATION_2_3 | ||||
|             AppDatabase.DATABASE_NAME, | ||||
|             Migrations.DB_VER_3, | ||||
|             true, | ||||
|             Migrations.MIGRATION_2_3 | ||||
|         ) | ||||
|  | ||||
|         testHelper.runMigrationsAndValidate( | ||||
|             AppDatabase.DATABASE_NAME, Migrations.DB_VER_4, | ||||
|             true, Migrations.MIGRATION_3_4 | ||||
|             AppDatabase.DATABASE_NAME, | ||||
|             Migrations.DB_VER_4, | ||||
|             true, | ||||
|             Migrations.MIGRATION_3_4 | ||||
|         ) | ||||
|  | ||||
|         testHelper.runMigrationsAndValidate( | ||||
|             AppDatabase.DATABASE_NAME, Migrations.DB_VER_5, | ||||
|             true, Migrations.MIGRATION_4_5 | ||||
|             AppDatabase.DATABASE_NAME, | ||||
|             Migrations.DB_VER_5, | ||||
|             true, | ||||
|             Migrations.MIGRATION_4_5 | ||||
|         ) | ||||
|  | ||||
|         testHelper.runMigrationsAndValidate( | ||||
|             AppDatabase.DATABASE_NAME, | ||||
|             Migrations.DB_VER_6, | ||||
|             true, | ||||
|             Migrations.MIGRATION_5_6 | ||||
|         ) | ||||
|  | ||||
|         val migratedDatabaseV3 = getMigratedDatabase() | ||||
| @@ -121,7 +138,8 @@ class DatabaseMigrationTest { | ||||
|     private fun getMigratedDatabase(): AppDatabase { | ||||
|         val database: AppDatabase = Room.databaseBuilder( | ||||
|             ApplicationProvider.getApplicationContext(), | ||||
|             AppDatabase::class.java, AppDatabase.DATABASE_NAME | ||||
|             AppDatabase::class.java, | ||||
|             AppDatabase.DATABASE_NAME | ||||
|         ) | ||||
|             .build() | ||||
|         testHelper.closeWhenFinished(database) | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import static org.schabi.newpipe.database.Migrations.MIGRATION_1_2; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_3_4; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_4_5; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_5_6; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| @@ -24,7 +25,8 @@ public final class NewPipeDatabase { | ||||
|     private static AppDatabase getDatabase(final Context context) { | ||||
|         return Room | ||||
|                 .databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) | ||||
|                 .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5) | ||||
|                 .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, | ||||
|                         MIGRATION_5_6) | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package org.schabi.newpipe.database; | ||||
|  | ||||
| import static org.schabi.newpipe.database.Migrations.DB_VER_5; | ||||
| import static org.schabi.newpipe.database.Migrations.DB_VER_6; | ||||
|  | ||||
| import androidx.room.Database; | ||||
| import androidx.room.RoomDatabase; | ||||
| @@ -38,7 +38,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity; | ||||
|                 FeedEntity.class, FeedGroupEntity.class, FeedGroupSubscriptionEntity.class, | ||||
|                 FeedLastUpdatedEntity.class | ||||
|         }, | ||||
|         version = DB_VER_5 | ||||
|         version = DB_VER_6 | ||||
| ) | ||||
| public abstract class AppDatabase extends RoomDatabase { | ||||
|     public static final String DATABASE_NAME = "newpipe.db"; | ||||
|   | ||||
| @@ -23,6 +23,7 @@ public final class Migrations { | ||||
|     public static final int DB_VER_3 = 3; | ||||
|     public static final int DB_VER_4 = 4; | ||||
|     public static final int DB_VER_5 = 5; | ||||
|     public static final int DB_VER_6 = 6; | ||||
|  | ||||
|     private static final String TAG = Migrations.class.getName(); | ||||
|     public static final boolean DEBUG = MainActivity.DEBUG; | ||||
| @@ -188,6 +189,14 @@ public final class Migrations { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) { | ||||
|         @Override | ||||
|         public void migrate(@NonNull final SupportSQLiteDatabase database) { | ||||
|             database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` " | ||||
|                     + "INTEGER NOT NULL DEFAULT 0"); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     private Migrations() { | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,7 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JO | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_THUMBNAIL_URL; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; | ||||
| @@ -53,6 +54,15 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> { | ||||
|             + " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId") | ||||
|     Flowable<Integer> getMaximumIndexOf(long playlistId); | ||||
|  | ||||
|     @Query("SELECT CASE WHEN COUNT(*) != 0 then " + STREAM_THUMBNAIL_URL + " ELSE :defaultUrl END" | ||||
|             + " FROM " + STREAM_TABLE | ||||
|             + " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE | ||||
|             + " ON " + STREAM_ID + " = " + JOIN_STREAM_ID | ||||
|             + " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId " | ||||
|             + " LIMIT 1" | ||||
|     ) | ||||
|     Flowable<String> getAutomaticThumbnailUrl(long playlistId, String defaultUrl); | ||||
|  | ||||
|     @RewriteQueriesToDropUnusedColumns | ||||
|     @Transaction | ||||
|     @Query("SELECT * FROM " + STREAM_TABLE + " INNER JOIN " | ||||
|   | ||||
| @@ -15,6 +15,7 @@ public class PlaylistEntity { | ||||
|     public static final String PLAYLIST_ID = "uid"; | ||||
|     public static final String PLAYLIST_NAME = "name"; | ||||
|     public static final String PLAYLIST_THUMBNAIL_URL = "thumbnail_url"; | ||||
|     public static final String PLAYLIST_THUMBNAIL_PERMANENT = "is_thumbnail_permanent"; | ||||
|  | ||||
|     @PrimaryKey(autoGenerate = true) | ||||
|     @ColumnInfo(name = PLAYLIST_ID) | ||||
| @@ -26,9 +27,14 @@ public class PlaylistEntity { | ||||
|     @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) | ||||
|     private String thumbnailUrl; | ||||
|  | ||||
|     public PlaylistEntity(final String name, final String thumbnailUrl) { | ||||
|     @ColumnInfo(name = PLAYLIST_THUMBNAIL_PERMANENT) | ||||
|     private boolean isThumbnailPermanent; | ||||
|  | ||||
|     public PlaylistEntity(final String name, final String thumbnailUrl, | ||||
|                           final boolean isThumbnailPermanent) { | ||||
|         this.name = name; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|         this.isThumbnailPermanent = isThumbnailPermanent; | ||||
|     } | ||||
|  | ||||
|     public long getUid() { | ||||
| @@ -54,4 +60,13 @@ public class PlaylistEntity { | ||||
|     public void setThumbnailUrl(final String thumbnailUrl) { | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|     } | ||||
|  | ||||
|     public boolean getIsThumbnailPermanent() { | ||||
|         return isThumbnailPermanent; | ||||
|     } | ||||
|  | ||||
|     public void setIsThumbnailPermanent(final boolean isThumbnailSet) { | ||||
|         this.isThumbnailPermanent = isThumbnailSet; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package org.schabi.newpipe.local.bookmark; | ||||
|  | ||||
| import android.content.DialogInterface; | ||||
| import android.os.Bundle; | ||||
| import android.os.Parcelable; | ||||
| import android.text.InputType; | ||||
| @@ -31,6 +32,7 @@ import org.schabi.newpipe.local.playlist.RemotePlaylistManager; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.OnClickGesture; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import icepick.State; | ||||
| @@ -256,6 +258,41 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|     } | ||||
|  | ||||
|     private void showLocalDialog(final PlaylistMetadataEntry selectedItem) { | ||||
|         final String rename = getString(R.string.rename); | ||||
|         final String delete = getString(R.string.delete); | ||||
|         final String unsetThumbnail = getString(R.string.unset_playlist_thumbnail); | ||||
|         final boolean isThumbnailPermanent = localPlaylistManager | ||||
|                 .getIsPlaylistThumbnailPermanent(selectedItem.uid); | ||||
|  | ||||
|         final AlertDialog.Builder builder = new AlertDialog.Builder(activity); | ||||
|  | ||||
|         final ArrayList<String> items = new ArrayList<>(); | ||||
|         items.add(rename); | ||||
|         items.add(delete); | ||||
|         if (isThumbnailPermanent) { | ||||
|             items.add(unsetThumbnail); | ||||
|         } | ||||
|  | ||||
|         final DialogInterface.OnClickListener action = (d, index) -> { | ||||
|             if (items.get(index).equals(rename)) { | ||||
|                 showRenameDialog(selectedItem); | ||||
|             } else if (items.get(index).equals(delete)) { | ||||
|                 showDeleteDialog(selectedItem.name, | ||||
|                         localPlaylistManager.deletePlaylist(selectedItem.uid)); | ||||
|             } else if (isThumbnailPermanent && items.get(index).equals(unsetThumbnail)) { | ||||
|                 final String thumbnailUrl = localPlaylistManager | ||||
|                         .getAutomaticPlaylistThumbnail(selectedItem.uid); | ||||
|                 localPlaylistManager | ||||
|                         .changePlaylistThumbnail(selectedItem.uid, thumbnailUrl, false) | ||||
|                         .observeOn(AndroidSchedulers.mainThread()) | ||||
|                         .subscribe(); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         builder.setItems(items.toArray(new String[0]), action).create().show(); | ||||
|     } | ||||
|  | ||||
|     private void showRenameDialog(final PlaylistMetadataEntry selectedItem) { | ||||
|         final DialogEditTextBinding dialogBinding = | ||||
|                 DialogEditTextBinding.inflate(getLayoutInflater()); | ||||
|         dialogBinding.dialogEditText.setHint(R.string.name); | ||||
| @@ -269,11 +306,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|                                 selectedItem.uid, | ||||
|                                 dialogBinding.dialogEditText.getText().toString())) | ||||
|                 .setNegativeButton(R.string.cancel, null) | ||||
|                 .setNeutralButton(R.string.delete, (dialog, which) -> { | ||||
|                     showDeleteDialog(selectedItem.name, | ||||
|                             localPlaylistManager.deletePlaylist(selectedItem.uid)); | ||||
|                     dialog.dismiss(); | ||||
|                 }) | ||||
|                 .create() | ||||
|                 .show(); | ||||
|     } | ||||
|   | ||||
| @@ -134,7 +134,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog { | ||||
|         if (playlist.thumbnailUrl | ||||
|                 .equals("drawable://" + R.drawable.placeholder_thumbnail_playlist)) { | ||||
|             playlistDisposables.add(manager | ||||
|                     .changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl()) | ||||
|                     .changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl(), false) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribe(ignored -> successToast.show())); | ||||
|         } | ||||
|   | ||||
| @@ -405,6 +405,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|                 .zipWith(historyIdsMaybe, (playlist, historyStreamIds) -> { | ||||
|                     // Remove Watched, Functionality data | ||||
|                     final List<PlaylistStreamEntry> notWatchedItems = new ArrayList<>(); | ||||
|                     final boolean isThumbnailPermanent = playlistManager | ||||
|                             .getIsPlaylistThumbnailPermanent(playlistId); | ||||
|                     boolean thumbnailVideoRemoved = false; | ||||
|  | ||||
|                     if (removePartiallyWatched) { | ||||
| @@ -414,7 +416,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|  | ||||
|                             if (indexInHistory < 0) { | ||||
|                                 notWatchedItems.add(playlistItem); | ||||
|                             } else if (!thumbnailVideoRemoved | ||||
|                             } else if (!isThumbnailPermanent && !thumbnailVideoRemoved | ||||
|                                     && playlistManager.getPlaylistThumbnail(playlistId) | ||||
|                                     .equals(playlistItem.getStreamEntity().getThumbnailUrl())) { | ||||
|                                 thumbnailVideoRemoved = true; | ||||
| @@ -435,7 +437,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|                             if (indexInHistory < 0 || (streamStateEntity != null | ||||
|                                     && !streamStateEntity.isFinished(duration))) { | ||||
|                                 notWatchedItems.add(playlistItem); | ||||
|                             } else if (!thumbnailVideoRemoved | ||||
|                             } else if (!isThumbnailPermanent && !thumbnailVideoRemoved | ||||
|                                     && playlistManager.getPlaylistThumbnail(playlistId) | ||||
|                                     .equals(playlistItem.getStreamEntity().getThumbnailUrl())) { | ||||
|                                 thumbnailVideoRemoved = true; | ||||
| @@ -585,8 +587,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         disposables.add(disposable); | ||||
|     } | ||||
|  | ||||
|     private void changeThumbnailUrl(final String thumbnailUrl) { | ||||
|         if (playlistManager == null) { | ||||
|     private void changeThumbnailUrl(final String thumbnailUrl, final boolean isPermanent) { | ||||
|         if (playlistManager == null || (!isPermanent && playlistManager | ||||
|                 .getIsPlaylistThumbnailPermanent(playlistId))) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -600,7 +603,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         } | ||||
|  | ||||
|         final Disposable disposable = playlistManager | ||||
|                 .changePlaylistThumbnail(playlistId, thumbnailUrl) | ||||
|                 .changePlaylistThumbnail(playlistId, thumbnailUrl, isPermanent) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(ignore -> successToast.show(), throwable -> | ||||
|                         showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK, | ||||
| @@ -609,6 +612,10 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|     } | ||||
|  | ||||
|     private void updateThumbnailUrl() { | ||||
|         if (playlistManager.getIsPlaylistThumbnailPermanent(playlistId)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         final String newThumbnailUrl; | ||||
|  | ||||
|         if (!itemListAdapter.getItemsList().isEmpty()) { | ||||
| @@ -618,7 +625,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|             newThumbnailUrl = "drawable://" + R.drawable.placeholder_thumbnail_playlist; | ||||
|         } | ||||
|  | ||||
|         changeThumbnailUrl(newThumbnailUrl); | ||||
|         changeThumbnailUrl(newThumbnailUrl, false); | ||||
|     } | ||||
|  | ||||
|     private void deleteItem(final PlaylistStreamEntry item) { | ||||
| @@ -786,7 +793,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|                     .setAction( | ||||
|                             StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL, | ||||
|                             (f, i) -> | ||||
|                                     changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl())) | ||||
|                                     changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl(), | ||||
|                                             true)) | ||||
|                     .setAction( | ||||
|                             StreamDialogDefaultEntry.DELETE, | ||||
|                             (f, i) -> deleteItem(item)) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package org.schabi.newpipe.local.playlist; | ||||
|  | ||||
| import androidx.annotation.Nullable; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.database.AppDatabase; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; | ||||
| @@ -41,7 +42,7 @@ public class LocalPlaylistManager { | ||||
|         } | ||||
|         final StreamEntity defaultStream = streams.get(0); | ||||
|         final PlaylistEntity newPlaylist = | ||||
|                 new PlaylistEntity(name, defaultStream.getThumbnailUrl()); | ||||
|                 new PlaylistEntity(name, defaultStream.getThumbnailUrl(), false); | ||||
|  | ||||
|         return Maybe.fromCallable(() -> database.runInTransaction(() -> | ||||
|                 upsertStreams(playlistTable.insert(newPlaylist), streams, 0)) | ||||
| @@ -96,21 +97,33 @@ public class LocalPlaylistManager { | ||||
|     } | ||||
|  | ||||
|     public Maybe<Integer> renamePlaylist(final long playlistId, final String name) { | ||||
|         return modifyPlaylist(playlistId, name, null); | ||||
|         return modifyPlaylist(playlistId, name, null, false); | ||||
|     } | ||||
|  | ||||
|     public Maybe<Integer> changePlaylistThumbnail(final long playlistId, | ||||
|                                                   final String thumbnailUrl) { | ||||
|         return modifyPlaylist(playlistId, null, thumbnailUrl); | ||||
|                                                   final String thumbnailUrl, | ||||
|                                                   final boolean isPermanent) { | ||||
|         return modifyPlaylist(playlistId, null, thumbnailUrl, isPermanent); | ||||
|     } | ||||
|  | ||||
|     public String getPlaylistThumbnail(final long playlistId) { | ||||
|         return playlistTable.getPlaylist(playlistId).blockingFirst().get(0).getThumbnailUrl(); | ||||
|     } | ||||
|  | ||||
|     public boolean getIsPlaylistThumbnailPermanent(final long playlistId) { | ||||
|         return playlistTable.getPlaylist(playlistId).blockingFirst().get(0) | ||||
|                 .getIsThumbnailPermanent(); | ||||
|     } | ||||
|  | ||||
|     public String getAutomaticPlaylistThumbnail(final long playlistId) { | ||||
|         final String def = "drawable://" + R.drawable.placeholder_thumbnail_playlist; | ||||
|         return playlistStreamTable.getAutomaticThumbnailUrl(playlistId, def).blockingFirst(); | ||||
|     } | ||||
|  | ||||
|     private Maybe<Integer> modifyPlaylist(final long playlistId, | ||||
|                                           @Nullable final String name, | ||||
|                                           @Nullable final String thumbnailUrl) { | ||||
|                                           @Nullable final String thumbnailUrl, | ||||
|                                           final boolean isPermanent) { | ||||
|         return playlistTable.getPlaylist(playlistId) | ||||
|                 .firstElement() | ||||
|                 .filter(playlistEntities -> !playlistEntities.isEmpty()) | ||||
| @@ -121,6 +134,7 @@ public class LocalPlaylistManager { | ||||
|                     } | ||||
|                     if (thumbnailUrl != null) { | ||||
|                         playlist.setThumbnailUrl(thumbnailUrl); | ||||
|                         playlist.setIsThumbnailPermanent(isPermanent); | ||||
|                     } | ||||
|                     return playlistTable.update(playlist); | ||||
|                 }).subscribeOn(Schedulers.io()); | ||||
|   | ||||
| @@ -439,6 +439,7 @@ | ||||
|     <string name="mute">Mute</string> | ||||
|     <string name="unmute">Unmute</string> | ||||
|     <string name="set_as_playlist_thumbnail">Set as playlist thumbnail</string> | ||||
|     <string name="unset_playlist_thumbnail">Unset permanent thumbnail</string> | ||||
|     <string name="bookmark_playlist">Bookmark Playlist</string> | ||||
|     <string name="unbookmark_playlist">Remove Bookmark</string> | ||||
|     <string name="delete_playlist_prompt">Delete this playlist\?</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Stypox
					Stypox