package org.schabi.newpipe.database.stream.model; import androidx.annotation.Nullable; 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; @Entity(tableName = STREAM_STATE_TABLE, primaryKeys = {JOIN_STREAM_ID}, foreignKeys = { @ForeignKey(entity = StreamEntity.class, parentColumns = StreamEntity.STREAM_ID, childColumns = JOIN_STREAM_ID, onDelete = CASCADE, onUpdate = CASCADE) }) public class StreamStateEntity { public static final String STREAM_STATE_TABLE = "stream_state"; public static final String JOIN_STREAM_ID = "stream_id"; // This additional field is required for the SQL query because 'stream_id' is used // for some other joins already public static final String JOIN_STREAM_ID_ALIAS = "stream_id_alias"; public static final String STREAM_PROGRESS_MILLIS = "progress_time"; /** * 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; /** * 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; @ColumnInfo(name = JOIN_STREAM_ID) private long streamUid; @ColumnInfo(name = STREAM_PROGRESS_MILLIS) private long progressMillis; public StreamStateEntity(final long streamUid, final long progressMillis) { this.streamUid = streamUid; this.progressMillis = progressMillis; } public long getStreamUid() { return streamUid; } public void setStreamUid(final long streamUid) { this.streamUid = streamUid; } public long getProgressMillis() { return progressMillis; } public void setProgressMillis(final long progressMillis) { this.progressMillis = progressMillis; } /** * The state will be considered valid, and thus be saved, if the progress is more than {@link * #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(final long durationInSeconds) { return progressMillis > PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS || progressMillis > durationInSeconds * 1000 / 4; } /** * The video will be considered as finished, if the time left is less than {@link * #PLAYBACK_FINISHED_END_MILLISECONDS} and the progress is at least 3/4 of the video length. * 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 */ public boolean isFinished(final long durationInSeconds) { return progressMillis >= durationInSeconds * 1000 - PLAYBACK_FINISHED_END_MILLISECONDS && progressMillis >= durationInSeconds * 1000 * 3 / 4; } @Override public boolean equals(@Nullable final Object obj) { if (obj instanceof StreamStateEntity) { return ((StreamStateEntity) obj).streamUid == streamUid && ((StreamStateEntity) obj).progressMillis == progressMillis; } else { return false; } } @Override public int hashCode() { return Objects.hash(streamUid, progressMillis); } }