-Added state saving for streams on skip and player exception events.
-Added query for loading saved stream states. -Modified orphan record removal to no longer consider stream table records.
This commit is contained in:
parent
9b4a07de34
commit
d3160eed9d
|
@ -92,10 +92,6 @@ public abstract class StreamDAO implements BasicDAO<StreamEntity> {
|
||||||
" ON " + STREAM_ID + " = " +
|
" ON " + STREAM_ID + " = " +
|
||||||
StreamHistoryEntity.STREAM_HISTORY_TABLE + "." + StreamHistoryEntity.JOIN_STREAM_ID +
|
StreamHistoryEntity.STREAM_HISTORY_TABLE + "." + StreamHistoryEntity.JOIN_STREAM_ID +
|
||||||
|
|
||||||
" LEFT JOIN " + STREAM_STATE_TABLE +
|
|
||||||
" ON " + STREAM_ID + " = " +
|
|
||||||
StreamStateEntity.STREAM_STATE_TABLE + "." + StreamStateEntity.JOIN_STREAM_ID +
|
|
||||||
|
|
||||||
" LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE +
|
" LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE +
|
||||||
" ON " + STREAM_ID + " = " +
|
" ON " + STREAM_ID + " = " +
|
||||||
PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE + "." + PlaylistStreamEntity.JOIN_STREAM_ID +
|
PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE + "." + PlaylistStreamEntity.JOIN_STREAM_ID +
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package org.schabi.newpipe.database.stream.dao;
|
package org.schabi.newpipe.database.stream.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import android.arch.persistence.room.Dao;
|
||||||
|
import android.arch.persistence.room.Insert;
|
||||||
|
import android.arch.persistence.room.OnConflictStrategy;
|
||||||
import android.arch.persistence.room.Query;
|
import android.arch.persistence.room.Query;
|
||||||
|
import android.arch.persistence.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
|
@ -28,6 +31,18 @@ public abstract class StreamStateDAO implements BasicDAO<StreamStateEntity> {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Query("SELECT * FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
||||||
|
public abstract Flowable<List<StreamStateEntity>> getState(final long streamId);
|
||||||
|
|
||||||
@Query("DELETE FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
@Query("DELETE FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
||||||
public abstract int deleteState(final long streamId);
|
public abstract int deleteState(final long streamId);
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
abstract void silentInsertInternal(final StreamStateEntity streamState);
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public long upsert(StreamStateEntity stream) {
|
||||||
|
silentInsertInternal(stream);
|
||||||
|
return update(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ public abstract class HistoryFragment<E> extends BaseFragment
|
||||||
.setMessage(R.string.delete_all_history_prompt)
|
.setMessage(R.string.delete_all_history_prompt)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setPositiveButton(R.string.delete, (dialog, i) -> clearHistory())
|
.setPositiveButton(R.string.delete_all, (dialog, i) -> clearHistory())
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,25 @@ package org.schabi.newpipe.history;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.AppDatabase;
|
import org.schabi.newpipe.database.AppDatabase;
|
||||||
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
||||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
|
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
||||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||||
|
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
||||||
|
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
|
||||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||||
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
||||||
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -34,6 +36,7 @@ public class HistoryRecordManager {
|
||||||
private final StreamDAO streamTable;
|
private final StreamDAO streamTable;
|
||||||
private final StreamHistoryDAO streamHistoryTable;
|
private final StreamHistoryDAO streamHistoryTable;
|
||||||
private final SearchHistoryDAO searchHistoryTable;
|
private final SearchHistoryDAO searchHistoryTable;
|
||||||
|
private final StreamStateDAO streamStateTable;
|
||||||
private final SharedPreferences sharedPreferences;
|
private final SharedPreferences sharedPreferences;
|
||||||
private final String searchHistoryKey;
|
private final String searchHistoryKey;
|
||||||
private final String streamHistoryKey;
|
private final String streamHistoryKey;
|
||||||
|
@ -43,15 +46,12 @@ public class HistoryRecordManager {
|
||||||
streamTable = database.streamDAO();
|
streamTable = database.streamDAO();
|
||||||
streamHistoryTable = database.streamHistoryDAO();
|
streamHistoryTable = database.streamHistoryDAO();
|
||||||
searchHistoryTable = database.searchHistoryDAO();
|
searchHistoryTable = database.searchHistoryDAO();
|
||||||
|
streamStateTable = database.streamStateDAO();
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
searchHistoryKey = context.getString(R.string.enable_search_history_key);
|
searchHistoryKey = context.getString(R.string.enable_search_history_key);
|
||||||
streamHistoryKey = context.getString(R.string.enable_watch_history_key);
|
streamHistoryKey = context.getString(R.string.enable_watch_history_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> removeOrphanedRecords() {
|
|
||||||
return Single.fromCallable(streamTable::deleteOrphans).subscribeOn(Schedulers.io());
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// Watch History
|
// Watch History
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
@ -161,4 +161,31 @@ public class HistoryRecordManager {
|
||||||
private boolean isSearchHistoryEnabled() {
|
private boolean isSearchHistoryEnabled() {
|
||||||
return sharedPreferences.getBoolean(searchHistoryKey, false);
|
return sharedPreferences.getBoolean(searchHistoryKey, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// Stream State History
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) {
|
||||||
|
return Maybe.fromCallable(() -> streamTable.upsert(new StreamEntity(info)))
|
||||||
|
.flatMap(streamId -> streamStateTable.getState(streamId).firstElement())
|
||||||
|
.flatMap(states -> states.isEmpty() ? Maybe.empty() : Maybe.just(states.get(0)))
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Maybe<Long> saveStreamState(@NonNull final StreamInfo info, final long progressTime) {
|
||||||
|
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
|
||||||
|
final long streamId = streamTable.upsert(new StreamEntity(info));
|
||||||
|
return streamStateTable.upsert(new StreamStateEntity(streamId, progressTime));
|
||||||
|
})).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// Utility
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Single<Integer> removeOrphanedRecords() {
|
||||||
|
return Single.fromCallable(streamTable::deleteOrphans).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -581,6 +581,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
errorToast = null;
|
errorToast = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
savePlaybackState();
|
||||||
|
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case ExoPlaybackException.TYPE_SOURCE:
|
case ExoPlaybackException.TYPE_SOURCE:
|
||||||
if (simpleExoPlayer.getCurrentPosition() <
|
if (simpleExoPlayer.getCurrentPosition() <
|
||||||
|
@ -758,6 +760,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
if (simpleExoPlayer == null || playQueue == null) return;
|
if (simpleExoPlayer == null || playQueue == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "onPlayPrevious() called");
|
if (DEBUG) Log.d(TAG, "onPlayPrevious() called");
|
||||||
|
|
||||||
|
savePlaybackState();
|
||||||
|
|
||||||
/* If current playback has run for PLAY_PREV_ACTIVATION_LIMIT milliseconds, restart current track.
|
/* If current playback has run for PLAY_PREV_ACTIVATION_LIMIT milliseconds, restart current track.
|
||||||
* Also restart the track if the current track is the first in a queue.*/
|
* Also restart the track if the current track is the first in a queue.*/
|
||||||
if (simpleExoPlayer.getCurrentPosition() > PLAY_PREV_ACTIVATION_LIMIT || playQueue.getIndex() == 0) {
|
if (simpleExoPlayer.getCurrentPosition() > PLAY_PREV_ACTIVATION_LIMIT || playQueue.getIndex() == 0) {
|
||||||
|
@ -772,6 +776,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
if (playQueue == null) return;
|
if (playQueue == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "onPlayNext() called");
|
if (DEBUG) Log.d(TAG, "onPlayNext() called");
|
||||||
|
|
||||||
|
savePlaybackState();
|
||||||
|
|
||||||
playQueue.offsetIndex(+1);
|
playQueue.offsetIndex(+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,6 +839,24 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void savePlaybackState(final StreamInfo info, final long progress) {
|
||||||
|
if (context == null || info == null || databaseUpdateReactor == null) return;
|
||||||
|
final Disposable stateSaver = recordManager.saveStreamState(info, progress)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.onErrorComplete()
|
||||||
|
.subscribe();
|
||||||
|
databaseUpdateReactor.add(stateSaver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePlaybackState() {
|
||||||
|
if (simpleExoPlayer == null || currentInfo == null) return;
|
||||||
|
|
||||||
|
if (simpleExoPlayer.getCurrentPosition() > RECOVERY_SKIP_THRESHOLD &&
|
||||||
|
simpleExoPlayer.getCurrentPosition() <
|
||||||
|
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD) {
|
||||||
|
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Getters and Setters
|
// Getters and Setters
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
Loading…
Reference in New Issue