mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-06-26 15:13:00 +00:00
parent
c6e1721884
commit
2339f51ad4
@ -2,6 +2,9 @@ package org.schabi.newpipe.local.playlist;
|
|||||||
|
|
||||||
import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar;
|
import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar;
|
||||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||||
|
import static org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS;
|
||||||
|
import static org.schabi.newpipe.local.playlist.PlayListShareMode.WITH_TITLES;
|
||||||
|
import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST;
|
||||||
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
|
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -64,12 +67,14 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void>
|
public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void>
|
||||||
implements PlaylistControlViewHolder, DebounceSavable {
|
implements PlaylistControlViewHolder, DebounceSavable {
|
||||||
@ -385,34 +390,76 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shares the playlist as a list of stream URLs if {@code shouldSharePlaylistDetails} is
|
* FIXME update this
|
||||||
* set to {@code false}. Shares the playlist name along with a list of video titles and URLs
|
|
||||||
* if {@code shouldSharePlaylistDetails} is set to {@code true}.
|
|
||||||
*
|
*
|
||||||
* @param shouldSharePlaylistDetails Whether the playlist details should be included in the
|
* Shares the playlist as a list of stream URLs if {@code shareMode} is
|
||||||
* shared content.
|
* set to {@code false}. Shares the playlist name along with a list of video titles and URLs
|
||||||
|
* if {@code shareMode} is set to {@code true}.
|
||||||
|
*
|
||||||
|
* @param shareMode Whether the playlist details should be included in the
|
||||||
|
* shared content.
|
||||||
*/
|
*/
|
||||||
private void sharePlaylist(final boolean shouldSharePlaylistDetails) {
|
private void sharePlaylist(PlayListShareMode shareMode) {
|
||||||
final Context context = requireContext();
|
final Context context = requireContext();
|
||||||
|
|
||||||
disposables.add(playlistManager.getPlaylistStreams(playlistId)
|
disposables.add(playlistManager.getPlaylistStreams(playlistId)
|
||||||
.flatMapSingle(playlist -> Single.just(playlist.stream()
|
.flatMapSingle(playlist -> Single.just(export( shareMode
|
||||||
.map(PlaylistStreamEntry::getStreamEntity)
|
, playlist.stream().map(PlaylistStreamEntry::getStreamEntity)
|
||||||
.map(streamEntity -> {
|
, context
|
||||||
if (shouldSharePlaylistDetails) {
|
)
|
||||||
return context.getString(R.string.video_details_list_item,
|
))
|
||||||
streamEntity.getTitle(), streamEntity.getUrl());
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
} else {
|
.subscribe( urlsText -> ShareUtils.shareText( context
|
||||||
return streamEntity.getUrl();
|
, name
|
||||||
}
|
, shareMode == JUST_URLS ? urlsText
|
||||||
})
|
: context.getString(R.string.share_playlist_content_details, name, urlsText))
|
||||||
.collect(Collectors.joining("\n"))))
|
, throwable -> showUiErrorSnackbar(this, "Sharing playlist", throwable))
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
);
|
||||||
.subscribe(urlsText -> ShareUtils.shareText(
|
}
|
||||||
context, name, shouldSharePlaylistDetails
|
|
||||||
? context.getString(R.string.share_playlist_content_details,
|
static String export(PlayListShareMode shareMode, Stream<StreamEntity> entityStream, Context context) {
|
||||||
name, urlsText) : urlsText),
|
|
||||||
throwable -> showUiErrorSnackbar(this, "Sharing playlist", throwable)));
|
return switch(shareMode) {
|
||||||
|
|
||||||
|
case WITH_TITLES -> exportWithTitles(entityStream, context);
|
||||||
|
case JUST_URLS -> exportJustUrls(entityStream);
|
||||||
|
case YOUTUBE_TEMP_PLAYLIST -> exportAsYoutubeTempPlaylist(entityStream);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static String exportWithTitles(Stream<StreamEntity> entityStream, Context context) {
|
||||||
|
|
||||||
|
return entityStream
|
||||||
|
.map(entity -> context.getString(R.string.video_details_list_item, entity.getTitle(), entity.getUrl()))
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String exportJustUrls(Stream<StreamEntity> entityStream) {
|
||||||
|
|
||||||
|
return entityStream
|
||||||
|
.map(StreamEntity::getUrl)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String exportAsYoutubeTempPlaylist(Stream<StreamEntity> entityStream) {
|
||||||
|
|
||||||
|
String videoIDs = entityStream
|
||||||
|
.map(entity -> getYouTubeId(entity.getUrl()))
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
|
||||||
|
return "http://www.youtube.com/watch_videos?video_ids=" + videoIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the video id from a YouTube URL
|
||||||
|
*/
|
||||||
|
static String getYouTubeId(String url) {
|
||||||
|
|
||||||
|
HttpUrl httpUrl = HttpUrl.parse(url);
|
||||||
|
|
||||||
|
return httpUrl == null ? null
|
||||||
|
: httpUrl.queryParameter("v")
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeWatchedStreams(final boolean removePartiallyWatched) {
|
public void removeWatchedStreams(final boolean removePartiallyWatched) {
|
||||||
@ -875,10 +922,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||||||
.setMessage(R.string.share_playlist_with_titles_message)
|
.setMessage(R.string.share_playlist_with_titles_message)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setPositiveButton(R.string.share_playlist_with_titles, (dialog, which) ->
|
.setPositiveButton(R.string.share_playlist_with_titles, (dialog, which) ->
|
||||||
sharePlaylist(/* shouldSharePlaylistDetails= */ true)
|
sharePlaylist(WITH_TITLES)
|
||||||
|
)
|
||||||
|
.setNeutralButton("Share as YouTube temporary playlist", (dialog, which) -> // TODO R.string.share_playlist_as_YouTube_temporary_playlist
|
||||||
|
sharePlaylist(YOUTUBE_TEMP_PLAYLIST)
|
||||||
)
|
)
|
||||||
.setNegativeButton(R.string.share_playlist_with_list, (dialog, which) ->
|
.setNegativeButton(R.string.share_playlist_with_list, (dialog, which) ->
|
||||||
sharePlaylist(/* shouldSharePlaylistDetails= */ false)
|
sharePlaylist(JUST_URLS)
|
||||||
)
|
)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package org.schabi.newpipe.local.playlist;
|
||||||
|
|
||||||
|
public enum PlayListShareMode {
|
||||||
|
|
||||||
|
JUST_URLS
|
||||||
|
,WITH_TITLES
|
||||||
|
,YOUTUBE_TEMP_PLAYLIST
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package org.schabi.newpipe.local.playlist;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class LocalPlaylistFragmentTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void youTubeTempPlaylist() {
|
||||||
|
|
||||||
|
Stream<StreamEntity> entityStream = List.of(
|
||||||
|
|
||||||
|
"https://www.youtube.com/watch?v=1"
|
||||||
|
,"https://www.youtube.com/watch?v=2"
|
||||||
|
,"https://www.youtube.com/watch?v=3"
|
||||||
|
)
|
||||||
|
.stream()
|
||||||
|
.map(LocalPlaylistFragmentTest::newStreamEntity)
|
||||||
|
;
|
||||||
|
|
||||||
|
String url = LocalPlaylistFragment.export(YOUTUBE_TEMP_PLAYLIST, entityStream, null);
|
||||||
|
|
||||||
|
Assert.assertEquals("http://www.youtube.com/watch_videos?video_ids=1,2,3", url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
static StreamEntity newStreamEntity(String url) {
|
||||||
|
|
||||||
|
return new StreamEntity(
|
||||||
|
|
||||||
|
0
|
||||||
|
, 1
|
||||||
|
, url
|
||||||
|
, "Title"
|
||||||
|
, StreamType.VIDEO_STREAM
|
||||||
|
, 100
|
||||||
|
, "Uploader"
|
||||||
|
, null
|
||||||
|
, null
|
||||||
|
, null
|
||||||
|
, null
|
||||||
|
, null
|
||||||
|
, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user