1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-02-24 06:50:07 +00:00

-Added bulk playlist creation and append.

-Added UI to create playlist from service player activity.
-Added state saving to playlist dialogs.
-Removed access to history activity on service player activity.
-Made StreamEntity serializable.
This commit is contained in:
John Zhen Mo 2018-01-21 19:32:49 -08:00
parent 168ac91ab8
commit 776dbc34f7
12 changed files with 182 additions and 101 deletions

View File

@ -19,7 +19,7 @@ public final class NewPipeDatabase {
public static void init(Context context) { public static void init(Context context) {
databaseInstance = Room databaseInstance = Room
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) .databaseBuilder(context, AppDatabase.class, DATABASE_NAME)
.addMigrations(MIGRATION_11_12) .addMigrations(MIGRATION_11_12)
.fallbackToDestructiveMigration() .fallbackToDestructiveMigration()
.build(); .build();

View File

@ -15,7 +15,7 @@ public class Migrations {
/* /*
* Unfortunately these queries must be hardcoded due to the possibility of * Unfortunately these queries must be hardcoded due to the possibility of
* schema and names changing at a later date, thus invalidating the older migration * schema and names changing at a later date, thus invalidating the older migration
* scripts if names are not hardcoded. * scripts if they are not hardcoded.
* */ * */
// Not much we can do about this, since room doesn't create tables before migration. // Not much we can do about this, since room doesn't create tables before migration.

View File

@ -9,15 +9,18 @@ import android.arch.persistence.room.PrimaryKey;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import java.io.Serializable;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_SERVICE_ID; 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_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL;
@Entity(tableName = STREAM_TABLE, @Entity(tableName = STREAM_TABLE,
indices = {@Index(value = {STREAM_SERVICE_ID, STREAM_URL}, unique = true)}) indices = {@Index(value = {STREAM_SERVICE_ID, STREAM_URL}, unique = true)})
public class StreamEntity { public class StreamEntity implements Serializable {
final public static String STREAM_TABLE = "streams"; final public static String STREAM_TABLE = "streams";
final public static String STREAM_ID = "uid"; final public static String STREAM_ID = "uid";
@ -78,6 +81,12 @@ public class StreamEntity {
info.uploader_name, info.duration); info.uploader_name, info.duration);
} }
@Ignore
public StreamEntity(final PlayQueueItem item) {
this(item.getServiceId(), item.getTitle(), item.getUrl(), item.getStreamType(),
item.getThumbnailUrl(), item.getUploader(), item.getDuration());
}
@Ignore @Ignore
public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException { public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException {
StreamInfoItem item = new StreamInfoItem( StreamInfoItem item = new StreamInfoItem(

View File

@ -331,7 +331,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
break; break;
case R.id.detail_controls_playlist_append: case R.id.detail_controls_playlist_append:
if (getFragmentManager() != null && currentInfo != null) { if (getFragmentManager() != null && currentInfo != null) {
PlaylistAppendDialog.newInstance(currentInfo).show(getFragmentManager(), TAG); PlaylistAppendDialog.fromStreamInfo(currentInfo)
.show(getFragmentManager(), TAG);
} }
break; break;
case R.id.detail_uploader_root_layout: case R.id.detail_uploader_root_layout:

View File

@ -33,34 +33,38 @@ public class LocalPlaylistManager {
} }
public Maybe<List<Long>> createPlaylist(final String name, final List<StreamEntity> streams) { public Maybe<List<Long>> createPlaylist(final String name, final List<StreamEntity> streams) {
// Disallow creation of empty playlists until user is able to select thumbnail // Disallow creation of empty playlists
if (streams.isEmpty()) return Maybe.empty(); if (streams.isEmpty()) return Maybe.empty();
final StreamEntity defaultStream = streams.get(0); final StreamEntity defaultStream = streams.get(0);
final PlaylistEntity newPlaylist = new PlaylistEntity(name, defaultStream.getThumbnailUrl()); final PlaylistEntity newPlaylist =
new PlaylistEntity(name, defaultStream.getThumbnailUrl());
return Maybe.fromCallable(() -> database.runInTransaction(() -> { return Maybe.fromCallable(() -> database.runInTransaction(() ->
final long playlistId = playlistTable.insert(newPlaylist); upsertStreams(playlistTable.insert(newPlaylist), streams, 0))
).subscribeOn(Schedulers.io());
}
public Maybe<List<Long>> appendToPlaylist(final long playlistId,
final List<StreamEntity> streams) {
return playlistStreamTable.getMaximumIndexOf(playlistId)
.firstElement()
.map(maxJoinIndex -> database.runInTransaction(() ->
upsertStreams(playlistId, streams, maxJoinIndex + 1))
).subscribeOn(Schedulers.io());
}
private List<Long> upsertStreams(final long playlistId,
final List<StreamEntity> streams,
final int indexOffset) {
List<PlaylistStreamEntity> joinEntities = new ArrayList<>(streams.size()); List<PlaylistStreamEntity> joinEntities = new ArrayList<>(streams.size());
for (int index = 0; index < streams.size(); index++) { for (int index = 0; index < streams.size(); index++) {
// Upsert streams and get their ids // Upsert streams and get their ids
final long streamId = streamTable.upsert(streams.get(index)); final long streamId = streamTable.upsert(streams.get(index));
joinEntities.add(new PlaylistStreamEntity(playlistId, streamId, index)); joinEntities.add(new PlaylistStreamEntity(playlistId, streamId,
index + indexOffset));
} }
return playlistStreamTable.insertAll(joinEntities); return playlistStreamTable.insertAll(joinEntities);
})).subscribeOn(Schedulers.io());
}
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))
).subscribeOn(Schedulers.io());
} }
public Completable updateJoin(final long playlistId, final List<Long> streamIds) { public Completable updateJoin(final long playlistId, final List<Long> streamIds) {

View File

@ -4,7 +4,6 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -19,34 +18,48 @@ import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem; import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem;
import org.schabi.newpipe.playlist.PlayQueueItem;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
public class PlaylistAppendDialog extends DialogFragment { public final class PlaylistAppendDialog extends PlaylistDialog {
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName(); private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
private static final String INFO_KEY = "info_key";
private StreamInfo streamInfo;
private View newPlaylistButton;
private RecyclerView playlistRecyclerView; private RecyclerView playlistRecyclerView;
private InfoListAdapter playlistAdapter; private InfoListAdapter playlistAdapter;
public static PlaylistAppendDialog newInstance(final StreamInfo info) { public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
PlaylistAppendDialog dialog = new PlaylistAppendDialog(); PlaylistAppendDialog dialog = new PlaylistAppendDialog();
dialog.setInfo(info); dialog.setInfo(Collections.singletonList(new StreamEntity(info)));
return dialog; return dialog;
} }
private void setInfo(StreamInfo info) { public static PlaylistAppendDialog fromStreamInfoItems(final List<StreamInfoItem> items) {
this.streamInfo = info; PlaylistAppendDialog dialog = new PlaylistAppendDialog();
List<StreamEntity> entities = new ArrayList<>(items.size());
for (final StreamInfoItem item : items) {
entities.add(new StreamEntity(item));
}
dialog.setInfo(entities);
return dialog;
}
public static PlaylistAppendDialog fromPlayQueueItems(final List<PlayQueueItem> items) {
PlaylistAppendDialog dialog = new PlaylistAppendDialog();
List<StreamEntity> entities = new ArrayList<>(items.size());
for (final PlayQueueItem item : items) {
entities.add(new StreamEntity(item));
}
dialog.setInfo(entities);
return dialog;
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -60,14 +73,9 @@ public class PlaylistAppendDialog extends DialogFragment {
playlistAdapter.useMiniItemVariants(true); playlistAdapter.useMiniItemVariants(true);
} }
@Override /*//////////////////////////////////////////////////////////////////////////
public void onCreate(@Nullable Bundle savedInstanceState) { // Views
super.onCreate(savedInstanceState); //////////////////////////////////////////////////////////////////////////*/
if (savedInstanceState != null) {
Serializable serial = savedInstanceState.getSerializable(INFO_KEY);
if (serial instanceof StreamInfo) streamInfo = (StreamInfo) serial;
}
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
@ -79,7 +87,7 @@ public class PlaylistAppendDialog extends DialogFragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
newPlaylistButton = view.findViewById(R.id.newPlaylist); final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
playlistRecyclerView = view.findViewById(R.id.playlist_list); playlistRecyclerView = view.findViewById(R.id.playlist_list);
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
playlistRecyclerView.setAdapter(playlistAdapter); playlistRecyclerView.setAdapter(playlistAdapter);
@ -92,12 +100,14 @@ public class PlaylistAppendDialog extends DialogFragment {
playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() { playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
@Override @Override
public void selected(PlaylistInfoItem selectedItem) { public void selected(PlaylistInfoItem selectedItem) {
if (!(selectedItem instanceof LocalPlaylistInfoItem)) return; if (!(selectedItem instanceof LocalPlaylistInfoItem) || getStreams() == null)
return;
final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId(); final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId();
final Toast successToast = final Toast successToast =
Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT); Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT);
playlistManager.appendToPlaylist(playlistId, new StreamEntity(streamInfo)) playlistManager.appendToPlaylist(playlistId, getStreams())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> successToast.show()); .subscribe(ignored -> successToast.show());
@ -127,20 +137,14 @@ public class PlaylistAppendDialog extends DialogFragment {
}); });
} }
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(INFO_KEY, streamInfo);
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Helper // Helper
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public void openCreatePlaylistDialog() { public void openCreatePlaylistDialog() {
if (streamInfo == null || getFragmentManager() == null) return; if (getStreams() == null || getFragmentManager() == null) return;
PlaylistCreationDialog.newInstance(streamInfo).show(getFragmentManager(), TAG); PlaylistCreationDialog.newInstance(getStreams()).show(getFragmentManager(), TAG);
getDialog().dismiss(); getDialog().dismiss();
} }
} }

View File

@ -5,63 +5,35 @@ import android.app.Dialog;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import org.schabi.newpipe.MainActivity;
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.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import java.util.Collections;
import java.util.List; import java.util.List;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
public class PlaylistCreationDialog extends DialogFragment { public final class PlaylistCreationDialog extends PlaylistDialog {
private static final String TAG = PlaylistCreationDialog.class.getCanonicalName(); private static final String TAG = PlaylistCreationDialog.class.getCanonicalName();
private static final boolean DEBUG = MainActivity.DEBUG;
private static final String INFO_KEY = "info_key"; public static PlaylistCreationDialog newInstance(final List<StreamEntity> streams) {
private StreamInfo streamInfo;
public static PlaylistCreationDialog newInstance(final StreamInfo info) {
PlaylistCreationDialog dialog = new PlaylistCreationDialog(); PlaylistCreationDialog dialog = new PlaylistCreationDialog();
dialog.setInfo(info); dialog.setInfo(streams);
return dialog; return dialog;
} }
private void setInfo(final StreamInfo info) {
this.streamInfo = info;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// LifeCycle // Dialog
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (streamInfo != null) {
outState.putSerializable(INFO_KEY, streamInfo);
}
}
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null && streamInfo == null) { if (getStreams() == null) return super.onCreateDialog(savedInstanceState);
final Object infoCandidate = savedInstanceState.getSerializable(INFO_KEY);
if (infoCandidate != null && infoCandidate instanceof StreamInfo) {
streamInfo = (StreamInfo) infoCandidate;
}
}
if (streamInfo == null) return super.onCreateDialog(savedInstanceState);
View dialogView = View.inflate(getContext(), View dialogView = View.inflate(getContext(),
R.layout.dialog_create_playlist, null); R.layout.dialog_create_playlist, null);
@ -76,13 +48,11 @@ public class PlaylistCreationDialog extends DialogFragment {
final String name = nameInput.getText().toString(); final String name = nameInput.getText().toString();
final LocalPlaylistManager playlistManager = final LocalPlaylistManager playlistManager =
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext())); new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
final List<StreamEntity> streams =
Collections.singletonList(new StreamEntity(streamInfo));
final Toast successToast = Toast.makeText(getActivity(), final Toast successToast = Toast.makeText(getActivity(),
"Playlist " + name + " successfully created", "Playlist successfully created",
Toast.LENGTH_SHORT); Toast.LENGTH_SHORT);
playlistManager.createPlaylist(name, streams) playlistManager.createPlaylist(name, getStreams())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(longs -> successToast.show()); .subscribe(longs -> successToast.show());
}); });

View File

@ -0,0 +1,73 @@
package org.schabi.newpipe.fragments.local;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.util.StateSaver;
import java.util.List;
import java.util.Queue;
public abstract class PlaylistDialog extends DialogFragment implements StateSaver.WriteRead {
private List<StreamEntity> streamEntities;
private StateSaver.SavedState savedState;
protected void setInfo(final List<StreamEntity> entities) {
this.streamEntities = entities;
}
protected List<StreamEntity> getStreams() {
return streamEntities;
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
savedState = StateSaver.tryToRestore(savedInstanceState, this);
}
@Override
public void onDestroy() {
super.onDestroy();
StateSaver.onDestroy(savedState);
}
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override
public String generateSuffix() {
final int size = streamEntities == null ? 0 : streamEntities.size();
return "." + size + ".list";
}
@Override
public void writeTo(Queue<Object> objectsToSave) {
objectsToSave.add(streamEntities);
}
@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
streamEntities = (List<StreamEntity>) savedObjects.poll();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (getActivity() != null) {
savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(),
savedState, outState, this);
}
}
}

View File

@ -675,6 +675,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
simpleExoPlayer.seekTo(currentSourceIndex, startPos); simpleExoPlayer.seekTo(currentSourceIndex, startPos);
} }
// TODO: update exoplayer to 2.6.x in order to register view count on repeated streams
databaseUpdateReactor.add(recordManager.onViewed(currentInfo).subscribe()); databaseUpdateReactor.add(recordManager.onViewed(currentInfo).subscribe());
initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url); initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url);
} }

View File

@ -29,6 +29,7 @@ import com.google.android.exoplayer2.Player;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.fragments.local.PlaylistAppendDialog;
import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.PlayQueueItemBuilder; import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
@ -149,8 +150,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
case android.R.id.home: case android.R.id.home:
finish(); finish();
return true; return true;
case R.id.action_history: case R.id.action_append_playlist:
NavigationHelper.openHistory(this); appendToPlaylist();
return true; return true;
case R.id.action_settings: case R.id.action_settings:
NavigationHelper.openSettings(this); NavigationHelper.openSettings(this);
@ -185,6 +186,14 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
null null
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} }
private void appendToPlaylist() {
if (this.player != null && this.player.getPlayQueue() != null) {
PlaylistAppendDialog.fromPlayQueueItems(this.player.getPlayQueue().getStreams())
.show(getSupportFragmentManager(), getTag());
}
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Service Connection // Service Connection
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////

View File

@ -5,6 +5,7 @@ import android.support.annotation.Nullable;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
import java.io.Serializable; import java.io.Serializable;
@ -23,6 +24,7 @@ public class PlayQueueItem implements Serializable {
final private long duration; final private long duration;
final private String thumbnailUrl; final private String thumbnailUrl;
final private String uploader; final private String uploader;
final private StreamType streamType;
private long recoveryPosition; private long recoveryPosition;
private Throwable error; private Throwable error;
@ -30,22 +32,26 @@ public class PlayQueueItem implements Serializable {
private transient Single<StreamInfo> stream; private transient Single<StreamInfo> stream;
PlayQueueItem(@NonNull final StreamInfo info) { PlayQueueItem(@NonNull final StreamInfo info) {
this(info.getName(), info.getUrl(), info.getServiceId(), info.duration, info.thumbnail_url, info.uploader_name); this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(),
info.getThumbnailUrl(), info.getUploaderName(), info.getStreamType());
this.stream = Single.just(info); this.stream = Single.just(info);
} }
PlayQueueItem(@NonNull final StreamInfoItem item) { PlayQueueItem(@NonNull final StreamInfoItem item) {
this(item.getName(), item.getUrl(), item.getServiceId(), item.duration, item.thumbnail_url, item.uploader_name); this(item.getName(), item.getUrl(), item.getServiceId(), item.getDuration(),
item.getThumbnailUrl(), item.getUploaderName(), item.getStreamType());
} }
private PlayQueueItem(final String name, final String url, final int serviceId, private PlayQueueItem(final String name, final String url, final int serviceId,
final long duration, final String thumbnailUrl, final String uploader) { final long duration, final String thumbnailUrl, final String uploader,
final StreamType streamType) {
this.title = name; this.title = name;
this.url = url; this.url = url;
this.serviceId = serviceId; this.serviceId = serviceId;
this.duration = duration; this.duration = duration;
this.thumbnailUrl = thumbnailUrl; this.thumbnailUrl = thumbnailUrl;
this.uploader = uploader; this.uploader = uploader;
this.streamType = streamType;
this.recoveryPosition = RECOVERY_UNSET; this.recoveryPosition = RECOVERY_UNSET;
} }
@ -78,6 +84,10 @@ public class PlayQueueItem implements Serializable {
return uploader; return uploader;
} }
public StreamType getStreamType() {
return streamType;
}
public long getRecoveryPosition() { public long getRecoveryPosition() {
return recoveryPosition; return recoveryPosition;
} }

View File

@ -1,11 +1,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="org.schabi.newpipe.history.HistoryActivity"> tools:context=".player.BackgroundPlayerActivity">
<item android:id="@+id/action_history" <item android:id="@+id/action_append_playlist"
android:orderInCategory="981" android:orderInCategory="981"
android:title="@string/action_history" android:title="@string/append_playlist"
app:showAsAction="never"/> app:showAsAction="never"/>
<item android:id="@+id/action_settings" <item android:id="@+id/action_settings"