Skip to content

Playback resume#1968

Closed
Koitharu wants to merge 29 commits intoTeamNewPipe:devfrom
Koitharu:playback_resume
Closed

Playback resume#1968
Koitharu wants to merge 29 commits intoTeamNewPipe:devfrom
Koitharu:playback_resume

Conversation

@Koitharu
Copy link
Contributor

@Koitharu Koitharu commented Dec 27, 2018

Store playing position in history table in database and resume it. Also show current position in VideoDetailFragment.

screenshot_2018-12-27-15-17-12 screenshot_2018-12-27-15-40-35

Closes #1970

Copy link
Contributor

@TobiGr TobiGr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need someone who can help us with code reviews :D Thanks for implementing this. I have no time to review the code now, but I quickly tested your changes.

I think we might need a setting to turn resume on and off / make it clearer that this is done by turning the watch history off. Maybe some additional words are needed in the summary of the watch history setting. Btw. can you please correct "History & Cache" to "Watch history"?

<string name="enable_watch_history_title">History &amp; Cache</string>

What do you think of showing the player interface instead of the toast? When the users sees that the current position is not at the beginning, they should understand that the last position has been restored. At the same time, they are able to hit the double back icon, to go to 0:00.

How do we want to handle exits which are right before the end? Many YouTube videos have these cards/ end-screens where you can select a recommended video to play next. In NewPipe these links are not shown and these end-cards have no value. I can only speak about my workflow, but I guess many other people hit next as soon this last image appears, too.

@Koitharu
Copy link
Contributor Author

Koitharu commented Dec 27, 2018

a setting to turn resume on and off

Yes, I will implement it also

showing the player interface instead of the toast

ok 👌

these cards/ end-screens

This feature needs extractor changes. As far as I understand, it needs some time for implementation. I'll think about it.

@TobiGr
Copy link
Contributor

TobiGr commented Dec 27, 2018

This feature needs extractor changes. As far as I understand, it needs some time for implementation. I'll think about it.

Sorry, you got me wrong :) One possible problem could be the saving of the position when you exit the video right before the end (~10 seconds).
Let's say, I stopped watching a video and left 5 seconds before it was finished. When I want to watch this video again, I click on it and have only 5 seconds to realize that I need to jump to the beginning. Otherwise the next video will start.
My question was about how to handle this problem: we can either say, that a video was finished, although there are some seconds left, or store that the user stopped to watch the video and is going to resume it later.

@Koitharu
Copy link
Contributor Author

Koitharu commented Dec 28, 2018

Ok, good note. Maybe, ignore saved position if left less then 10 seconds

@TobiGr
Copy link
Contributor

TobiGr commented Dec 29, 2018

Just found a little bug: Watch a video for the first time when watch history and resume playback are disabled. The player interface shows the loading spinner the whole time and changing the playback position does not work. I guess the state has not been changed at some point.
screenshot_20181229-154758

@Koitharu
Copy link
Contributor Author

I think, it is done, aren't?)

@TobiGr
Copy link
Contributor

TobiGr commented Dec 30, 2018

I think, it is done, aren't?)

Almost :) When upgrading from 0.14.2 or current dev, I get following crashes at app start:

Exception

  • User Action: ui error
  • Request: App crash, UI failure
  • Content Language: GB
  • Service: none
  • Version: 0.14.2
  • OS: Linux ,release-keys 6.0 - 23
Crash log

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/org.schabi.newpipe.debug/databases/newpipe.db
	at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
	at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1312)
	at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1291)
	at android.arch.persistence.db.framework.FrameworkSQLiteDatabase.query(FrameworkSQLiteDatabase.java:161)
	at android.arch.persistence.db.framework.FrameworkSQLiteDatabase.query(FrameworkSQLiteDatabase.java:150)
	at android.arch.persistence.room.RoomOpenHelper.hasRoomMasterTable(RoomOpenHelper.java:151)
	at android.arch.persistence.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:123)
	at android.arch.persistence.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:115)
	at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:151)
	at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:266)
	at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
	at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:96)
	at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
	at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:233)
	at org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO_Impl$6.call(PlaylistRemoteDAO_Impl.java:274)
	at org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO_Impl$6.call(PlaylistRemoteDAO_Impl.java:271)
	at android.arch.persistence.room.RxRoom$4.apply(RxRoom.java:111)
	at android.arch.persistence.room.RxRoom$4.apply(RxRoom.java:108)
	at io.reactivex.internal.operators.flowable.FlowableMap$MapConditionalSubscriber.tryOnNext(FlowableMap.java:123)
	at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnConditionalSubscriber.runAsync(FlowableObserveOn.java:649)
	at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
	at java.lang.Thread.run(Thread.java:818)


Exception

  • User Action: something
  • Request: Subscriptions
  • Content Language: GB
  • Service: none
  • Version: 0.14.2
  • OS: Linux ,release-keys 6.0 - 23
Crash log

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
	at android.arch.persistence.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:135)
	at android.arch.persistence.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:115)
	at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:151)
	at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:266)
	at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
	at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:96)
	at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
	at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:233)
	at org.schabi.newpipe.database.subscription.SubscriptionDAO_Impl$6.call(SubscriptionDAO_Impl.java:299)
	at org.schabi.newpipe.database.subscription.SubscriptionDAO_Impl$6.call(SubscriptionDAO_Impl.java:296)
	at android.arch.persistence.room.RxRoom$4.apply(RxRoom.java:111)
	at android.arch.persistence.room.RxRoom$4.apply(RxRoom.java:108)
	at io.reactivex.internal.operators.flowable.FlowableMap$MapConditionalSubscriber.tryOnNext(FlowableMap.java:123)
	at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnConditionalSubscriber.runAsync(FlowableObserveOn.java:649)
	at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
	at java.lang.Thread.run(Thread.java:818)


@Koitharu
Copy link
Contributor Author

Hm

@karyogamy
Copy link
Contributor

Hi @nv95. Thank you for you work.

I just want to quickly mention the playback position saving and loading is already available, so there is no need to update the database and create another migration. This is done on a separate StreamState table, so the stream metadata and user-generated data for streams can be kept and updated separately.

@Koitharu
Copy link
Contributor Author

Koitharu commented Jan 1, 2019

It is unused now, and I don't understand how to use StreamState. Besides my solution is simple and ready to use now

@TobiGr
Copy link
Contributor

TobiGr commented Jan 8, 2019

@karyogamy welcome back! It's good to hear from you. Do you have time to review the code further?
@nv95 I agree with @karyogamy that using the StreamState table might be a better approach. Unfortunately, I can't help you with that until the semester is over and my exams written.

@karyogamy @theScrabi It might be good to have a model of our db in the wiki, so devs can work with it easier. What do you think?

@Koitharu
Copy link
Contributor Author

Ok?

@theScrabi
Copy link
Member

@karyogamy @theScrabi It might be good to have a model of our db in the wiki, so devs can work with it easier. What do you think?

Sounds good.

@theScrabi
Copy link
Member

Documented the database. See here:
#2136

The files need to be enroled in a wiki/documentation though.

@TobiGr
Copy link
Contributor

TobiGr commented Mar 7, 2019

Thanks for fixing that. One minor thing is left:

When resuming or re-starting and finishing a video/stream, we should reset the last playback position instead of doing nothing

I think we should do the same with pop-up and background player.

@Koitharu
Copy link
Contributor Author

Koitharu commented Mar 7, 2019

with pop-up and background player

I think it must works in any player

@TobiGr
Copy link
Contributor

TobiGr commented Mar 7, 2019

I tested and it didn't work for some reason. This is what I did:

  1. open a video and play it with the main player
  2. switch to background and remember the current playback position
  3. let the stream finish and wait until the next element in the queue starts to play
  4. open the video detail page of the stream which just finished. Notice the time for playback resume is the same at which we switched to background.

@TobiGr TobiGr mentioned this pull request Mar 10, 2019
1 task
Copy link
Contributor

@TobiGr TobiGr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some problems with the popup player:

  1. start a video in popup mode by clicking on a timestep-link in a video description or comment
  2. Notice the video repeating the first miliseconds again and again until some more of the video stream is buffered.
  3. Let the video play for some seconds
  4. Pause the video. Notice the current playback position is reset back to the point from where we started in 1.

In some cases this problem is even bigger.

  1. start a video in popup mode by clicking on a timestep-link in a video description or comment
  2. start another video in popup mode by clicking on a timestep-link in a video description or comment
  3. let the first video finish or skip it (to skip it, click on the popup notification and then press the control to get to the next video
  4. You'll be in an endless loop of the first frames. The only way the interrupt the loop is pressing the pause button. after pressing play again, the video is played properly.

  1. open a video and play it with the main player
  2. switch to background and remember the current playback position
  3. let the stream finish and wait until the next element in the queue starts to play
  4. open the video detail page of the stream which just finished. Notice the time for playback resume is the same at which we switched to background.

Unfortunately, this is not resolved yet.


We might need to discuss the correct error handling. First of all, we should only log when we have a debug build. Apart from that, we can create a bug report and display it to the user. @theScrabi Can you please take a look at the lines below and tell us how you would handle the errors?

}
},
error -> {
Log.e(TAG, "Player resume failure: ", error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Log.e(TAG, "Player resume failure: ", error);
if (DEBUG) Log.e(TAG, "Player resume failure: ", error);

changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
},
error -> {
Log.e(TAG, "Player resume failure: ", error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Log.e(TAG, "Player resume failure: ", error);
if (DEBUG) Log.e(TAG, "Player resume failure: ", error);

protected void savePlaybackState(final StreamInfo info, final long progress) {
private void savePlaybackState(final StreamInfo info, final long progress) {
if (info == null) return;
Log.d(TAG, "savePlaybackState() called");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Log.d(TAG, "savePlaybackState() called");
if (DEBUG) Log.d(TAG, "savePlaybackState() called");

private void savePlaybackState() {
private void resetPlaybackState(final StreamInfo info) {
if (info == null) return;
Log.d(TAG, "resetPlaybackState() called");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Log.d(TAG, "resetPlaybackState() called");
if (DEBUG) Log.d(TAG, "resetPlaybackState() called");

.onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "resetPlaybackState() failure: ", error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug

@Koitharu
Copy link
Contributor Author

I have some troubles with understanding, where and when is rationally to save playback state. Also I think, that it is undesirable to call savePlaybackState() from main/popup/background player activities or services, aren`t? So, give me some time

@Koitharu
Copy link
Contributor Author

by clicking on a timestep-link

this bug is not related to the saving playback state

@Koitharu
Copy link
Contributor Author

@yausername I found a trouble with CommentTextOnTouchListener.playOnPopup. You remove start position parameter from the url and put info into cache. As a result, after opening a timestamp link we have a StreamInfo with non-zero startPosition in cache, but its key does not contains timestamp information due to
cleanUrl = factory.getUrl(factory.getId(url));
Are we really need this line?

@TobiGr
Copy link
Contributor

TobiGr commented Mar 12, 2019

So, give me some time

Sure. We all want to have the best result in the end. If this takes a bit longer - no problem :)

this bug is not related to the saving playback state

That's good to know. I didn't test the latest dev and compared the bugs of this PR with the dev branch.

@yausername
Copy link
Contributor

cleanUrl = factory.getUrl(factory.getId(url));
Are we really need this line?

@nv95 I wanted to use cached value of streamInfo. If I do not remove the position parameter from url then it fetches streamInfo again from server.
Anyway, a fix is available here #2207
Let me know if this is ok.

@Koitharu
Copy link
Contributor Author

@yausername your patch is not compatible with this pr.

I propose to merge #2207 first, then I will complete this.

@TobiGr TobiGr closed this in #2207 Apr 3, 2019
@yausername
Copy link
Contributor

I think this got closed due to github's automatic trigger. @nv95 @TobiGr please reopen.

@TobiGr
Copy link
Contributor

TobiGr commented Apr 3, 2019

Oh 😅

@TobiGr TobiGr reopened this Apr 3, 2019
@TobiGr
Copy link
Contributor

TobiGr commented Apr 11, 2019

@nv95 I'll do one last round of testing. Is there anything you intend to fix or change or have you completed your work on this?

@Koitharu
Copy link
Contributor Author

@TobiGr Sorry, I have not time just now, but I will do it whatever. Give me some time 👌

@Koitharu Koitharu mentioned this pull request Apr 13, 2019
1 task
@Koitharu Koitharu closed this Apr 13, 2019
@playest
Copy link

playest commented Jul 30, 2019

Any news about when this feature will be implemented?

@Koitharu
Copy link
Contributor Author

@playest already merged in #2288

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request : Resume to last playback position

7 participants