mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	-Added schema for local playlist and stream statistics.
-Added normalized schema for stream history. -Added managers for specialized database access for stream and local playlist.
This commit is contained in:
		| @@ -28,4 +28,10 @@ public final class NewPipeDatabase { | ||||
|  | ||||
|         return databaseInstance; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     public static AppDatabase getInstance(Context context) { | ||||
|         if (databaseInstance == null) init(context); | ||||
|         return databaseInstance; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,16 +4,31 @@ import android.arch.persistence.room.Database; | ||||
| import android.arch.persistence.room.RoomDatabase; | ||||
| import android.arch.persistence.room.TypeConverters; | ||||
|  | ||||
| import org.schabi.newpipe.database.history.Converters; | ||||
| import org.schabi.newpipe.database.history.dao.SearchHistoryDAO; | ||||
| import org.schabi.newpipe.database.history.dao.WatchHistoryDAO; | ||||
| import org.schabi.newpipe.database.history.model.SearchHistoryEntry; | ||||
| import org.schabi.newpipe.database.history.model.WatchHistoryEntry; | ||||
| import org.schabi.newpipe.database.playlist.dao.PlaylistDAO; | ||||
| import org.schabi.newpipe.database.playlist.dao.PlaylistStreamDAO; | ||||
| import org.schabi.newpipe.database.stream.dao.StreamHistoryDAO; | ||||
| import org.schabi.newpipe.database.stream.dao.StreamDAO; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistEntity; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.database.subscription.SubscriptionDAO; | ||||
| import org.schabi.newpipe.database.subscription.SubscriptionEntity; | ||||
|  | ||||
| @TypeConverters({Converters.class}) | ||||
| @Database(entities = {SubscriptionEntity.class, WatchHistoryEntry.class, SearchHistoryEntry.class}, version = 1, exportSchema = false) | ||||
| @Database( | ||||
|         entities = { | ||||
|                 SubscriptionEntity.class, WatchHistoryEntry.class, SearchHistoryEntry.class, | ||||
|                 StreamEntity.class, StreamHistoryEntity.class, PlaylistEntity.class, | ||||
|                 PlaylistStreamEntity.class | ||||
|         }, | ||||
|         version = 1, | ||||
|         exportSchema = false | ||||
| ) | ||||
| public abstract class AppDatabase extends RoomDatabase { | ||||
|  | ||||
|     public static final String DATABASE_NAME = "newpipe.db"; | ||||
| @@ -23,4 +38,12 @@ public abstract class AppDatabase extends RoomDatabase { | ||||
|     public abstract WatchHistoryDAO watchHistoryDAO(); | ||||
|  | ||||
|     public abstract SearchHistoryDAO searchHistoryDAO(); | ||||
|  | ||||
|     public abstract StreamDAO streamDAO(); | ||||
|  | ||||
|     public abstract StreamHistoryDAO streamHistoryDAO(); | ||||
|  | ||||
|     public abstract PlaylistDAO playlistDAO(); | ||||
|  | ||||
|     public abstract PlaylistStreamDAO playlistStreamDAO(); | ||||
| } | ||||
|   | ||||
| @@ -23,9 +23,6 @@ public interface BasicDAO<Entity> { | ||||
|     @Insert(onConflict = OnConflictStrategy.FAIL) | ||||
|     List<Long> insertAll(final Collection<Entity> entities); | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.REPLACE) | ||||
|     long upsert(final Entity entity); | ||||
|  | ||||
|     /* Searches */ | ||||
|     Flowable<List<Entity>> getAll(); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| package org.schabi.newpipe.database.history; | ||||
| package org.schabi.newpipe.database; | ||||
| 
 | ||||
| import android.arch.persistence.room.TypeConverter; | ||||
| 
 | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| 
 | ||||
| public class Converters { | ||||
| @@ -25,4 +27,14 @@ public class Converters { | ||||
|     public static Long dateToTimestamp(Date date) { | ||||
|         return date == null ? null : date.getTime(); | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static StreamType streamTypeOf(String value) { | ||||
|         return StreamType.valueOf(value); | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static String stringOf(StreamType streamType) { | ||||
|         return streamType.name(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| package org.schabi.newpipe.database.playlist; | ||||
|  | ||||
| import android.arch.persistence.room.ColumnInfo; | ||||
|  | ||||
| import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem; | ||||
|  | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL; | ||||
|  | ||||
| public class PlaylistMetadataEntry { | ||||
|     final public static String PLAYLIST_STREAM_COUNT = "streamCount"; | ||||
|  | ||||
|     @ColumnInfo(name = PLAYLIST_ID) | ||||
|     final public long uid; | ||||
|     @ColumnInfo(name = PLAYLIST_NAME) | ||||
|     final public String name; | ||||
|     @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) | ||||
|     final public String thumbnailUrl; | ||||
|     @ColumnInfo(name = PLAYLIST_STREAM_COUNT) | ||||
|     final public long streamCount; | ||||
|  | ||||
|     public PlaylistMetadataEntry(long uid, String name, String thumbnailUrl, long streamCount) { | ||||
|         this.uid = uid; | ||||
|         this.name = name; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|         this.streamCount = streamCount; | ||||
|     } | ||||
|  | ||||
|     public LocalPlaylistInfoItem toStoredPlaylistInfoItem() { | ||||
|         LocalPlaylistInfoItem storedPlaylistInfoItem = new LocalPlaylistInfoItem(uid, name); | ||||
|         storedPlaylistInfoItem.setThumbnailUrl(thumbnailUrl); | ||||
|         storedPlaylistInfoItem.setStreamCount(streamCount); | ||||
|         return storedPlaylistInfoItem; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| package org.schabi.newpipe.database.playlist.dao; | ||||
|  | ||||
| import android.arch.persistence.room.Dao; | ||||
| import android.arch.persistence.room.Query; | ||||
| import android.arch.persistence.room.Transaction; | ||||
|  | ||||
| import org.schabi.newpipe.database.BasicDAO; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistEntity; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.Flowable; | ||||
|  | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; | ||||
|  | ||||
| @Dao | ||||
| public abstract class PlaylistDAO implements BasicDAO<PlaylistEntity> { | ||||
|     @Override | ||||
|     @Query("SELECT * FROM " + PLAYLIST_TABLE) | ||||
|     public abstract Flowable<List<PlaylistEntity>> getAll(); | ||||
|  | ||||
|     @Override | ||||
|     @Query("DELETE FROM " + PLAYLIST_TABLE) | ||||
|     public abstract int deleteAll(); | ||||
|  | ||||
|     @Override | ||||
|     public Flowable<List<PlaylistEntity>> listByService(int serviceId) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
|  | ||||
|     @Query("SELECT * FROM " + PLAYLIST_TABLE + " WHERE " + PLAYLIST_ID + " = :playlistId") | ||||
|     public abstract Flowable<List<PlaylistEntity>> getPlaylist(final long playlistId); | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| package org.schabi.newpipe.database.playlist.dao; | ||||
|  | ||||
| import android.arch.persistence.room.Dao; | ||||
| import android.arch.persistence.room.Query; | ||||
| import android.arch.persistence.room.Transaction; | ||||
|  | ||||
| import org.schabi.newpipe.database.BasicDAO; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.Flowable; | ||||
|  | ||||
| import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.*; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.*; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.*; | ||||
|  | ||||
| @Dao | ||||
| public abstract class PlaylistStreamDAO implements BasicDAO<PlaylistStreamEntity> { | ||||
|     @Override | ||||
|     @Query("SELECT * FROM " + PLAYLIST_STREAM_JOIN_TABLE) | ||||
|     public abstract Flowable<List<PlaylistStreamEntity>> getAll(); | ||||
|  | ||||
|     @Override | ||||
|     @Query("DELETE FROM " + PLAYLIST_STREAM_JOIN_TABLE) | ||||
|     public abstract int deleteAll(); | ||||
|  | ||||
|     @Override | ||||
|     public Flowable<List<PlaylistStreamEntity>> listByService(int serviceId) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
|  | ||||
|     @Query("DELETE FROM " + PLAYLIST_STREAM_JOIN_TABLE + | ||||
|             " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId") | ||||
|     public abstract void deleteBatch(final long playlistId); | ||||
|  | ||||
|     @Query("SELECT MAX(" + JOIN_INDEX + ")" + | ||||
|             " FROM " + PLAYLIST_STREAM_JOIN_TABLE + | ||||
|             " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId") | ||||
|     public abstract Flowable<Integer> getMaximumIndexOf(final long playlistId); | ||||
|  | ||||
|     @Transaction | ||||
|     @Query("SELECT " + STREAM_ID + ", " + STREAM_SERVICE_ID + ", " + STREAM_URL + ", " + | ||||
|             STREAM_TITLE + ", " + STREAM_TYPE + ", " + STREAM_UPLOADER + ", " + | ||||
|             STREAM_DURATION + ", " + STREAM_THUMBNAIL_URL + | ||||
|  | ||||
|             " FROM " + STREAM_TABLE + " INNER JOIN " + | ||||
|             // get ids of streams of the given playlist | ||||
|             "(SELECT " + JOIN_STREAM_ID + "," + JOIN_INDEX + | ||||
|             " FROM " + PLAYLIST_STREAM_JOIN_TABLE + " WHERE " | ||||
|             + JOIN_PLAYLIST_ID + " = :playlistId)" + | ||||
|  | ||||
|             // then merge with the stream metadata | ||||
|             " ON " + STREAM_ID + " = " + JOIN_STREAM_ID + | ||||
|             " ORDER BY " + JOIN_INDEX + " ASC") | ||||
|     public abstract Flowable<List<StreamEntity>> getOrderedStreamsOf(long playlistId); | ||||
|  | ||||
|     @Transaction | ||||
|     @Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + | ||||
|             PLAYLIST_THUMBNAIL_URL + ", COUNT(*) AS " + PLAYLIST_STREAM_COUNT + | ||||
|  | ||||
|             " FROM " + PLAYLIST_TABLE + " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE + | ||||
|             " ON " + PLAYLIST_TABLE + "." + PLAYLIST_ID + " = " + PLAYLIST_STREAM_JOIN_TABLE + "." + JOIN_PLAYLIST_ID + | ||||
|             " GROUP BY " + JOIN_PLAYLIST_ID) | ||||
|     public abstract Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata(); | ||||
| } | ||||
| @@ -0,0 +1,59 @@ | ||||
| package org.schabi.newpipe.database.playlist.model; | ||||
|  | ||||
| import android.arch.persistence.room.ColumnInfo; | ||||
| import android.arch.persistence.room.Entity; | ||||
| import android.arch.persistence.room.Index; | ||||
| import android.arch.persistence.room.PrimaryKey; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; | ||||
|  | ||||
| @Entity(tableName = PLAYLIST_TABLE, | ||||
|         indices = {@Index(value = {PLAYLIST_NAME})}) | ||||
| public class PlaylistEntity { | ||||
|     final public static String PLAYLIST_TABLE           = "playlists"; | ||||
|     final public static String PLAYLIST_ID              = "uid"; | ||||
|     final public static String PLAYLIST_NAME            = "name"; | ||||
|     final public static String PLAYLIST_THUMBNAIL_URL   = "thumbnail_url"; | ||||
|  | ||||
|     @PrimaryKey(autoGenerate = true) | ||||
|     @ColumnInfo(name = PLAYLIST_ID) | ||||
|     private long uid = 0; | ||||
|  | ||||
|     @ColumnInfo(name = PLAYLIST_NAME) | ||||
|     private String name; | ||||
|  | ||||
|     @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) | ||||
|     private String thumbnailUrl; | ||||
|  | ||||
|     public PlaylistEntity(String name, String thumbnailUrl) { | ||||
|         this.name = name; | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|     } | ||||
|  | ||||
|     public long getUid() { | ||||
|         return uid; | ||||
|     } | ||||
|  | ||||
|     public void setUid(long uid) { | ||||
|         this.uid = uid; | ||||
|     } | ||||
|  | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     public void setName(String name) { | ||||
|         this.name = name; | ||||
|     } | ||||
|  | ||||
|     public String getThumbnailUrl() { | ||||
|         return thumbnailUrl; | ||||
|     } | ||||
|  | ||||
|     public void setThumbnailUrl(String thumbnailUrl) { | ||||
|         this.thumbnailUrl = thumbnailUrl; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,77 @@ | ||||
| package org.schabi.newpipe.database.playlist.model; | ||||
|  | ||||
| import android.arch.persistence.room.ColumnInfo; | ||||
| import android.arch.persistence.room.Entity; | ||||
| import android.arch.persistence.room.ForeignKey; | ||||
| import android.arch.persistence.room.Index; | ||||
|  | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
|  | ||||
| import static android.arch.persistence.room.ForeignKey.CASCADE; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE; | ||||
|  | ||||
| @Entity(tableName = PLAYLIST_STREAM_JOIN_TABLE, | ||||
|         primaryKeys = {JOIN_PLAYLIST_ID, JOIN_STREAM_ID, JOIN_INDEX}, | ||||
|         indices = { | ||||
|                 @Index(value = {JOIN_PLAYLIST_ID, JOIN_INDEX}, unique = true), | ||||
|                 @Index(value = {JOIN_STREAM_ID}) | ||||
|         }, | ||||
|         foreignKeys = { | ||||
|                 @ForeignKey(entity = PlaylistEntity.class, | ||||
|                         parentColumns = PlaylistEntity.PLAYLIST_ID, | ||||
|                         childColumns = JOIN_PLAYLIST_ID, | ||||
|                         onDelete = CASCADE, onUpdate = CASCADE, deferred = true), | ||||
|                 @ForeignKey(entity = StreamEntity.class, | ||||
|                         parentColumns = StreamEntity.STREAM_ID, | ||||
|                         childColumns = JOIN_STREAM_ID, | ||||
|                         onDelete = CASCADE, onUpdate = CASCADE, deferred = true) | ||||
|         }) | ||||
| public class PlaylistStreamEntity { | ||||
|  | ||||
|     final public static String PLAYLIST_STREAM_JOIN_TABLE   = "playlist_stream_join"; | ||||
|     final public static String JOIN_PLAYLIST_ID             = "playlist_id"; | ||||
|     final public static String JOIN_STREAM_ID               = "stream_id"; | ||||
|     final public static String JOIN_INDEX                   = "join_index"; | ||||
|  | ||||
|     @ColumnInfo(name = JOIN_PLAYLIST_ID) | ||||
|     private long playlistUid; | ||||
|  | ||||
|     @ColumnInfo(name = JOIN_STREAM_ID) | ||||
|     private long streamUid; | ||||
|  | ||||
|     @ColumnInfo(name = JOIN_INDEX) | ||||
|     private int index; | ||||
|  | ||||
|     public PlaylistStreamEntity(final long playlistUid, final long streamUid, final int index) { | ||||
|         this.playlistUid = playlistUid; | ||||
|         this.streamUid = streamUid; | ||||
|         this.index = index; | ||||
|     } | ||||
|  | ||||
|     public long getPlaylistUid() { | ||||
|         return playlistUid; | ||||
|     } | ||||
|  | ||||
|     public long getStreamUid() { | ||||
|         return streamUid; | ||||
|     } | ||||
|  | ||||
|     public int getIndex() { | ||||
|         return index; | ||||
|     } | ||||
|  | ||||
|     public void setPlaylistUid(long playlistUid) { | ||||
|         this.playlistUid = playlistUid; | ||||
|     } | ||||
|  | ||||
|     public void setStreamUid(long streamUid) { | ||||
|         this.streamUid = streamUid; | ||||
|     } | ||||
|  | ||||
|     public void setIndex(int index) { | ||||
|         this.index = index; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| package org.schabi.newpipe.database.stream; | ||||
|  | ||||
| import android.arch.persistence.room.ColumnInfo; | ||||
|  | ||||
| import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class StreamStatisticsEntry { | ||||
|     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; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,57 @@ | ||||
| package org.schabi.newpipe.database.stream.dao; | ||||
|  | ||||
| import android.arch.persistence.room.Dao; | ||||
| import android.arch.persistence.room.Query; | ||||
| import android.arch.persistence.room.Transaction; | ||||
|  | ||||
| import org.schabi.newpipe.database.BasicDAO; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.Flowable; | ||||
|  | ||||
| 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; | ||||
|  | ||||
| @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 + " LIKE :url AND " + | ||||
|             STREAM_SERVICE_ID + " = :serviceId") | ||||
|     public abstract Flowable<List<StreamEntity>> getStream(long serviceId, String url); | ||||
|  | ||||
|     @Query("SELECT * FROM " + STREAM_TABLE + " WHERE " + | ||||
|             STREAM_URL + " LIKE :url AND " + | ||||
|             STREAM_SERVICE_ID + " = :serviceId") | ||||
|     abstract List<StreamEntity> getStreamInternal(long serviceId, String url); | ||||
|  | ||||
|     @Transaction | ||||
|     public long upsert(StreamEntity stream) { | ||||
|         final List<StreamEntity> streams = getStreamInternal(stream.getServiceId(), stream.getUrl()); | ||||
|  | ||||
|         final long uid; | ||||
|         if (streams.isEmpty()) { | ||||
|             uid = insert(stream); | ||||
|         } else { | ||||
|             uid = streams.get(0).getUid(); | ||||
|             stream.setUid(uid); | ||||
|             update(stream); | ||||
|         } | ||||
|         return uid; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| package org.schabi.newpipe.database.stream.dao; | ||||
|  | ||||
|  | ||||
| import android.arch.persistence.room.Dao; | ||||
| import android.arch.persistence.room.Query; | ||||
| import android.arch.persistence.room.Transaction; | ||||
|  | ||||
| import org.schabi.newpipe.database.BasicDAO; | ||||
| import org.schabi.newpipe.database.stream.StreamStatisticsEntry; | ||||
| import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.Flowable; | ||||
|  | ||||
| import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_LATEST_DATE; | ||||
| import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_WATCH_COUNT; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.JOIN_STREAM_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.STREAM_ACCESS_DATE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; | ||||
|  | ||||
| @Dao | ||||
| public abstract class StreamHistoryDAO implements BasicDAO<StreamHistoryEntity> { | ||||
|     @Override | ||||
|     @Query("SELECT * FROM " + STREAM_HISTORY_TABLE) | ||||
|     public abstract Flowable<List<StreamHistoryEntity>> getAll(); | ||||
|  | ||||
|     @Override | ||||
|     @Query("DELETE FROM " + STREAM_HISTORY_TABLE) | ||||
|     public abstract int deleteAll(); | ||||
|  | ||||
|     @Override | ||||
|     public Flowable<List<StreamHistoryEntity>> listByService(int serviceId) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
|  | ||||
|     @Query("DELETE FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId") | ||||
|     public abstract int deleteHistory(final long streamId); | ||||
|  | ||||
|     @Query("SELECT * FROM " + STREAM_TABLE + | ||||
|  | ||||
|             // Select the latest entry and watch count for each stream id on history table | ||||
|             " INNER JOIN " + | ||||
|             "(SELECT " + JOIN_STREAM_ID + ", " + | ||||
|             "  MAX(" + STREAM_ACCESS_DATE + ") AS " + STREAM_LATEST_DATE + ", " + | ||||
|             "  COUNT(*) AS " + STREAM_WATCH_COUNT + | ||||
|             " FROM " + STREAM_HISTORY_TABLE + " GROUP BY " + JOIN_STREAM_ID + ")" + | ||||
|  | ||||
|             " ON " + STREAM_ID + " = " + JOIN_STREAM_ID + | ||||
|             " ORDER BY " + STREAM_ACCESS_DATE + " DESC") | ||||
|     public abstract Flowable<List<StreamStatisticsEntry>> getStatistics(); | ||||
| } | ||||
| @@ -0,0 +1,154 @@ | ||||
| package org.schabi.newpipe.database.stream.model; | ||||
|  | ||||
| import android.arch.persistence.room.ColumnInfo; | ||||
| import android.arch.persistence.room.Entity; | ||||
| import android.arch.persistence.room.Ignore; | ||||
| import android.arch.persistence.room.Index; | ||||
| import android.arch.persistence.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.util.Constants; | ||||
|  | ||||
| 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 { | ||||
|  | ||||
|     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              = "streamType"; | ||||
|     final public static String STREAM_UPLOADER          = "uploader"; | ||||
|     final public static String STREAM_DURATION          = "duration"; | ||||
|     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.service_id, item.name, item.url, item.stream_type, item.thumbnail_url, | ||||
|                 item.uploader_name, item.duration); | ||||
|     } | ||||
|  | ||||
|     @Ignore | ||||
|     public StreamEntity(final StreamInfo info) { | ||||
|         this(info.service_id, info.name, info.url, info.stream_type, info.thumbnail_url, | ||||
|                 info.uploader_name, info.duration); | ||||
|     } | ||||
|  | ||||
|     @Ignore | ||||
|     public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException { | ||||
|         StreamInfoItem item = new StreamInfoItem( | ||||
|                 getServiceId(), getUrl(), getTitle(), getStreamType()); | ||||
|         item.setThumbnailUrl(getThumbnailUrl()); | ||||
|         item.setUploaderName(getUploader()); | ||||
|         item.setDuration(getDuration()); | ||||
|         return item; | ||||
|     } | ||||
|  | ||||
|     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,58 @@ | ||||
| package org.schabi.newpipe.database.stream.model; | ||||
|  | ||||
| import android.arch.persistence.room.ColumnInfo; | ||||
| import android.arch.persistence.room.Entity; | ||||
| import android.arch.persistence.room.ForeignKey; | ||||
| import android.arch.persistence.room.Index; | ||||
| import android.support.annotation.NonNull; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| import static android.arch.persistence.room.ForeignKey.CASCADE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.JOIN_STREAM_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.STREAM_ACCESS_DATE; | ||||
|  | ||||
| @Entity(tableName = STREAM_HISTORY_TABLE, | ||||
|         primaryKeys = {JOIN_STREAM_ID, STREAM_ACCESS_DATE}, | ||||
|         // No need to index for timestamp as they will almost always be unique | ||||
|         indices = {@Index(value = {JOIN_STREAM_ID})}, | ||||
|         foreignKeys = { | ||||
|                 @ForeignKey(entity = StreamEntity.class, | ||||
|                         parentColumns = StreamEntity.STREAM_ID, | ||||
|                         childColumns = JOIN_STREAM_ID, | ||||
|                         onDelete = CASCADE, onUpdate = CASCADE) | ||||
|         }) | ||||
| public class StreamHistoryEntity { | ||||
|     final public static String STREAM_HISTORY_TABLE = "stream_history"; | ||||
|     final public static String JOIN_STREAM_ID       = "stream_id"; | ||||
|     final public static String STREAM_ACCESS_DATE   = "access_date"; | ||||
|  | ||||
|     @ColumnInfo(name = JOIN_STREAM_ID) | ||||
|     private long streamUid; | ||||
|  | ||||
|     @NonNull | ||||
|     @ColumnInfo(name = STREAM_ACCESS_DATE) | ||||
|     private Date accessDate; | ||||
|  | ||||
|     public StreamHistoryEntity(long streamUid, @NonNull Date accessDate) { | ||||
|         this.streamUid = streamUid; | ||||
|         this.accessDate = accessDate; | ||||
|     } | ||||
|  | ||||
|     public long getStreamUid() { | ||||
|         return streamUid; | ||||
|     } | ||||
|  | ||||
|     public void setStreamUid(long streamUid) { | ||||
|         this.streamUid = streamUid; | ||||
|     } | ||||
|  | ||||
|     public Date getAccessDate() { | ||||
|         return accessDate; | ||||
|     } | ||||
|  | ||||
|     public void setAccessDate(@NonNull Date accessDate) { | ||||
|         this.accessDate = accessDate; | ||||
|     } | ||||
| } | ||||
| @@ -50,8 +50,7 @@ public class SubscriptionEntity { | ||||
|         return uid; | ||||
|     } | ||||
|  | ||||
|     /* Keep this package-private since UID should always be auto generated by Room impl */ | ||||
|     void setUid(long uid) { | ||||
|     public void setUid(long uid) { | ||||
|         this.uid = uid; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,78 @@ | ||||
| package org.schabi.newpipe.fragments.playlist; | ||||
|  | ||||
| import org.schabi.newpipe.database.AppDatabase; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||
| import org.schabi.newpipe.database.playlist.dao.PlaylistDAO; | ||||
| import org.schabi.newpipe.database.playlist.dao.PlaylistStreamDAO; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistEntity; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; | ||||
| import org.schabi.newpipe.database.stream.dao.StreamDAO; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.Completable; | ||||
| import io.reactivex.Maybe; | ||||
|  | ||||
| public class LocalPlaylistManager { | ||||
|  | ||||
|     private final AppDatabase database; | ||||
|     private final StreamDAO streamTable; | ||||
|     private final PlaylistDAO playlistTable; | ||||
|     private final PlaylistStreamDAO playlistStreamTable; | ||||
|  | ||||
|     public LocalPlaylistManager(final AppDatabase db) { | ||||
|         database = db; | ||||
|         streamTable = db.streamDAO(); | ||||
|         playlistTable = db.playlistDAO(); | ||||
|         playlistStreamTable = db.playlistStreamDAO(); | ||||
|     } | ||||
|  | ||||
|     public Maybe<List<Long>> createPlaylist(final String name, final List<StreamEntity> streams) { | ||||
|         // Disallow creation of empty playlists until user is able to select thumbnail | ||||
|         if (streams.isEmpty()) return Maybe.empty(); | ||||
|         final StreamEntity defaultStream = streams.get(0); | ||||
|         final PlaylistEntity newPlaylist = new PlaylistEntity(name, defaultStream.getThumbnailUrl()); | ||||
|  | ||||
|         return Maybe.fromCallable(() -> database.runInTransaction(() -> { | ||||
|             final long playlistId = playlistTable.insert(newPlaylist); | ||||
|  | ||||
|             List<PlaylistStreamEntity> joinEntities = new ArrayList<>(streams.size()); | ||||
|             for (int index = 0; index < streams.size(); index++) { | ||||
|                 // Upsert streams and get their ids | ||||
|                 final long streamId = streamTable.upsert(streams.get(index)); | ||||
|                 joinEntities.add(new PlaylistStreamEntity(playlistId, streamId, index)); | ||||
|             } | ||||
|  | ||||
|             return playlistStreamTable.insertAll(joinEntities); | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     public Maybe<Long> appendToPlaylist(final long playlistId, final StreamEntity stream) { | ||||
|         final Maybe<Long> streamIdFuture = Maybe.fromCallable(() -> streamTable.upsert(stream)); | ||||
|         final Maybe<Integer> joinIndexFuture = | ||||
|                 playlistStreamTable.getMaximumIndexOf(playlistId).firstElement(); | ||||
|  | ||||
|         return Maybe.zip(streamIdFuture, joinIndexFuture, (streamId, currentMaxJoinIndex) -> | ||||
|                 playlistStreamTable.insert(new PlaylistStreamEntity(playlistId, | ||||
|                         streamId, currentMaxJoinIndex + 1)) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public Completable updateJoin(final long playlistId, final List<Long> streamIds) { | ||||
|         List<PlaylistStreamEntity> joinEntities = new ArrayList<>(streamIds.size()); | ||||
|         for (int i = 0; i < streamIds.size(); i++) { | ||||
|             joinEntities.add(new PlaylistStreamEntity(playlistId, streamIds.get(i), i)); | ||||
|         } | ||||
|  | ||||
|         return Completable.fromRunnable(() -> database.runInTransaction(() -> { | ||||
|             playlistStreamTable.deleteBatch(playlistId); | ||||
|             playlistStreamTable.insertAll(joinEntities); | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     public Maybe<List<PlaylistMetadataEntry>> getPlaylists() { | ||||
|         return playlistStreamTable.getPlaylistMetadata().firstElement(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,47 @@ | ||||
| package org.schabi.newpipe.fragments.playlist; | ||||
|  | ||||
| import org.schabi.newpipe.database.AppDatabase; | ||||
| import org.schabi.newpipe.database.stream.StreamStatisticsEntry; | ||||
| import org.schabi.newpipe.database.stream.dao.StreamDAO; | ||||
| import org.schabi.newpipe.database.stream.dao.StreamHistoryDAO; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||
|  | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.MaybeObserver; | ||||
| import io.reactivex.Single; | ||||
| import io.reactivex.disposables.Disposable; | ||||
| import io.reactivex.schedulers.Schedulers; | ||||
|  | ||||
| public class StreamRecordManager { | ||||
|  | ||||
|     private final AppDatabase database; | ||||
|     private final StreamDAO streamTable; | ||||
|     private final StreamHistoryDAO historyTable; | ||||
|  | ||||
|     public StreamRecordManager(final AppDatabase db) { | ||||
|         database = db; | ||||
|         streamTable = db.streamDAO(); | ||||
|         historyTable = db.streamHistoryDAO(); | ||||
|     } | ||||
|  | ||||
|     public int onChanged(final StreamInfoItem infoItem) { | ||||
|         // Only existing streams are updated | ||||
|         return streamTable.update(new StreamEntity(infoItem)); | ||||
|     } | ||||
|  | ||||
|     public Single<Long> onViewed(final StreamInfo info) { | ||||
|         return Single.fromCallable(() -> database.runInTransaction(() -> { | ||||
|             final long streamId = streamTable.upsert(new StreamEntity(info)); | ||||
|             return historyTable.insert(new StreamHistoryEntity(streamId, new Date())); | ||||
|         })).subscribeOn(Schedulers.io()); | ||||
|     } | ||||
|  | ||||
|     public int removeHistory(final long streamId) { | ||||
|         return historyTable.deleteHistory(streamId); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,30 @@ | ||||
| package org.schabi.newpipe.info_list.stored; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
|  | ||||
| import static org.schabi.newpipe.util.Constants.NO_SERVICE_ID; | ||||
| import static org.schabi.newpipe.util.Constants.NO_URL; | ||||
|  | ||||
| public class LocalPlaylistInfoItem extends InfoItem { | ||||
|     private final long playlistId; | ||||
|     private long streamCount; | ||||
|  | ||||
|     public LocalPlaylistInfoItem(final long playlistId, final String name) { | ||||
|         super(InfoType.PLAYLIST, NO_SERVICE_ID, NO_URL, name); | ||||
|  | ||||
|         this.playlistId = playlistId; | ||||
|         this.streamCount = streamCount; | ||||
|     } | ||||
|  | ||||
|     public long getPlaylistId() { | ||||
|         return playlistId; | ||||
|     } | ||||
|  | ||||
|     public long getStreamCount() { | ||||
|         return streamCount; | ||||
|     } | ||||
|  | ||||
|     public void setStreamCount(long streamCount) { | ||||
|         this.streamCount = streamCount; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| package org.schabi.newpipe.info_list.stored; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class StreamStatisticsInfoItem extends InfoItem { | ||||
|     private final long streamId; | ||||
|  | ||||
|     private Date latestAccessDate; | ||||
|     private long watchCount; | ||||
|  | ||||
|     public StreamStatisticsInfoItem(final long streamId, final int serviceId, | ||||
|                                     final String url, final String name) { | ||||
|         super(InfoType.STREAM, serviceId, url, name); | ||||
|         this.streamId = streamId; | ||||
|     } | ||||
|  | ||||
|     public long getStreamId() { | ||||
|         return streamId; | ||||
|     } | ||||
|  | ||||
|     public Date getLatestAccessDate() { | ||||
|         return latestAccessDate; | ||||
|     } | ||||
|  | ||||
|     public void setLatestAccessDate(Date latestAccessDate) { | ||||
|         this.latestAccessDate = latestAccessDate; | ||||
|     } | ||||
|  | ||||
|     public long getWatchCount() { | ||||
|         return watchCount; | ||||
|     } | ||||
|  | ||||
|     public void setWatchCount(long watchCount) { | ||||
|         this.watchCount = watchCount; | ||||
|     } | ||||
| } | ||||
| @@ -61,8 +61,10 @@ import com.google.android.exoplayer2.util.Util; | ||||
| import com.nostra13.universalimageloader.core.ImageLoader; | ||||
| import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; | ||||
|  | ||||
| import org.schabi.newpipe.NewPipeDatabase; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.fragments.playlist.StreamRecordManager; | ||||
| import org.schabi.newpipe.player.helper.AudioReactor; | ||||
| import org.schabi.newpipe.player.helper.CacheFactory; | ||||
| import org.schabi.newpipe.player.helper.LoadController; | ||||
| @@ -77,9 +79,9 @@ import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| import io.reactivex.Observable; | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.disposables.CompositeDisposable; | ||||
| import io.reactivex.disposables.Disposable; | ||||
| import io.reactivex.functions.Consumer; | ||||
| import io.reactivex.functions.Predicate; | ||||
| import io.reactivex.schedulers.Schedulers; | ||||
|  | ||||
| import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; | ||||
|  | ||||
| @@ -147,6 +149,9 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen | ||||
|     protected DefaultExtractorsFactory extractorsFactory; | ||||
|  | ||||
|     protected Disposable progressUpdateReactor; | ||||
|     protected CompositeDisposable databaseUpdateReactor; | ||||
|  | ||||
|     protected StreamRecordManager recordManager; | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
| @@ -172,6 +177,12 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen | ||||
|     public void initPlayer() { | ||||
|         if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]"); | ||||
|  | ||||
|         if (recordManager == null) { | ||||
|             recordManager = new StreamRecordManager(NewPipeDatabase.getInstance(context)); | ||||
|         } | ||||
|         if (databaseUpdateReactor != null) databaseUpdateReactor.dispose(); | ||||
|         databaseUpdateReactor = new CompositeDisposable(); | ||||
|  | ||||
|         final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); | ||||
|         final AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); | ||||
|         final LoadControl loadControl = new LoadController(context); | ||||
| @@ -193,18 +204,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen | ||||
|     private Disposable getProgressReactor() { | ||||
|         return Observable.interval(PROGRESS_LOOP_INTERVAL, TimeUnit.MILLISECONDS) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .filter(new Predicate<Long>() { | ||||
|                     @Override | ||||
|                     public boolean test(Long aLong) throws Exception { | ||||
|                         return isProgressLoopRunning(); | ||||
|                     } | ||||
|                 }) | ||||
|                 .subscribe(new Consumer<Long>() { | ||||
|                     @Override | ||||
|                     public void accept(Long aLong) throws Exception { | ||||
|                         triggerProgressUpdate(); | ||||
|                     } | ||||
|                 }); | ||||
|                 .filter(ignored -> isProgressLoopRunning()) | ||||
|                 .subscribe(ignored -> triggerProgressUpdate()); | ||||
|     } | ||||
|  | ||||
|     public void handleIntent(Intent intent) { | ||||
| @@ -281,6 +282,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen | ||||
|         if (playQueue != null) playQueue.dispose(); | ||||
|         if (playbackManager != null) playbackManager.dispose(); | ||||
|         if (audioReactor != null) audioReactor.abandonAudioFocus(); | ||||
|         if (databaseUpdateReactor != null) databaseUpdateReactor.dispose(); | ||||
|     } | ||||
|  | ||||
|     public void destroy() { | ||||
| @@ -291,6 +293,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen | ||||
|  | ||||
|         trackSelector = null; | ||||
|         simpleExoPlayer = null; | ||||
|         recordManager = null; | ||||
|     } | ||||
|  | ||||
|     public MediaSource buildMediaSource(String url, String overrideExtension) { | ||||
| @@ -668,10 +671,13 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen | ||||
|                     "], queue index=[" + playQueue.getIndex() + "]"); | ||||
|         } else if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex || !isPlaying()) { | ||||
|             final long startPos = info != null ? info.start_position : 0; | ||||
|             if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos)); | ||||
|             if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + | ||||
|                     " at: " + getTimeString((int)startPos)); | ||||
|             simpleExoPlayer.seekTo(currentSourceIndex, startPos); | ||||
|         } | ||||
|  | ||||
|         databaseUpdateReactor.add(recordManager.onViewed(currentInfo).subscribe()); | ||||
|         recordManager.removeRecord(); | ||||
|         initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -12,4 +12,5 @@ public class Constants { | ||||
|     public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change"; | ||||
|  | ||||
|     public static final int NO_SERVICE_ID = -1; | ||||
|     public static final String NO_URL = ""; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 John Zhen Mo
					John Zhen Mo