From 2142f05a88f2848586debcf1501cb71dd4abc522 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 7 Jun 2021 09:43:08 +0200 Subject: [PATCH] Fix hiding finished streams in groups; new stream state validity condition Consider stream state valid also if >1/4 of video was watched --- .../newpipe/database/feed/dao/FeedDAO.kt | 16 ++++++++++++ .../stream/model/StreamStateEntity.java | 26 ++++++++++++++----- .../local/history/HistoryRecordManager.java | 8 +++--- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index 769c72832..689f1ead6 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -64,6 +64,12 @@ abstract class FeedDAO { ) abstract fun getAllStreamsForGroup(groupId: Long): Flowable> + /** + * @see StreamStateEntity.isFinished() + * @see StreamStateEntity.PLAYBACK_FINISHED_END_MILLISECONDS + * @return all of the non-live, never-played and non-finished streams in the feed + * (all of the cited conditions must hold for a stream to be in the returned list) + */ @Query( """ SELECT s.*, sst.progress_time @@ -93,6 +99,13 @@ abstract class FeedDAO { ) abstract fun getLiveOrNotPlayedStreams(): Flowable> + /** + * @see StreamStateEntity.isFinished() + * @see StreamStateEntity.PLAYBACK_FINISHED_END_MILLISECONDS + * @param groupId the group id to get streams of + * @return all of the non-live, never-played and non-finished streams for the given feed group + * (all of the cited conditions must hold for a stream to be in the returned list) + */ @Query( """ SELECT s.*, sst.progress_time @@ -113,6 +126,9 @@ abstract class FeedDAO { WHERE fgs.group_id = :groupId AND ( sh.stream_id IS NULL + OR sst.stream_id IS NULL + OR sst.progress_time < s.duration * 1000 - ${StreamStateEntity.PLAYBACK_FINISHED_END_MILLISECONDS} + OR sst.progress_time < s.duration * 1000 * 3 / 4 OR s.stream_type = 'LIVE_STREAM' OR s.stream_type = 'AUDIO_LIVE_STREAM' ) diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java index 63a21fa9a..75766850f 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java @@ -5,6 +5,8 @@ import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.ForeignKey; +import java.util.Objects; + import static androidx.room.ForeignKey.CASCADE; import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID; import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; @@ -26,15 +28,18 @@ public class StreamStateEntity { public static final String STREAM_PROGRESS_MILLIS = "progress_time"; /** - * Playback state will not be saved, if playback time is less than this threshold. + * Playback state will not be saved, if playback time is less than this threshold (5000ms = 5s). */ - private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000; // 5000ms = 5s + private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000; /** + * Stream will be considered finished if the playback time left exceeds this threshold + * (60000ms = 60s). * @see #isFinished(long) * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams() + * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long) */ - public static final long PLAYBACK_FINISHED_END_MILLISECONDS = 60000; // 60000ms = 60s + public static final long PLAYBACK_FINISHED_END_MILLISECONDS = 60000; @ColumnInfo(name = JOIN_STREAM_ID) private long streamUid; @@ -65,11 +70,13 @@ public class StreamStateEntity { /** * The state will be considered valid, and thus be saved, if the progress is more than {@link - * #PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS}. + * #PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS} or at least 1/4 of the video length. + * @param durationInSeconds the duration of the stream connected with this state, in seconds * @return whether this stream state entity should be saved or not */ - public boolean isValid() { - return progressMillis > PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS; + public boolean isValid(final long durationInSeconds) { + return progressMillis > PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS + || progressMillis > durationInSeconds * 1000 / 4; } /** @@ -78,6 +85,8 @@ public class StreamStateEntity { * The state will be saved anyway, so that it can be shown under stream info items, but the * player will not resume if a state is considered as finished. Finished streams are also the * ones that can be filtered out in the feed fragment. + * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams() + * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long) * @param durationInSeconds the duration of the stream connected with this state, in seconds * @return whether the stream is finished or not */ @@ -95,4 +104,9 @@ public class StreamStateEntity { return false; } } + + @Override + public int hashCode() { + return Objects.hash(streamUid, progressMillis); + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index d2ffbfc27..38ebe504e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -211,11 +211,11 @@ public class HistoryRecordManager { public Maybe loadStreamState(final PlayQueueItem queueItem) { return queueItem.getStream() - .map((info) -> streamTable.upsert(new StreamEntity(info))) + .map(info -> streamTable.upsert(new StreamEntity(info))) .flatMapPublisher(streamStateTable::getState) .firstElement() .flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0))) - .filter(StreamStateEntity::isValid) + .filter(state -> state.isValid(queueItem.getDuration())) .subscribeOn(Schedulers.io()); } @@ -224,7 +224,7 @@ public class HistoryRecordManager { .flatMapPublisher(streamStateTable::getState) .firstElement() .flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0))) - .filter(StreamStateEntity::isValid) + .filter(state -> state.isValid(info.getDuration())) .subscribeOn(Schedulers.io()); } @@ -232,7 +232,7 @@ public class HistoryRecordManager { return Completable.fromAction(() -> database.runInTransaction(() -> { final long streamId = streamTable.upsert(new StreamEntity(info)); final StreamStateEntity state = new StreamStateEntity(streamId, progressMillis); - if (state.isValid()) { + if (state.isValid(info.getDuration())) { streamStateTable.upsert(state); } })).subscribeOn(Schedulers.io());