Skip to content

Commit 2339f51

Browse files
committed
[#11930] Share as YouTube temporary playlist
Initial commit.
1 parent c6e1721 commit 2339f51

3 files changed

Lines changed: 137 additions & 24 deletions

File tree

app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar;
44
import static org.schabi.newpipe.ktx.ViewUtils.animate;
5+
import static org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS;
6+
import static org.schabi.newpipe.local.playlist.PlayListShareMode.WITH_TITLES;
7+
import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST;
58
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
69

710
import android.content.Context;
@@ -64,12 +67,14 @@
6467
import java.util.List;
6568
import java.util.concurrent.atomic.AtomicBoolean;
6669
import java.util.stream.Collectors;
70+
import java.util.stream.Stream;
6771

6872
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
6973
import io.reactivex.rxjava3.core.Single;
7074
import io.reactivex.rxjava3.disposables.CompositeDisposable;
7175
import io.reactivex.rxjava3.disposables.Disposable;
7276
import io.reactivex.rxjava3.schedulers.Schedulers;
77+
import okhttp3.HttpUrl;
7378

7479
public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void>
7580
implements PlaylistControlViewHolder, DebounceSavable {
@@ -385,34 +390,76 @@ public boolean onOptionsItemSelected(final MenuItem item) {
385390
}
386391

387392
/**
388-
* Shares the playlist as a list of stream URLs if {@code shouldSharePlaylistDetails} is
393+
* FIXME update this
394+
*
395+
* Shares the playlist as a list of stream URLs if {@code shareMode} is
389396
* set to {@code false}. Shares the playlist name along with a list of video titles and URLs
390-
* if {@code shouldSharePlaylistDetails} is set to {@code true}.
397+
* if {@code shareMode} is set to {@code true}.
391398
*
392-
* @param shouldSharePlaylistDetails Whether the playlist details should be included in the
393-
* shared content.
399+
* @param shareMode Whether the playlist details should be included in the
400+
* shared content.
394401
*/
395-
private void sharePlaylist(final boolean shouldSharePlaylistDetails) {
402+
private void sharePlaylist(PlayListShareMode shareMode) {
396403
final Context context = requireContext();
397404

398405
disposables.add(playlistManager.getPlaylistStreams(playlistId)
399-
.flatMapSingle(playlist -> Single.just(playlist.stream()
400-
.map(PlaylistStreamEntry::getStreamEntity)
401-
.map(streamEntity -> {
402-
if (shouldSharePlaylistDetails) {
403-
return context.getString(R.string.video_details_list_item,
404-
streamEntity.getTitle(), streamEntity.getUrl());
405-
} else {
406-
return streamEntity.getUrl();
407-
}
408-
})
409-
.collect(Collectors.joining("\n"))))
410-
.observeOn(AndroidSchedulers.mainThread())
411-
.subscribe(urlsText -> ShareUtils.shareText(
412-
context, name, shouldSharePlaylistDetails
413-
? context.getString(R.string.share_playlist_content_details,
414-
name, urlsText) : urlsText),
415-
throwable -> showUiErrorSnackbar(this, "Sharing playlist", throwable)));
406+
.flatMapSingle(playlist -> Single.just(export( shareMode
407+
, playlist.stream().map(PlaylistStreamEntry::getStreamEntity)
408+
, context
409+
)
410+
))
411+
.observeOn(AndroidSchedulers.mainThread())
412+
.subscribe( urlsText -> ShareUtils.shareText( context
413+
, name
414+
, shareMode == JUST_URLS ? urlsText
415+
: context.getString(R.string.share_playlist_content_details, name, urlsText))
416+
, throwable -> showUiErrorSnackbar(this, "Sharing playlist", throwable))
417+
);
418+
}
419+
420+
static String export(PlayListShareMode shareMode, Stream<StreamEntity> entityStream, Context context) {
421+
422+
return switch(shareMode) {
423+
424+
case WITH_TITLES -> exportWithTitles(entityStream, context);
425+
case JUST_URLS -> exportJustUrls(entityStream);
426+
case YOUTUBE_TEMP_PLAYLIST -> exportAsYoutubeTempPlaylist(entityStream);
427+
};
428+
}
429+
430+
static String exportWithTitles(Stream<StreamEntity> entityStream, Context context) {
431+
432+
return entityStream
433+
.map(entity -> context.getString(R.string.video_details_list_item, entity.getTitle(), entity.getUrl()))
434+
.collect(Collectors.joining("\n"));
435+
}
436+
437+
static String exportJustUrls(Stream<StreamEntity> entityStream) {
438+
439+
return entityStream
440+
.map(StreamEntity::getUrl)
441+
.collect(Collectors.joining("\n"));
442+
}
443+
444+
static String exportAsYoutubeTempPlaylist(Stream<StreamEntity> entityStream) {
445+
446+
String videoIDs = entityStream
447+
.map(entity -> getYouTubeId(entity.getUrl()))
448+
.collect(Collectors.joining(","));
449+
450+
return "http://www.youtube.com/watch_videos?video_ids=" + videoIDs;
451+
}
452+
453+
/**
454+
* Gets the video id from a YouTube URL
455+
*/
456+
static String getYouTubeId(String url) {
457+
458+
HttpUrl httpUrl = HttpUrl.parse(url);
459+
460+
return httpUrl == null ? null
461+
: httpUrl.queryParameter("v")
462+
;
416463
}
417464

418465
public void removeWatchedStreams(final boolean removePartiallyWatched) {
@@ -875,10 +922,13 @@ private void createShareConfirmationDialog() {
875922
.setMessage(R.string.share_playlist_with_titles_message)
876923
.setCancelable(true)
877924
.setPositiveButton(R.string.share_playlist_with_titles, (dialog, which) ->
878-
sharePlaylist(/* shouldSharePlaylistDetails= */ true)
925+
sharePlaylist(WITH_TITLES)
926+
)
927+
.setNeutralButton("Share as YouTube temporary playlist", (dialog, which) -> // TODO R.string.share_playlist_as_YouTube_temporary_playlist
928+
sharePlaylist(YOUTUBE_TEMP_PLAYLIST)
879929
)
880930
.setNegativeButton(R.string.share_playlist_with_list, (dialog, which) ->
881-
sharePlaylist(/* shouldSharePlaylistDetails= */ false)
931+
sharePlaylist(JUST_URLS)
882932
)
883933
.show();
884934
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.schabi.newpipe.local.playlist;
2+
3+
public enum PlayListShareMode {
4+
5+
JUST_URLS
6+
,WITH_TITLES
7+
,YOUTUBE_TEMP_PLAYLIST
8+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.schabi.newpipe.local.playlist;
2+
3+
import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST;
4+
5+
import androidx.annotation.NonNull;
6+
7+
import org.junit.Assert;
8+
import org.junit.Test;
9+
import org.schabi.newpipe.database.stream.model.StreamEntity;
10+
import org.schabi.newpipe.extractor.stream.StreamType;
11+
12+
import java.util.List;
13+
import java.util.stream.Stream;
14+
15+
public class LocalPlaylistFragmentTest {
16+
17+
@Test
18+
public void youTubeTempPlaylist() {
19+
20+
Stream<StreamEntity> entityStream = List.of(
21+
22+
"https://www.youtube.com/watch?v=1"
23+
,"https://www.youtube.com/watch?v=2"
24+
,"https://www.youtube.com/watch?v=3"
25+
)
26+
.stream()
27+
.map(LocalPlaylistFragmentTest::newStreamEntity)
28+
;
29+
30+
String url = LocalPlaylistFragment.export(YOUTUBE_TEMP_PLAYLIST, entityStream, null);
31+
32+
Assert.assertEquals("http://www.youtube.com/watch_videos?video_ids=1,2,3", url);
33+
}
34+
35+
@NonNull
36+
static StreamEntity newStreamEntity(String url) {
37+
38+
return new StreamEntity(
39+
40+
0
41+
, 1
42+
, url
43+
, "Title"
44+
, StreamType.VIDEO_STREAM
45+
, 100
46+
, "Uploader"
47+
, null
48+
, null
49+
, null
50+
, null
51+
, null
52+
, null
53+
);
54+
}
55+
}

0 commit comments

Comments
 (0)