mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Setup initial database for feed implementation
- Update the database diagram - Add new migration for the new tables and fields - Enable schema exports
This commit is contained in:
		| @@ -35,6 +35,12 @@ android { | ||||
|  | ||||
|         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||||
|         vectorDrawables.useSupportLibrary = true | ||||
|  | ||||
|         javaCompileOptions { | ||||
|             annotationProcessorOptions { | ||||
|                 arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|   | ||||
							
								
								
									
										479
									
								
								app/schemas/org.schabi.newpipe.database.AppDatabase/2.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										479
									
								
								app/schemas/org.schabi.newpipe.database.AppDatabase/2.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,479 @@ | ||||
| { | ||||
|   "formatVersion": 1, | ||||
|   "database": { | ||||
|     "version": 2, | ||||
|     "identityHash": "b7856223e2595ddf20a3ce6243ce9527", | ||||
|     "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)", | ||||
|         "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_subscriptions_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `index_subscriptions_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "search_history", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `creation_date` INTEGER, `service_id` INTEGER NOT NULL, `search` TEXT)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "id", | ||||
|             "columnName": "id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "id" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_search_history_search", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "search" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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, `title` TEXT, `stream_type` TEXT, `duration` INTEGER, `uploader` TEXT, `thumbnail_url` TEXT)", | ||||
|         "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": "title", | ||||
|             "columnName": "title", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "streamType", | ||||
|             "columnName": "stream_type", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "duration", | ||||
|             "columnName": "duration", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "uploader", | ||||
|             "columnName": "uploader", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "thumbnailUrl", | ||||
|             "columnName": "thumbnail_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_streams_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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": "progressTime", | ||||
|             "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)", | ||||
|         "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_playlists_name", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "name" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `index_remote_playlists_name` ON `${TABLE_NAME}` (`name`)" | ||||
|           }, | ||||
|           { | ||||
|             "name": "index_remote_playlists_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       } | ||||
|     ], | ||||
|     "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, \"b7856223e2595ddf20a3ce6243ce9527\")" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										647
									
								
								app/schemas/org.schabi.newpipe.database.AppDatabase/3.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										647
									
								
								app/schemas/org.schabi.newpipe.database.AppDatabase/3.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,647 @@ | ||||
| { | ||||
|   "formatVersion": 1, | ||||
|   "database": { | ||||
|     "version": 3, | ||||
|     "identityHash": "ecffbb2ea251aeb38a8f508acf2aa404", | ||||
|     "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)", | ||||
|         "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_subscriptions_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `index_subscriptions_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" | ||||
|           } | ||||
|         ], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "search_history", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `creation_date` INTEGER, `service_id` INTEGER NOT NULL, `search` TEXT)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "id", | ||||
|             "columnName": "id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "id" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_search_history_search", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "search" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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, `thumbnail_url` TEXT, `view_count` INTEGER, `textual_upload_date` TEXT, `upload_date` 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": "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_streams_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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": "progressTime", | ||||
|             "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)", | ||||
|         "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 | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [ | ||||
|           { | ||||
|             "name": "index_playlists_name", | ||||
|             "unique": false, | ||||
|             "columnNames": [ | ||||
|               "name" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `index_remote_playlists_name` ON `${TABLE_NAME}` (`name`)" | ||||
|           }, | ||||
|           { | ||||
|             "name": "index_remote_playlists_service_id_url", | ||||
|             "unique": true, | ||||
|             "columnNames": [ | ||||
|               "service_id", | ||||
|               "url" | ||||
|             ], | ||||
|             "createSql": "CREATE UNIQUE INDEX `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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "name", | ||||
|             "columnName": "name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "iconId", | ||||
|             "columnName": "icon_id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "uid" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [], | ||||
|         "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" | ||||
|             ], | ||||
|             "createSql": "CREATE  INDEX `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" | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ], | ||||
|     "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, \"ecffbb2ea251aeb38a8f508acf2aa404\")" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -9,7 +9,8 @@ import androidx.room.Room; | ||||
| import org.schabi.newpipe.database.AppDatabase; | ||||
|  | ||||
| import static org.schabi.newpipe.database.AppDatabase.DATABASE_NAME; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_11_12; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_1_2; | ||||
| import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3; | ||||
|  | ||||
| public final class NewPipeDatabase { | ||||
|  | ||||
| @@ -22,7 +23,7 @@ public final class NewPipeDatabase { | ||||
|     private static AppDatabase getDatabase(Context context) { | ||||
|         return Room | ||||
|                 .databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) | ||||
|                 .addMigrations(MIGRATION_11_12) | ||||
|                 .addMigrations(MIGRATION_1_2, MIGRATION_2_3) | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,11 @@ import androidx.room.Database; | ||||
| import androidx.room.RoomDatabase; | ||||
| import androidx.room.TypeConverters; | ||||
|  | ||||
| import org.schabi.newpipe.database.feed.dao.FeedDAO; | ||||
| import org.schabi.newpipe.database.feed.dao.FeedGroupDAO; | ||||
| import org.schabi.newpipe.database.feed.model.FeedEntity; | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupEntity; | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity; | ||||
| import org.schabi.newpipe.database.history.dao.SearchHistoryDAO; | ||||
| import org.schabi.newpipe.database.history.dao.StreamHistoryDAO; | ||||
| import org.schabi.newpipe.database.history.model.SearchHistoryEntry; | ||||
| @@ -21,35 +26,32 @@ import org.schabi.newpipe.database.stream.model.StreamStateEntity; | ||||
| import org.schabi.newpipe.database.subscription.SubscriptionDAO; | ||||
| import org.schabi.newpipe.database.subscription.SubscriptionEntity; | ||||
|  | ||||
| import static org.schabi.newpipe.database.Migrations.DB_VER_12_0; | ||||
| import static org.schabi.newpipe.database.Migrations.DB_VER_3; | ||||
|  | ||||
| @TypeConverters({Converters.class}) | ||||
| @Database( | ||||
|         entities = { | ||||
|                 SubscriptionEntity.class, SearchHistoryEntry.class, | ||||
|                 StreamEntity.class, StreamHistoryEntity.class, StreamStateEntity.class, | ||||
|                 PlaylistEntity.class, PlaylistStreamEntity.class, PlaylistRemoteEntity.class | ||||
|                 PlaylistEntity.class, PlaylistStreamEntity.class, PlaylistRemoteEntity.class, | ||||
|                 FeedEntity.class, FeedGroupEntity.class, FeedGroupSubscriptionEntity.class | ||||
|         }, | ||||
|         version = DB_VER_12_0, | ||||
|         exportSchema = false | ||||
|         version = DB_VER_3 | ||||
| ) | ||||
| public abstract class AppDatabase extends RoomDatabase { | ||||
|  | ||||
|     public static final String DATABASE_NAME = "newpipe.db"; | ||||
|  | ||||
|     public abstract SubscriptionDAO subscriptionDAO(); | ||||
|  | ||||
|     public abstract SearchHistoryDAO searchHistoryDAO(); | ||||
|  | ||||
|     public abstract StreamDAO streamDAO(); | ||||
|  | ||||
|     public abstract StreamHistoryDAO streamHistoryDAO(); | ||||
|  | ||||
|     public abstract StreamStateDAO streamStateDAO(); | ||||
|  | ||||
|     public abstract PlaylistDAO playlistDAO(); | ||||
|  | ||||
|     public abstract PlaylistStreamDAO playlistStreamDAO(); | ||||
|  | ||||
|     public abstract PlaylistRemoteDAO playlistRemoteDAO(); | ||||
|  | ||||
|     public abstract FeedDAO feedDAO(); | ||||
|     public abstract FeedGroupDAO feedGroupDAO(); | ||||
|     public abstract SubscriptionDAO subscriptionDAO(); | ||||
| } | ||||
|   | ||||
| @@ -8,14 +8,14 @@ import android.util.Log; | ||||
| import org.schabi.newpipe.BuildConfig; | ||||
|  | ||||
| public class Migrations { | ||||
|  | ||||
|     public static final int DB_VER_11_0 = 1; | ||||
|     public static final int DB_VER_12_0 = 2; | ||||
|     public static final int DB_VER_1 = 1; | ||||
|     public static final int DB_VER_2 = 2; | ||||
|     public static final int DB_VER_3 = 3; | ||||
|  | ||||
|     public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); | ||||
|     private static final String TAG = Migrations.class.getName(); | ||||
|  | ||||
|     public static final Migration MIGRATION_11_12 = new Migration(DB_VER_11_0, DB_VER_12_0) { | ||||
|     public static final Migration MIGRATION_1_2 = new Migration(DB_VER_1, DB_VER_2) { | ||||
|         @Override | ||||
|         public void migrate(@NonNull SupportSQLiteDatabase database) { | ||||
|             if(DEBUG) { | ||||
| @@ -71,4 +71,29 @@ public class Migrations { | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     public static final Migration MIGRATION_2_3 = new Migration(DB_VER_2, DB_VER_3) { | ||||
|         @Override | ||||
|         public void migrate(@NonNull SupportSQLiteDatabase database) { | ||||
|             // Add NOT NULLs and new fields | ||||
|             database.execSQL("CREATE TABLE IF NOT EXISTS streams_new " + | ||||
|                     "(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, thumbnail_url TEXT, view_count INTEGER, textual_upload_date TEXT, upload_date INTEGER)"); | ||||
|  | ||||
|             database.execSQL("INSERT INTO streams_new (uid, service_id, url, title, stream_type, duration, uploader, thumbnail_url, view_count, textual_upload_date, upload_date)"+ | ||||
|                     " SELECT uid, service_id, url, title, stream_type, duration, uploader, thumbnail_url, NULL, NULL, NULL FROM streams"); | ||||
|  | ||||
|             database.execSQL("DROP TABLE streams"); | ||||
|             database.execSQL("ALTER TABLE streams_new RENAME TO streams"); | ||||
|             database.execSQL("CREATE UNIQUE INDEX index_streams_service_id_url ON streams (service_id, url)"); | ||||
|  | ||||
|             // Tables for feed feature | ||||
|             database.execSQL("CREATE TABLE IF NOT EXISTS feed (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)"); | ||||
|             database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)"); | ||||
|             database.execSQL("CREATE TABLE IF NOT EXISTS feed_group (uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, icon_id INTEGER NOT NULL)"); | ||||
|             database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join (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)"); | ||||
|             database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id ON feed_group_subscription_join (subscription_id)"); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,47 @@ | ||||
| package org.schabi.newpipe.database.feed.dao | ||||
|  | ||||
| import androidx.room.Dao | ||||
| import androidx.room.Insert | ||||
| import androidx.room.OnConflictStrategy | ||||
| import androidx.room.Query | ||||
| import io.reactivex.Flowable | ||||
| import org.schabi.newpipe.database.feed.model.FeedEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
|  | ||||
| @Dao | ||||
| abstract class FeedDAO { | ||||
|     @Query("DELETE FROM feed") | ||||
|     abstract fun deleteAll(): Int | ||||
|  | ||||
|     @Query(""" | ||||
|         SELECT s.* FROM streams s | ||||
|  | ||||
|         INNER JOIN feed f | ||||
|         ON s.uid = f.stream_id | ||||
|  | ||||
|         ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC | ||||
|         """) | ||||
|     abstract fun getAllStreams(): Flowable<List<StreamEntity>> | ||||
|  | ||||
|     @Query(""" | ||||
|         SELECT s.* FROM streams s | ||||
|  | ||||
|         INNER JOIN feed f | ||||
|         ON s.uid = f.stream_id | ||||
|  | ||||
|         INNER JOIN feed_group_subscription_join fgs | ||||
|         ON fgs.subscription_id = f.subscription_id | ||||
|  | ||||
|         INNER JOIN feed_group fg | ||||
|         ON fg.uid = fgs.group_id | ||||
|  | ||||
|         WHERE fgs.group_id = :groupId | ||||
|         """) | ||||
|     abstract fun getAllStreamsFromGroup(groupId: Long): Flowable<List<StreamEntity>> | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.FAIL) | ||||
|     abstract fun insert(feedEntity: FeedEntity) | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.FAIL) | ||||
|     abstract fun insertAll(entities: List<FeedEntity>): List<Long> | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| package org.schabi.newpipe.database.feed.dao | ||||
|  | ||||
| import androidx.room.* | ||||
| import io.reactivex.Flowable | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupEntity | ||||
|  | ||||
| @Dao | ||||
| abstract class FeedGroupDAO { | ||||
|     @Query("DELETE FROM feed_group") | ||||
|     abstract fun deleteAll(): Int | ||||
|  | ||||
|     @Query("SELECT * FROM feed_group") | ||||
|     abstract fun getAll(): Flowable<List<FeedGroupEntity>> | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.ABORT) | ||||
|     abstract fun insert(feedEntity: FeedGroupEntity) | ||||
| } | ||||
| @@ -0,0 +1,43 @@ | ||||
| package org.schabi.newpipe.database.feed.model | ||||
|  | ||||
| import androidx.room.ColumnInfo | ||||
| import androidx.room.Entity | ||||
| import androidx.room.ForeignKey | ||||
| import androidx.room.Index | ||||
| import org.schabi.newpipe.database.feed.model.FeedEntity.Companion.FEED_TABLE | ||||
| import org.schabi.newpipe.database.feed.model.FeedEntity.Companion.STREAM_ID | ||||
| import org.schabi.newpipe.database.feed.model.FeedEntity.Companion.SUBSCRIPTION_ID | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import org.schabi.newpipe.database.subscription.SubscriptionEntity | ||||
|  | ||||
| @Entity(tableName = FEED_TABLE, | ||||
|         primaryKeys = [STREAM_ID, SUBSCRIPTION_ID], | ||||
|         indices = [Index(SUBSCRIPTION_ID)], | ||||
|         foreignKeys = [ | ||||
|             ForeignKey( | ||||
|                     entity = StreamEntity::class, | ||||
|                     parentColumns = [StreamEntity.STREAM_ID], | ||||
|                     childColumns = [STREAM_ID], | ||||
|                     onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true), | ||||
|             ForeignKey( | ||||
|                     entity = SubscriptionEntity::class, | ||||
|                     parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID], | ||||
|                     childColumns = [SUBSCRIPTION_ID], | ||||
|                     onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true) | ||||
|         ] | ||||
| ) | ||||
| data class FeedEntity( | ||||
|         @ColumnInfo(name = STREAM_ID) | ||||
|         var streamId: Long, | ||||
|  | ||||
|         @ColumnInfo(name = SUBSCRIPTION_ID) | ||||
|         var subscriptionId: Long | ||||
| ) { | ||||
|  | ||||
|     companion object { | ||||
|         const val FEED_TABLE = "feed" | ||||
|  | ||||
|         const val STREAM_ID = "stream_id" | ||||
|         const val SUBSCRIPTION_ID = "subscription_id" | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package org.schabi.newpipe.database.feed.model | ||||
|  | ||||
| import androidx.room.ColumnInfo | ||||
| import androidx.room.Entity | ||||
| import androidx.room.PrimaryKey | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.FEED_GROUP_TABLE | ||||
|  | ||||
| @Entity(tableName = FEED_GROUP_TABLE) | ||||
| data class FeedGroupEntity( | ||||
|         @PrimaryKey(autoGenerate = true) | ||||
|         @ColumnInfo(name = ID) | ||||
|         val uid: Long, | ||||
|  | ||||
|         @ColumnInfo(name = NAME) | ||||
|         var name: String, | ||||
|  | ||||
|         @ColumnInfo(name = ICON) | ||||
|         var iconId: Int | ||||
| ) { | ||||
|     companion object { | ||||
|         const val FEED_GROUP_TABLE = "feed_group" | ||||
|  | ||||
|         const val ID = "uid" | ||||
|         const val NAME = "name" | ||||
|         const val ICON = "icon_id" | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| package org.schabi.newpipe.database.feed.model | ||||
|  | ||||
| import androidx.room.ColumnInfo | ||||
| import androidx.room.Entity | ||||
| import androidx.room.ForeignKey | ||||
| import androidx.room.ForeignKey.CASCADE | ||||
| import androidx.room.Index | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity.Companion.FEED_GROUP_SUBSCRIPTION_TABLE | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity.Companion.GROUP_ID | ||||
| import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity.Companion.SUBSCRIPTION_ID | ||||
| import org.schabi.newpipe.database.subscription.SubscriptionEntity | ||||
|  | ||||
| @Entity( | ||||
|         tableName = FEED_GROUP_SUBSCRIPTION_TABLE, | ||||
|         primaryKeys = [GROUP_ID, SUBSCRIPTION_ID], | ||||
|         indices = [Index(SUBSCRIPTION_ID)], | ||||
|         foreignKeys = [ | ||||
|             ForeignKey( | ||||
|                     entity = FeedGroupEntity::class, | ||||
|                     parentColumns = [FeedGroupEntity.ID], | ||||
|                     childColumns = [GROUP_ID], | ||||
|                     onDelete = CASCADE, onUpdate = CASCADE, deferred = true), | ||||
|  | ||||
|             ForeignKey( | ||||
|                     entity = SubscriptionEntity::class, | ||||
|                     parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID], | ||||
|                     childColumns = [SUBSCRIPTION_ID], | ||||
|                     onDelete = CASCADE, onUpdate = CASCADE, deferred = true) | ||||
|         ] | ||||
| ) | ||||
| data class FeedGroupSubscriptionEntity( | ||||
|         @ColumnInfo(name = GROUP_ID) | ||||
|         var feedGroupId: Long, | ||||
|  | ||||
|         @ColumnInfo(name = SUBSCRIPTION_ID) | ||||
|         var subscriptionId: Long | ||||
| ) { | ||||
|  | ||||
|     companion object { | ||||
|         const val FEED_GROUP_SUBSCRIPTION_TABLE = "feed_group_subscription_join" | ||||
|  | ||||
|         const val GROUP_ID = "group_id" | ||||
|         const val SUBSCRIPTION_ID = "subscription_id" | ||||
|     } | ||||
| } | ||||
| @@ -1,59 +0,0 @@ | ||||
| package org.schabi.newpipe.database.history.model; | ||||
|  | ||||
| import androidx.room.ColumnInfo; | ||||
|  | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class StreamHistoryEntry { | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_ID) | ||||
|     final public long uid; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_SERVICE_ID) | ||||
|     final public int serviceId; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_URL) | ||||
|     final public String url; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_TITLE) | ||||
|     final public String title; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_TYPE) | ||||
|     final public StreamType streamType; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_DURATION) | ||||
|     final public long duration; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_UPLOADER) | ||||
|     final public String uploader; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_THUMBNAIL_URL) | ||||
|     final public String thumbnailUrl; | ||||
|     @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) | ||||
|     final public long streamId; | ||||
|     @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) | ||||
|     final public Date accessDate; | ||||
|     @ColumnInfo(name = StreamHistoryEntity.STREAM_REPEAT_COUNT) | ||||
|     final public long repeatCount; | ||||
|  | ||||
|     public StreamHistoryEntry(long uid, int serviceId, String url, String title, | ||||
|                               StreamType streamType, long duration, String uploader, | ||||
|                               String thumbnailUrl, long streamId, Date accessDate, | ||||
|                               long repeatCount) { | ||||
|         this.uid = uid; | ||||
|         this.serviceId = serviceId; | ||||
|         this.url = url; | ||||
|         this.title = title; | ||||
|         this.streamType = streamType; | ||||
|         this.duration = duration; | ||||
|         this.uploader = uploader; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|         this.streamId = streamId; | ||||
|         this.accessDate = accessDate; | ||||
|         this.repeatCount = repeatCount; | ||||
|     } | ||||
|  | ||||
|     public StreamHistoryEntity toStreamHistoryEntity() { | ||||
|         return new StreamHistoryEntity(streamId, accessDate, repeatCount); | ||||
|     } | ||||
|  | ||||
|     public boolean hasEqualValues(StreamHistoryEntry other) { | ||||
|         return this.uid == other.uid && streamId == other.streamId && | ||||
|                 accessDate.compareTo(other.accessDate) == 0; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,30 @@ | ||||
| package org.schabi.newpipe.database.history.model | ||||
|  | ||||
| import androidx.room.ColumnInfo | ||||
| import androidx.room.Embedded | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import java.util.* | ||||
|  | ||||
| data class StreamHistoryEntry( | ||||
|         @Embedded | ||||
|         val streamEntity: StreamEntity, | ||||
|  | ||||
|         @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) | ||||
|         val streamId: Long, | ||||
|  | ||||
|         @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) | ||||
|         val accessDate: Date, | ||||
|  | ||||
|         @ColumnInfo(name = StreamHistoryEntity.STREAM_REPEAT_COUNT) | ||||
|         val repeatCount: Long | ||||
| ) { | ||||
|  | ||||
|     fun toStreamHistoryEntity(): StreamHistoryEntity { | ||||
|         return StreamHistoryEntity(streamId, accessDate, repeatCount) | ||||
|     } | ||||
|  | ||||
|     fun hasEqualValues(other: StreamHistoryEntry): Boolean { | ||||
|         return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId && | ||||
|                 accessDate.compareTo(other.accessDate) == 0 | ||||
|     } | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| package org.schabi.newpipe.database.playlist; | ||||
|  | ||||
| import androidx.room.ColumnInfo; | ||||
|  | ||||
| import org.schabi.newpipe.database.LocalItem; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
|  | ||||
| public class PlaylistStreamEntry implements LocalItem { | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_ID) | ||||
|     final public long uid; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_SERVICE_ID) | ||||
|     final public int serviceId; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_URL) | ||||
|     final public String url; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_TITLE) | ||||
|     final public String title; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_TYPE) | ||||
|     final public StreamType streamType; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_DURATION) | ||||
|     final public long duration; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_UPLOADER) | ||||
|     final public String uploader; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_THUMBNAIL_URL) | ||||
|     final public String thumbnailUrl; | ||||
|     @ColumnInfo(name = PlaylistStreamEntity.JOIN_STREAM_ID) | ||||
|     final public long streamId; | ||||
|     @ColumnInfo(name = PlaylistStreamEntity.JOIN_INDEX) | ||||
|     final public int joinIndex; | ||||
|  | ||||
|     public PlaylistStreamEntry(long uid, int serviceId, String url, String title, | ||||
|                                StreamType streamType, long duration, String uploader, | ||||
|                                String thumbnailUrl, long streamId, int joinIndex) { | ||||
|         this.uid = uid; | ||||
|         this.serviceId = serviceId; | ||||
|         this.url = url; | ||||
|         this.title = title; | ||||
|         this.streamType = streamType; | ||||
|         this.duration = duration; | ||||
|         this.uploader = uploader; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|         this.streamId = streamId; | ||||
|         this.joinIndex = joinIndex; | ||||
|     } | ||||
|  | ||||
|     public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException { | ||||
|         StreamInfoItem item = new StreamInfoItem(serviceId, url, title, streamType); | ||||
|         item.setThumbnailUrl(thumbnailUrl); | ||||
|         item.setUploaderName(uploader); | ||||
|         item.setDuration(duration); | ||||
|         return item; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public LocalItemType getLocalItemType() { | ||||
|         return LocalItemType.PLAYLIST_STREAM_ITEM; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| package org.schabi.newpipe.database.playlist | ||||
|  | ||||
| import androidx.room.ColumnInfo | ||||
| import androidx.room.Embedded | ||||
| import org.schabi.newpipe.database.LocalItem | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
|  | ||||
| class PlaylistStreamEntry( | ||||
|         @Embedded | ||||
|         val streamEntity: StreamEntity, | ||||
|  | ||||
|         @ColumnInfo(name = PlaylistStreamEntity.JOIN_STREAM_ID) | ||||
|         val streamId: Long, | ||||
|  | ||||
|         @ColumnInfo(name = PlaylistStreamEntity.JOIN_INDEX) | ||||
|         val joinIndex: Int | ||||
| ) : LocalItem { | ||||
|  | ||||
|     @Throws(IllegalArgumentException::class) | ||||
|     fun toStreamInfoItem(): StreamInfoItem { | ||||
|         val item = StreamInfoItem(streamEntity.serviceId, streamEntity.url, streamEntity.title, streamEntity.streamType) | ||||
|         item.duration = streamEntity.duration | ||||
|         item.uploaderName = streamEntity.uploader | ||||
|         item.thumbnailUrl = streamEntity.thumbnailUrl | ||||
|  | ||||
|         return item | ||||
|     } | ||||
|  | ||||
|     override fun getLocalItemType(): LocalItem.LocalItemType { | ||||
|         return LocalItem.LocalItemType.PLAYLIST_STREAM_ITEM | ||||
|     } | ||||
| } | ||||
| @@ -1,69 +0,0 @@ | ||||
| package org.schabi.newpipe.database.stream; | ||||
|  | ||||
| import androidx.room.ColumnInfo; | ||||
|  | ||||
| import org.schabi.newpipe.database.LocalItem; | ||||
| import org.schabi.newpipe.database.history.model.StreamHistoryEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class StreamStatisticsEntry implements LocalItem { | ||||
|     final public static String STREAM_LATEST_DATE = "latestAccess"; | ||||
|     final public static String STREAM_WATCH_COUNT = "watchCount"; | ||||
|  | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_ID) | ||||
|     final public long uid; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_SERVICE_ID) | ||||
|     final public int serviceId; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_URL) | ||||
|     final public String url; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_TITLE) | ||||
|     final public String title; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_TYPE) | ||||
|     final public StreamType streamType; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_DURATION) | ||||
|     final public long duration; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_UPLOADER) | ||||
|     final public String uploader; | ||||
|     @ColumnInfo(name = StreamEntity.STREAM_THUMBNAIL_URL) | ||||
|     final public String thumbnailUrl; | ||||
|     @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) | ||||
|     final public long streamId; | ||||
|     @ColumnInfo(name = StreamStatisticsEntry.STREAM_LATEST_DATE) | ||||
|     final public Date latestAccessDate; | ||||
|     @ColumnInfo(name = StreamStatisticsEntry.STREAM_WATCH_COUNT) | ||||
|     final public long watchCount; | ||||
|  | ||||
|     public StreamStatisticsEntry(long uid, int serviceId, String url, String title, | ||||
|                                  StreamType streamType, long duration, String uploader, | ||||
|                                  String thumbnailUrl, long streamId, Date latestAccessDate, | ||||
|                                  long watchCount) { | ||||
|         this.uid = uid; | ||||
|         this.serviceId = serviceId; | ||||
|         this.url = url; | ||||
|         this.title = title; | ||||
|         this.streamType = streamType; | ||||
|         this.duration = duration; | ||||
|         this.uploader = uploader; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|         this.streamId = streamId; | ||||
|         this.latestAccessDate = latestAccessDate; | ||||
|         this.watchCount = watchCount; | ||||
|     } | ||||
|  | ||||
|     public StreamInfoItem toStreamInfoItem() { | ||||
|         StreamInfoItem item = new StreamInfoItem(serviceId, url, title, streamType); | ||||
|         item.setDuration(duration); | ||||
|         item.setUploaderName(uploader); | ||||
|         item.setThumbnailUrl(thumbnailUrl); | ||||
|         return item; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public LocalItemType getLocalItemType() { | ||||
|         return LocalItemType.STATISTIC_STREAM_ITEM; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| package org.schabi.newpipe.database.stream | ||||
|  | ||||
| import androidx.room.ColumnInfo | ||||
| import androidx.room.Embedded | ||||
| import org.schabi.newpipe.database.LocalItem | ||||
| import org.schabi.newpipe.database.history.model.StreamHistoryEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
| import java.util.* | ||||
|  | ||||
| class StreamStatisticsEntry( | ||||
|         @Embedded | ||||
|         val streamEntity: StreamEntity, | ||||
|  | ||||
|         @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) | ||||
|         val streamId: Long, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_LATEST_DATE) | ||||
|         val latestAccessDate: Date, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_WATCH_COUNT) | ||||
|         val watchCount: Long | ||||
| ) : LocalItem { | ||||
|  | ||||
|     fun toStreamInfoItem(): StreamInfoItem { | ||||
|         val item = StreamInfoItem(streamEntity.serviceId, streamEntity.url, streamEntity.title, streamEntity.streamType) | ||||
|         item.duration = streamEntity.duration | ||||
|         item.uploaderName = streamEntity.uploader | ||||
|         item.thumbnailUrl = streamEntity.thumbnailUrl | ||||
|  | ||||
|         return item | ||||
|     } | ||||
|  | ||||
|     override fun getLocalItemType(): LocalItem.LocalItemType { | ||||
|         return LocalItem.LocalItemType.STATISTIC_STREAM_ITEM | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         const val STREAM_LATEST_DATE = "latestAccess" | ||||
|         const val STREAM_WATCH_COUNT = "watchCount" | ||||
|     } | ||||
| } | ||||
| @@ -1,98 +0,0 @@ | ||||
| package org.schabi.newpipe.database.stream.dao; | ||||
|  | ||||
| import androidx.room.Dao; | ||||
| import androidx.room.Insert; | ||||
| import androidx.room.OnConflictStrategy; | ||||
| import androidx.room.Query; | ||||
| import androidx.room.Transaction; | ||||
|  | ||||
| import org.schabi.newpipe.database.BasicDAO; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.database.history.model.StreamHistoryEntity; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.Flowable; | ||||
|  | ||||
| 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_SERVICE_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL; | ||||
| import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; | ||||
|  | ||||
| @Dao | ||||
| public abstract class StreamDAO implements BasicDAO<StreamEntity> { | ||||
|     @Override | ||||
|     @Query("SELECT * FROM " + STREAM_TABLE) | ||||
|     public abstract Flowable<List<StreamEntity>> getAll(); | ||||
|  | ||||
|     @Override | ||||
|     @Query("DELETE FROM " + STREAM_TABLE) | ||||
|     public abstract int deleteAll(); | ||||
|  | ||||
|     @Override | ||||
|     @Query("SELECT * FROM " + STREAM_TABLE + " WHERE " + STREAM_SERVICE_ID + " = :serviceId") | ||||
|     public abstract Flowable<List<StreamEntity>> listByService(int serviceId); | ||||
|  | ||||
|     @Query("SELECT * FROM " + STREAM_TABLE + " WHERE " + | ||||
|             STREAM_URL + " = :url AND " + | ||||
|             STREAM_SERVICE_ID + " = :serviceId") | ||||
|     public abstract Flowable<List<StreamEntity>> getStream(long serviceId, String url); | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.IGNORE) | ||||
|     abstract void silentInsertAllInternal(final List<StreamEntity> streams); | ||||
|  | ||||
|     @Query("SELECT " + STREAM_ID + " FROM " + STREAM_TABLE + " WHERE " + | ||||
|             STREAM_URL + " = :url AND " + | ||||
|             STREAM_SERVICE_ID + " = :serviceId") | ||||
|     abstract Long getStreamIdInternal(long serviceId, String url); | ||||
|  | ||||
|     @Transaction | ||||
|     public long upsert(StreamEntity stream) { | ||||
|         final Long streamIdCandidate = getStreamIdInternal(stream.getServiceId(), stream.getUrl()); | ||||
|  | ||||
|         if (streamIdCandidate == null) { | ||||
|             return insert(stream); | ||||
|         } else { | ||||
|             stream.setUid(streamIdCandidate); | ||||
|             update(stream); | ||||
|             return streamIdCandidate; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Transaction | ||||
|     public List<Long> upsertAll(List<StreamEntity> streams) { | ||||
|         silentInsertAllInternal(streams); | ||||
|  | ||||
|         final List<Long> streamIds = new ArrayList<>(streams.size()); | ||||
|         for (StreamEntity stream : streams) { | ||||
|             final Long streamId = getStreamIdInternal(stream.getServiceId(), stream.getUrl()); | ||||
|             if (streamId == null) { | ||||
|                 throw new IllegalStateException("StreamID cannot be null just after insertion."); | ||||
|             } | ||||
|  | ||||
|             streamIds.add(streamId); | ||||
|             stream.setUid(streamId); | ||||
|         } | ||||
|  | ||||
|         update(streams); | ||||
|         return streamIds; | ||||
|     } | ||||
|  | ||||
|     @Query("DELETE FROM " + STREAM_TABLE + " WHERE " + STREAM_ID + | ||||
|             " NOT IN " + | ||||
|             "(SELECT DISTINCT " + STREAM_ID + " FROM " + STREAM_TABLE + | ||||
|  | ||||
|             " LEFT JOIN " + STREAM_HISTORY_TABLE + | ||||
|             " ON " + STREAM_ID + " = " + | ||||
|             StreamHistoryEntity.STREAM_HISTORY_TABLE + "." + StreamHistoryEntity.JOIN_STREAM_ID + | ||||
|  | ||||
|             " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE + | ||||
|             " ON " + STREAM_ID + " = " + | ||||
|             PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE + "." + PlaylistStreamEntity.JOIN_STREAM_ID + | ||||
|             ")") | ||||
|     public abstract int deleteOrphans(); | ||||
| } | ||||
| @@ -0,0 +1,116 @@ | ||||
| package org.schabi.newpipe.database.stream.dao | ||||
|  | ||||
| import androidx.room.* | ||||
| import io.reactivex.Flowable | ||||
| import org.schabi.newpipe.database.BasicDAO | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID | ||||
| import org.schabi.newpipe.extractor.stream.StreamType | ||||
| import org.schabi.newpipe.extractor.stream.StreamType.* | ||||
| import java.util.* | ||||
| import kotlin.collections.ArrayList | ||||
|  | ||||
| @Dao | ||||
| abstract class StreamDAO : BasicDAO<StreamEntity> { | ||||
|     @Query("SELECT * FROM streams") | ||||
|     abstract override fun getAll(): Flowable<List<StreamEntity>> | ||||
|  | ||||
|     @Query("DELETE FROM streams") | ||||
|     abstract override fun deleteAll(): Int | ||||
|  | ||||
|     @Query("SELECT * FROM streams WHERE service_id = :serviceId") | ||||
|     abstract override fun listByService(serviceId: Int): Flowable<List<StreamEntity>> | ||||
|  | ||||
|     @Query("SELECT * FROM streams WHERE url = :url AND service_id = :serviceId") | ||||
|     abstract fun getStream(serviceId: Long, url: String): Flowable<List<StreamEntity>> | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.IGNORE) | ||||
|     internal abstract fun silentInsertInternal(stream: StreamEntity): Long | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.IGNORE) | ||||
|     internal abstract fun silentInsertAllInternal(streams: List<StreamEntity>): List<Long> | ||||
|  | ||||
|     @Query(""" | ||||
|         SELECT uid, stream_type, textual_upload_date, upload_date FROM streams | ||||
|         WHERE url = :url AND service_id = :serviceId | ||||
|         """) | ||||
|     internal abstract fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed? | ||||
|  | ||||
|     @Transaction | ||||
|     open fun upsert(newerStream: StreamEntity): Long { | ||||
|         val uid = silentInsertInternal(newerStream) | ||||
|  | ||||
|         if (uid != -1L) { | ||||
|             newerStream.uid = uid | ||||
|             return uid | ||||
|         } | ||||
|  | ||||
|         compareAndUpdateStream(newerStream) | ||||
|  | ||||
|         update(newerStream) | ||||
|         return newerStream.uid | ||||
|     } | ||||
|  | ||||
|     @Transaction | ||||
|     open fun upsertAll(streams: List<StreamEntity>): List<Long> { | ||||
|         val insertUidList = silentInsertAllInternal(streams) | ||||
|  | ||||
|         val streamIds = ArrayList<Long>(streams.size) | ||||
|         for ((index, uid) in insertUidList.withIndex()) { | ||||
|             val newerStream = streams[index] | ||||
|             if (uid != -1L) { | ||||
|                 streamIds.add(uid) | ||||
|                 newerStream.uid = uid | ||||
|                 continue | ||||
|             } | ||||
|  | ||||
|             compareAndUpdateStream(newerStream) | ||||
|             streamIds.add(newerStream.uid) | ||||
|         } | ||||
|  | ||||
|         update(streams) | ||||
|         return streamIds | ||||
|     } | ||||
|  | ||||
|     private fun compareAndUpdateStream(newerStream: StreamEntity) { | ||||
|         val existentMinimalStream = getMinimalStreamForCompare(newerStream.serviceId, newerStream.url) | ||||
|                 ?: throw IllegalStateException("Stream cannot be null just after insertion.") | ||||
|         newerStream.uid = existentMinimalStream.uid | ||||
|  | ||||
|         val isNewerStreamLive = newerStream.streamType == AUDIO_LIVE_STREAM || newerStream.streamType == LIVE_STREAM | ||||
|         if (!isNewerStreamLive) { | ||||
|             if (existentMinimalStream.uploadDate != null) newerStream.uploadDate = existentMinimalStream.uploadDate | ||||
|             if (existentMinimalStream.textualUploadDate != null) newerStream.textualUploadDate = existentMinimalStream.textualUploadDate | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Query(""" | ||||
|         DELETE FROM streams WHERE | ||||
|  | ||||
|         NOT EXISTS (SELECT 1 FROM stream_history sh | ||||
|         WHERE sh.stream_id = streams.uid) | ||||
|  | ||||
|         AND NOT EXISTS (SELECT 1 FROM playlist_stream_join ps | ||||
|         WHERE ps.stream_id = streams.uid) | ||||
|  | ||||
|         AND NOT EXISTS (SELECT 1 FROM feed f | ||||
|         WHERE f.stream_id = streams.uid) | ||||
|         """) | ||||
|     abstract fun deleteOrphans(): Int | ||||
|  | ||||
|     /** | ||||
|      * Minimal entry class used when comparing/updating an existent stream. | ||||
|      */ | ||||
|     internal data class StreamCompareFeed( | ||||
|             @ColumnInfo(name = STREAM_ID) | ||||
|             var uid: Long = 0, | ||||
|  | ||||
|             @field:ColumnInfo(name = StreamEntity.STREAM_TYPE) | ||||
|             var streamType: StreamType, | ||||
|  | ||||
|             @field:ColumnInfo(name = StreamEntity.STREAM_TEXTUAL_UPLOAD_DATE) | ||||
|             var textualUploadDate: String? = null, | ||||
|  | ||||
|             @field:ColumnInfo(name = StreamEntity.STREAM_UPLOAD_DATE) | ||||
|             var uploadDate: Date? = null) | ||||
| } | ||||
| @@ -1,153 +0,0 @@ | ||||
| package org.schabi.newpipe.database.stream.model; | ||||
|  | ||||
| import androidx.room.ColumnInfo; | ||||
| import androidx.room.Entity; | ||||
| import androidx.room.Ignore; | ||||
| import androidx.room.Index; | ||||
| import androidx.room.PrimaryKey; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_SERVICE_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL; | ||||
|  | ||||
| @Entity(tableName = STREAM_TABLE, | ||||
|         indices = {@Index(value = {STREAM_SERVICE_ID, STREAM_URL}, unique = true)}) | ||||
| public class StreamEntity implements Serializable { | ||||
|  | ||||
|     final public static String STREAM_TABLE             = "streams"; | ||||
|     final public static String STREAM_ID                = "uid"; | ||||
|     final public static String STREAM_SERVICE_ID        = "service_id"; | ||||
|     final public static String STREAM_URL               = "url"; | ||||
|     final public static String STREAM_TITLE             = "title"; | ||||
|     final public static String STREAM_TYPE              = "stream_type"; | ||||
|     final public static String STREAM_DURATION          = "duration"; | ||||
|     final public static String STREAM_UPLOADER          = "uploader"; | ||||
|     final public static String STREAM_THUMBNAIL_URL     = "thumbnail_url"; | ||||
|  | ||||
|     @PrimaryKey(autoGenerate = true) | ||||
|     @ColumnInfo(name = STREAM_ID) | ||||
|     private long uid = 0; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_SERVICE_ID) | ||||
|     private int serviceId = Constants.NO_SERVICE_ID; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_URL) | ||||
|     private String url; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_TITLE) | ||||
|     private String title; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_TYPE) | ||||
|     private StreamType streamType; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_DURATION) | ||||
|     private Long duration; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_UPLOADER) | ||||
|     private String uploader; | ||||
|  | ||||
|     @ColumnInfo(name = STREAM_THUMBNAIL_URL) | ||||
|     private String thumbnailUrl; | ||||
|  | ||||
|     public StreamEntity(final int serviceId, final String title, final String url, | ||||
|                         final StreamType streamType, final String thumbnailUrl, final String uploader, | ||||
|                         final long duration) { | ||||
|         this.serviceId = serviceId; | ||||
|         this.title = title; | ||||
|         this.url = url; | ||||
|         this.streamType = streamType; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|         this.uploader = uploader; | ||||
|         this.duration = duration; | ||||
|     } | ||||
|  | ||||
|     @Ignore | ||||
|     public StreamEntity(final StreamInfoItem item) { | ||||
|         this(item.getServiceId(), item.getName(), item.getUrl(), item.getStreamType(), item.getThumbnailUrl(), | ||||
|                 item.getUploaderName(), item.getDuration()); | ||||
|     } | ||||
|  | ||||
|     @Ignore | ||||
|     public StreamEntity(final StreamInfo info) { | ||||
|         this(info.getServiceId(), info.getName(), info.getUrl(), info.getStreamType(), info.getThumbnailUrl(), | ||||
|                 info.getUploaderName(), info.getDuration()); | ||||
|     } | ||||
|  | ||||
|     @Ignore | ||||
|     public StreamEntity(final PlayQueueItem item) { | ||||
|         this(item.getServiceId(), item.getTitle(), item.getUrl(), item.getStreamType(), | ||||
|                 item.getThumbnailUrl(), item.getUploader(), item.getDuration()); | ||||
|     } | ||||
|  | ||||
|     public long getUid() { | ||||
|         return uid; | ||||
|     } | ||||
|  | ||||
|     public void setUid(long uid) { | ||||
|         this.uid = uid; | ||||
|     } | ||||
|  | ||||
|     public int getServiceId() { | ||||
|         return serviceId; | ||||
|     } | ||||
|  | ||||
|     public void setServiceId(int serviceId) { | ||||
|         this.serviceId = serviceId; | ||||
|     } | ||||
|  | ||||
|     public String getUrl() { | ||||
|         return url; | ||||
|     } | ||||
|  | ||||
|     public void setUrl(String url) { | ||||
|         this.url = url; | ||||
|     } | ||||
|  | ||||
|     public String getTitle() { | ||||
|         return title; | ||||
|     } | ||||
|  | ||||
|     public void setTitle(String title) { | ||||
|         this.title = title; | ||||
|     } | ||||
|  | ||||
|     public StreamType getStreamType() { | ||||
|         return streamType; | ||||
|     } | ||||
|  | ||||
|     public void setStreamType(StreamType type) { | ||||
|         this.streamType = type; | ||||
|     } | ||||
|  | ||||
|     public Long getDuration() { | ||||
|         return duration; | ||||
|     } | ||||
|  | ||||
|     public void setDuration(Long duration) { | ||||
|         this.duration = duration; | ||||
|     } | ||||
|  | ||||
|     public String getUploader() { | ||||
|         return uploader; | ||||
|     } | ||||
|  | ||||
|     public void setUploader(String uploader) { | ||||
|         this.uploader = uploader; | ||||
|     } | ||||
|  | ||||
|     public String getThumbnailUrl() { | ||||
|         return thumbnailUrl; | ||||
|     } | ||||
|  | ||||
|     public void setThumbnailUrl(String thumbnailUrl) { | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,107 @@ | ||||
| package org.schabi.newpipe.database.stream.model | ||||
|  | ||||
| import androidx.room.* | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_SERVICE_ID | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_TABLE | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_URL | ||||
| import org.schabi.newpipe.extractor.localization.DateWrapper | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
| import org.schabi.newpipe.extractor.stream.StreamType | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem | ||||
| import java.io.Serializable | ||||
| import java.util.* | ||||
|  | ||||
| @Entity(tableName = STREAM_TABLE, | ||||
|         indices = [ | ||||
|             Index(value = [STREAM_SERVICE_ID, STREAM_URL], unique = true) | ||||
|         ] | ||||
| ) | ||||
| data class StreamEntity( | ||||
|         @PrimaryKey(autoGenerate = true) | ||||
|         @ColumnInfo(name = STREAM_ID) | ||||
|         var uid: Long = 0, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_SERVICE_ID) | ||||
|         var serviceId: Int, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_URL) | ||||
|         var url: String, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_TITLE) | ||||
|         var title: String, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_TYPE) | ||||
|         var streamType: StreamType, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_DURATION) | ||||
|         var duration: Long, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_UPLOADER) | ||||
|         var uploader: String, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_THUMBNAIL_URL) | ||||
|         var thumbnailUrl: String? = null, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_VIEWS) | ||||
|         var viewCount: Long? = null, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_TEXTUAL_UPLOAD_DATE) | ||||
|         var textualUploadDate: String? = null, | ||||
|  | ||||
|         @ColumnInfo(name = STREAM_UPLOAD_DATE) | ||||
|         var uploadDate: Date? = null | ||||
| ) : Serializable { | ||||
|  | ||||
|     @Ignore | ||||
|     constructor(item: StreamInfoItem) : this( | ||||
|             serviceId = item.serviceId, url = item.url, title = item.name, | ||||
|             streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, | ||||
|             thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount, | ||||
|             textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.date()?.time | ||||
|     ) | ||||
|  | ||||
|     @Ignore | ||||
|     constructor(info: StreamInfo) : this( | ||||
|             serviceId = info.serviceId, url = info.url, title = info.name, | ||||
|             streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, | ||||
|             thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount, | ||||
|             textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.date()?.time | ||||
|     ) | ||||
|  | ||||
|     @Ignore | ||||
|     constructor(item: PlayQueueItem) : this( | ||||
|             serviceId = item.serviceId, url = item.url, title = item.title, | ||||
|             streamType = item.streamType, duration = item.duration, uploader = item.uploader, | ||||
|             thumbnailUrl = item.thumbnailUrl | ||||
|     ) | ||||
|  | ||||
|     fun toStreamInfoItem(): StreamInfoItem { | ||||
|         val item = StreamInfoItem(serviceId, url, title, streamType) | ||||
|         item.duration = duration | ||||
|         item.uploaderName = uploader | ||||
|         item.thumbnailUrl = thumbnailUrl | ||||
|  | ||||
|         if (viewCount != null) item.viewCount = viewCount as Long | ||||
|         item.textualUploadDate = textualUploadDate | ||||
|         item.uploadDate = uploadDate?.let { DateWrapper(Calendar.getInstance().apply { time = it }) } | ||||
|  | ||||
|         return item | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         const val STREAM_TABLE = "streams" | ||||
|         const val STREAM_ID = "uid" | ||||
|         const val STREAM_SERVICE_ID = "service_id" | ||||
|         const val STREAM_URL = "url" | ||||
|         const val STREAM_TITLE = "title" | ||||
|         const val STREAM_TYPE = "stream_type" | ||||
|         const val STREAM_DURATION = "duration" | ||||
|         const val STREAM_UPLOADER = "uploader" | ||||
|         const val STREAM_THUMBNAIL_URL = "thumbnail_url" | ||||
|  | ||||
|         const val STREAM_VIEWS = "view_count" | ||||
|         const val STREAM_TEXTUAL_UPLOAD_DATE = "textual_upload_date" | ||||
|         const val STREAM_UPLOAD_DATE = "upload_date" | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,9 @@ public abstract class SubscriptionDAO implements BasicDAO<SubscriptionEntity> { | ||||
|     @Query("SELECT * FROM " + SUBSCRIPTION_TABLE) | ||||
|     public abstract Flowable<List<SubscriptionEntity>> getAll(); | ||||
|  | ||||
|     @Query("SELECT COUNT(*) FROM subscriptions") | ||||
|     public abstract Flowable<Long> rowCount(); | ||||
|  | ||||
|     @Override | ||||
|     @Query("DELETE FROM " + SUBSCRIPTION_TABLE) | ||||
|     public abstract int deleteAll(); | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCR | ||||
|         indices = {@Index(value = {SUBSCRIPTION_SERVICE_ID, SUBSCRIPTION_URL}, unique = true)}) | ||||
| public class SubscriptionEntity { | ||||
|  | ||||
|     final static String SUBSCRIPTION_UID                = "uid"; | ||||
|     public final static String SUBSCRIPTION_UID         = "uid"; | ||||
|     final static String SUBSCRIPTION_TABLE              = "subscriptions"; | ||||
|     final static String SUBSCRIPTION_SERVICE_ID         = "service_id"; | ||||
|     final static String SUBSCRIPTION_URL                = "url"; | ||||
|   | ||||
| @@ -269,11 +269,11 @@ public class HistoryRecordManager { | ||||
|             for (LocalItem item : items) { | ||||
|                 long streamId; | ||||
|                 if (item instanceof StreamStatisticsEntry) { | ||||
|                     streamId = ((StreamStatisticsEntry) item).streamId; | ||||
|                     streamId = ((StreamStatisticsEntry) item).getStreamId(); | ||||
|                 } else if (item instanceof PlaylistStreamEntity) { | ||||
|                     streamId = ((PlaylistStreamEntity) item).getStreamUid(); | ||||
|                 } else if (item instanceof PlaylistStreamEntry) { | ||||
|                     streamId = ((PlaylistStreamEntry) item).streamId; | ||||
|                     streamId = ((PlaylistStreamEntry) item).getStreamId(); | ||||
|                 } else { | ||||
|                     result.add(null); | ||||
|                     continue; | ||||
|   | ||||
| @@ -76,11 +76,11 @@ public class StatisticsPlaylistFragment | ||||
|         switch (sortMode) { | ||||
|             case LAST_PLAYED: | ||||
|                 Collections.sort(results, (left, right) -> | ||||
|                     right.latestAccessDate.compareTo(left.latestAccessDate)); | ||||
|                     right.getLatestAccessDate().compareTo(left.getLatestAccessDate())); | ||||
|                 return results; | ||||
|             case MOST_PLAYED: | ||||
|                 Collections.sort(results, (left, right) -> | ||||
|                         Long.compare(right.watchCount, left.watchCount)); | ||||
|                         Long.compare(right.getWatchCount(), left.getWatchCount())); | ||||
|                 return results; | ||||
|             default: return null; | ||||
|         } | ||||
| @@ -153,9 +153,9 @@ public class StatisticsPlaylistFragment | ||||
|                 if (selectedItem instanceof StreamStatisticsEntry) { | ||||
|                     final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem; | ||||
|                     NavigationHelper.openVideoDetailFragment(getFM(), | ||||
|                             item.serviceId, | ||||
|                             item.url, | ||||
|                             item.title); | ||||
|                             item.getStreamEntity().getServiceId(), | ||||
|                             item.getStreamEntity().getUrl(), | ||||
|                             item.getStreamEntity().getTitle()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -402,7 +402,7 @@ public class StatisticsPlaylistFragment | ||||
|                 .get(index); | ||||
|         if(infoItem instanceof StreamStatisticsEntry) { | ||||
|             final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem; | ||||
|             final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId) | ||||
|             final Disposable onDelete = recordManager.deleteStreamHistory(entry.getStreamId()) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribe( | ||||
|                             howManyDeleted -> { | ||||
|   | ||||
| @@ -52,12 +52,12 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { | ||||
|         if (!(localItem instanceof PlaylistStreamEntry)) return; | ||||
|         final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem; | ||||
|  | ||||
|         itemVideoTitleView.setText(item.title); | ||||
|         itemAdditionalDetailsView.setText(Localization.concatenateStrings(item.uploader, | ||||
|                 NewPipe.getNameOfService(item.serviceId))); | ||||
|         itemVideoTitleView.setText(item.getStreamEntity().getTitle()); | ||||
|         itemAdditionalDetailsView.setText(Localization.concatenateStrings(item.getStreamEntity().getUploader(), | ||||
|                 NewPipe.getNameOfService(item.getStreamEntity().getServiceId()))); | ||||
|  | ||||
|         if (item.duration > 0) { | ||||
|             itemDurationView.setText(Localization.getDurationString(item.duration)); | ||||
|         if (item.getStreamEntity().getDuration() > 0) { | ||||
|             itemDurationView.setText(Localization.getDurationString(item.getStreamEntity().getDuration())); | ||||
|             itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(), | ||||
|                     R.color.duration_background_color)); | ||||
|             itemDurationView.setVisibility(View.VISIBLE); | ||||
| @@ -65,7 +65,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { | ||||
|             StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0); | ||||
|             if (state != null) { | ||||
|                 itemProgressView.setVisibility(View.VISIBLE); | ||||
|                 itemProgressView.setMax((int) item.duration); | ||||
|                 itemProgressView.setMax((int) item.getStreamEntity().getDuration()); | ||||
|                 itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime())); | ||||
|             } else { | ||||
|                 itemProgressView.setVisibility(View.GONE); | ||||
| @@ -75,7 +75,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { | ||||
|         } | ||||
|  | ||||
|         // Default thumbnail is shown on error, while loading and if the url is empty | ||||
|         itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, | ||||
|         itemBuilder.displayImage(item.getStreamEntity().getThumbnailUrl(), itemThumbnailView, | ||||
|                 ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); | ||||
|  | ||||
|         itemView.setOnClickListener(view -> { | ||||
| @@ -102,8 +102,8 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { | ||||
|         final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem; | ||||
|  | ||||
|         StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0); | ||||
|         if (state != null && item.duration > 0) { | ||||
|             itemProgressView.setMax((int) item.duration); | ||||
|         if (state != null && item.getStreamEntity().getDuration() > 0) { | ||||
|             itemProgressView.setMax((int) item.getStreamEntity().getDuration()); | ||||
|             if (itemProgressView.getVisibility() == View.VISIBLE) { | ||||
|                 itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime())); | ||||
|             } else { | ||||
|   | ||||
| @@ -71,9 +71,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { | ||||
|     private String getStreamInfoDetailLine(final StreamStatisticsEntry entry, | ||||
|                                            final DateFormat dateFormat) { | ||||
|         final String watchCount = Localization.shortViewCount(itemBuilder.getContext(), | ||||
|                 entry.watchCount); | ||||
|         final String uploadDate = dateFormat.format(entry.latestAccessDate); | ||||
|         final String serviceName = NewPipe.getNameOfService(entry.serviceId); | ||||
|                 entry.getWatchCount()); | ||||
|         final String uploadDate = dateFormat.format(entry.getLatestAccessDate()); | ||||
|         final String serviceName = NewPipe.getNameOfService(entry.getStreamEntity().getServiceId()); | ||||
|         return Localization.concatenateStrings(watchCount, uploadDate, serviceName); | ||||
|     } | ||||
|  | ||||
| @@ -82,11 +82,11 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { | ||||
|         if (!(localItem instanceof StreamStatisticsEntry)) return; | ||||
|         final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem; | ||||
|  | ||||
|         itemVideoTitleView.setText(item.title); | ||||
|         itemUploaderView.setText(item.uploader); | ||||
|         itemVideoTitleView.setText(item.getStreamEntity().getTitle()); | ||||
|         itemUploaderView.setText(item.getStreamEntity().getUploader()); | ||||
|  | ||||
|         if (item.duration > 0) { | ||||
|             itemDurationView.setText(Localization.getDurationString(item.duration)); | ||||
|         if (item.getStreamEntity().getDuration() > 0) { | ||||
|             itemDurationView.setText(Localization.getDurationString(item.getStreamEntity().getDuration())); | ||||
|             itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(), | ||||
|                     R.color.duration_background_color)); | ||||
|             itemDurationView.setVisibility(View.VISIBLE); | ||||
| @@ -94,7 +94,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { | ||||
|             StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0); | ||||
|             if (state != null) { | ||||
|                 itemProgressView.setVisibility(View.VISIBLE); | ||||
|                 itemProgressView.setMax((int) item.duration); | ||||
|                 itemProgressView.setMax((int) item.getStreamEntity().getDuration()); | ||||
|                 itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime())); | ||||
|             } else { | ||||
|                 itemProgressView.setVisibility(View.GONE); | ||||
| @@ -109,7 +109,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { | ||||
|         } | ||||
|  | ||||
|         // Default thumbnail is shown on error, while loading and if the url is empty | ||||
|         itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, | ||||
|         itemBuilder.displayImage(item.getStreamEntity().getThumbnailUrl(), itemThumbnailView, | ||||
|                 ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); | ||||
|  | ||||
|         itemView.setOnClickListener(view -> { | ||||
| @@ -133,8 +133,8 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { | ||||
|         final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem; | ||||
|  | ||||
|         StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0); | ||||
|         if (state != null && item.duration > 0) { | ||||
|             itemProgressView.setMax((int) item.duration); | ||||
|         if (state != null && item.getStreamEntity().getDuration() > 0) { | ||||
|             itemProgressView.setMax((int) item.getStreamEntity().getDuration()); | ||||
|             if (itemProgressView.getVisibility() == View.VISIBLE) { | ||||
|                 itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime())); | ||||
|             } else { | ||||
|   | ||||
| @@ -168,7 +168,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|                 if (selectedItem instanceof PlaylistStreamEntry) { | ||||
|                     final PlaylistStreamEntry item = (PlaylistStreamEntry) selectedItem; | ||||
|                     NavigationHelper.openVideoDetailFragment(getFragmentManager(), | ||||
|                             item.serviceId, item.url, item.title); | ||||
|                             item.getStreamEntity().getServiceId(), item.getStreamEntity().getUrl(), item.getStreamEntity().getTitle()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -422,7 +422,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         String newThumbnailUrl; | ||||
|  | ||||
|         if (!itemListAdapter.getItemsList().isEmpty()) { | ||||
|             newThumbnailUrl = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0)).thumbnailUrl; | ||||
|             newThumbnailUrl = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0)).getStreamEntity().getThumbnailUrl(); | ||||
|         } else { | ||||
|             newThumbnailUrl = "drawable://" + R.drawable.dummy_thumbnail_playlist; | ||||
|         } | ||||
| @@ -434,7 +434,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         if (itemListAdapter == null) return; | ||||
|  | ||||
|         itemListAdapter.removeItem(item); | ||||
|         if (playlistManager.getPlaylistThumbnail(playlistId).equals(item.thumbnailUrl)) | ||||
|         if (playlistManager.getPlaylistThumbnail(playlistId).equals(item.getStreamEntity().getThumbnailUrl())) | ||||
|             updateThumbnailUrl(); | ||||
|  | ||||
|         setVideoCount(itemListAdapter.getItemsList().size()); | ||||
| @@ -472,7 +472,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         List<Long> streamIds = new ArrayList<>(items.size()); | ||||
|         for (final LocalItem item : items) { | ||||
|             if (item instanceof PlaylistStreamEntry) { | ||||
|                 streamIds.add(((PlaylistStreamEntry) item).streamId); | ||||
|                 streamIds.add(((PlaylistStreamEntry) item).getStreamId()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -579,7 +579,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         StreamDialogEntry.start_here_on_background.setCustomAction( | ||||
|                 (fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true)); | ||||
|         StreamDialogEntry.set_as_playlist_thumbnail.setCustomAction( | ||||
|                 (fragment, infoItemDuplicate) -> changeThumbnailUrl(item.thumbnailUrl)); | ||||
|                 (fragment, infoItemDuplicate) -> changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl())); | ||||
|         StreamDialogEntry.delete.setCustomAction( | ||||
|                 (fragment, infoItemDuplicate) -> deleteItem(item)); | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/db.dia
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/db.dia
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user
	 Mauricio Colli
					Mauricio Colli