mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-11-13 13:37:12 +00:00
@@ -14,7 +14,7 @@ import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.SaveUploaderUrlHelper;
|
||||
import org.schabi.newpipe.util.SparseItemUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -62,7 +62,8 @@ public final class QueueItemMenuUtil {
|
||||
|
||||
return true;
|
||||
case R.id.menu_item_channel_details:
|
||||
SaveUploaderUrlHelper.saveUploaderUrlIfNeeded(context, item,
|
||||
SparseItemUtil.fetchUploaderUrlIfSparse(context, item.getServiceId(),
|
||||
item.getUrl(), item.getUploaderUrl(),
|
||||
// An intent must be used here.
|
||||
// Opening with FragmentManager transactions is not working,
|
||||
// as PlayQueueActivity doesn't use fragments.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.schabi.newpipe.info_list.dialog;
|
||||
|
||||
import static org.schabi.newpipe.info_list.dialog.StreamDialogEntry.fetchItemInfoIfSparse;
|
||||
import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment;
|
||||
import static org.schabi.newpipe.util.SparseItemUtil.fetchItemInfoIfSparse;
|
||||
import static org.schabi.newpipe.util.SparseItemUtil.fetchUploaderUrlIfSparse;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
@@ -14,7 +15,6 @@ import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.SaveUploaderUrlHelper;
|
||||
import org.schabi.newpipe.util.external_communication.KoreUtils;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
|
||||
@@ -40,8 +40,8 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
*/
|
||||
public enum StreamDialogDefaultEntry {
|
||||
SHOW_CHANNEL_DETAILS(R.string.show_channel_details, (fragment, item) ->
|
||||
SaveUploaderUrlHelper.saveUploaderUrlIfNeeded(fragment, item,
|
||||
uploaderUrl -> openChannelFragment(fragment, item, uploaderUrl))
|
||||
fetchUploaderUrlIfSparse(fragment.requireContext(), item.getServiceId(), item.getUrl(),
|
||||
item.getUploaderUrl(), url -> openChannelFragment(fragment, item, url))
|
||||
),
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,19 +6,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ErrorUtil;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class StreamDialogEntry {
|
||||
|
||||
@@ -40,52 +28,4 @@ public class StreamDialogEntry {
|
||||
public interface StreamDialogEntryAction {
|
||||
void onClick(Fragment fragment, StreamInfoItem infoItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a {@link StreamInfoItem} if it is incomplete and executes the callback.
|
||||
* <br />
|
||||
* This method is required if the info has been fetched
|
||||
* via a {@link org.schabi.newpipe.extractor.feed.FeedExtractor}.
|
||||
* FeedExtractors provide a fast and lightweight method to fetch info,
|
||||
* but the info might be incomplete
|
||||
* (see {@link org.schabi.newpipe.local.feed.service.FeedLoadService} for more details).
|
||||
* @param context
|
||||
* @param item the item which is checked and eventually loaded completely
|
||||
* @param callback
|
||||
*/
|
||||
public static void fetchItemInfoIfSparse(@NonNull final Context context,
|
||||
@NonNull final StreamInfoItem item,
|
||||
@NonNull final Consumer<SinglePlayQueue> callback) {
|
||||
if (!(item.getStreamType() == StreamType.LIVE_STREAM
|
||||
|| item.getStreamType() == StreamType.AUDIO_LIVE_STREAM)
|
||||
&& item.getDuration() < 0) {
|
||||
// Sparse item: fetched by fast fetch
|
||||
ExtractorHelper.getStreamInfo(
|
||||
item.getServiceId(),
|
||||
item.getUrl(),
|
||||
false
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
final HistoryRecordManager recordManager =
|
||||
new HistoryRecordManager(context);
|
||||
recordManager.saveStreamState(result, 0)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnError(throwable -> ErrorUtil.showSnackbar(
|
||||
context,
|
||||
new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
||||
item.getUrl(), item.getServiceId())))
|
||||
.subscribe();
|
||||
|
||||
callback.accept(new SinglePlayQueue(result));
|
||||
}, throwable -> ErrorUtil.createNotification(context,
|
||||
new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL,
|
||||
"Could not fetch missing stream info")));
|
||||
} else {
|
||||
callback.accept(new SinglePlayQueue(item));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
@@ -30,8 +32,6 @@ import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
import org.schabi.newpipe.util.external_communication.TextLinkifier;
|
||||
import org.schabi.newpipe.extractor.Info;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
|
||||
@@ -42,6 +42,7 @@ import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfo;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
import org.schabi.newpipe.extractor.feed.FeedExtractor;
|
||||
import org.schabi.newpipe.extractor.feed.FeedInfo;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
|
||||
@@ -50,6 +51,7 @@ import org.schabi.newpipe.extractor.search.SearchInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
|
||||
import org.schabi.newpipe.util.external_communication.TextLinkifier;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -58,8 +60,6 @@ import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
public final class ExtractorHelper {
|
||||
private static final String TAG = ExtractorHelper.class.getSimpleName();
|
||||
private static final InfoCache CACHE = InfoCache.getInstance();
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ErrorUtil;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Utility class for putting the uploader url into the database - when required.
|
||||
*/
|
||||
public final class SaveUploaderUrlHelper {
|
||||
private SaveUploaderUrlHelper() {
|
||||
}
|
||||
|
||||
// Public functions which call the function that does
|
||||
// the actual work with the correct parameters
|
||||
public static void saveUploaderUrlIfNeeded(@NonNull final Fragment fragment,
|
||||
@NonNull final StreamInfoItem infoItem,
|
||||
@NonNull final SaveUploaderUrlCallback callback) {
|
||||
saveUploaderUrlIfNeeded(fragment.requireContext(),
|
||||
infoItem.getServiceId(),
|
||||
infoItem.getUrl(),
|
||||
infoItem.getUploaderUrl(),
|
||||
callback);
|
||||
}
|
||||
public static void saveUploaderUrlIfNeeded(@NonNull final Context context,
|
||||
@NonNull final PlayQueueItem queueItem,
|
||||
@NonNull final SaveUploaderUrlCallback callback) {
|
||||
saveUploaderUrlIfNeeded(context,
|
||||
queueItem.getServiceId(),
|
||||
queueItem.getUrl(),
|
||||
queueItem.getUploaderUrl(),
|
||||
callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and saves the uploaderUrl if it is empty (meaning that it does
|
||||
* not exist in the video item). The callback is called with either the
|
||||
* fetched uploaderUrl, or the already saved uploaderUrl, but it is always
|
||||
* called with a valid uploaderUrl that can be used to show channel details.
|
||||
*
|
||||
* @param context Context
|
||||
* @param serviceId The serviceId of the item
|
||||
* @param url The item url
|
||||
* @param uploaderUrl The uploaderUrl of the item, if null or empty, it
|
||||
* will be fetched using the item url.
|
||||
* @param callback The callback that returns the fetched or existing
|
||||
* uploaderUrl
|
||||
*/
|
||||
private static void saveUploaderUrlIfNeeded(@NonNull final Context context,
|
||||
final int serviceId,
|
||||
@NonNull final String url,
|
||||
// Only used if not null or empty
|
||||
@Nullable final String uploaderUrl,
|
||||
@NonNull final SaveUploaderUrlCallback callback) {
|
||||
if (isNullOrEmpty(uploaderUrl)) {
|
||||
Toast.makeText(context, R.string.loading_channel_details,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
ExtractorHelper.getStreamInfo(serviceId, url, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
NewPipeDatabase.getInstance(context).streamDAO()
|
||||
.setUploaderUrl(serviceId, url, result.getUploaderUrl())
|
||||
.subscribeOn(Schedulers.io()).subscribe();
|
||||
callback.onCallback(result.getUploaderUrl());
|
||||
}, throwable -> ErrorUtil.createNotification(context,
|
||||
new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL,
|
||||
"Could not load channel details")
|
||||
));
|
||||
} else {
|
||||
callback.onCallback(uploaderUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public interface SaveUploaderUrlCallback {
|
||||
void onCallback(@NonNull String uploaderUrl);
|
||||
}
|
||||
}
|
||||
126
app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java
Normal file
126
app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import static org.schabi.newpipe.extractor.stream.StreamType.AUDIO_LIVE_STREAM;
|
||||
import static org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ErrorUtil;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Utility class for fetching additional data for stream items when needed.
|
||||
*/
|
||||
public final class SparseItemUtil {
|
||||
private SparseItemUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to certainly obtain an single play queue with all of the data filled in when the
|
||||
* stream info item you are handling might be sparse, e.g. because it was fetched via a {@link
|
||||
* org.schabi.newpipe.extractor.feed.FeedExtractor}. FeedExtractors provide a fast and
|
||||
* lightweight method to fetch info, but the info might be incomplete (see
|
||||
* {@link org.schabi.newpipe.local.feed.service.FeedLoadService} for more details).
|
||||
*
|
||||
* @param context the Android context
|
||||
* @param item the item which is checked and eventually loaded completely
|
||||
* @param callback the callback to call with the single play queue built from the original item
|
||||
* if all info was available, otherwise from the fetched {@link
|
||||
* org.schabi.newpipe.extractor.stream.StreamInfo}
|
||||
*/
|
||||
public static void fetchItemInfoIfSparse(@NonNull final Context context,
|
||||
@NonNull final StreamInfoItem item,
|
||||
@NonNull final Consumer<SinglePlayQueue> callback) {
|
||||
if ((!(item.getStreamType() == LIVE_STREAM || item.getStreamType() == AUDIO_LIVE_STREAM)
|
||||
&& item.getDuration() < 0) || isNullOrEmpty(item.getUploaderUrl())) {
|
||||
fetchStreamInfoAndSaveToDatabase(context, item.getServiceId(), item.getUrl(),
|
||||
streamInfo -> callback.accept(new SinglePlayQueue(streamInfo)));
|
||||
} else {
|
||||
// all info is already there, no need to fetch
|
||||
callback.accept(new SinglePlayQueue(item));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to certainly obtain an uploader url when the stream info item or play queue item you
|
||||
* are handling might not have the uploader url (e.g. because it was fetched with {@link
|
||||
* org.schabi.newpipe.extractor.feed.FeedExtractor}). A toast is shown if loading details is
|
||||
* required.
|
||||
*
|
||||
* @param context the Android context
|
||||
* @param serviceId the serviceId of the item
|
||||
* @param url the item url
|
||||
* @param uploaderUrl the uploaderUrl of the item; if null or empty will be fetched
|
||||
* @param callback the callback called with either the original uploaderUrl, if it was a valid
|
||||
* url, otherwise with the uploader url obtained by fetching the {@link
|
||||
* org.schabi.newpipe.extractor.stream.StreamInfo} corresponding to the item
|
||||
*/
|
||||
public static void fetchUploaderUrlIfSparse(@NonNull final Context context,
|
||||
final int serviceId,
|
||||
@NonNull final String url,
|
||||
@Nullable final String uploaderUrl,
|
||||
@NonNull final Consumer<String> callback) {
|
||||
if (isNullOrEmpty(uploaderUrl)) {
|
||||
fetchStreamInfoAndSaveToDatabase(context, serviceId, url,
|
||||
streamInfo -> callback.accept(streamInfo.getUploaderUrl()));
|
||||
} else {
|
||||
callback.accept(uploaderUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the stream info corresponding to the given data on an I/O thread, stores the result in
|
||||
* the database and calls the callback on the main thread with the result. A toast will be shown
|
||||
* to the user about loading stream details, so this needs to be called on the main thread.
|
||||
*
|
||||
* @param context the Android context
|
||||
* @param serviceId the service id of the stream to load
|
||||
* @param url the url of the stream to load
|
||||
* @param callback the callback to call with the result
|
||||
*/
|
||||
private static void fetchStreamInfoAndSaveToDatabase(final Context context,
|
||||
final int serviceId,
|
||||
@NonNull final String url,
|
||||
final Consumer<StreamInfo> callback) {
|
||||
Toast.makeText(context, R.string.loading_stream_details, Toast.LENGTH_SHORT).show();
|
||||
ExtractorHelper.getStreamInfo(serviceId, url, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
// save to database in the background (not on main thread)
|
||||
Completable.fromAction(() -> NewPipeDatabase.getInstance(context)
|
||||
.streamDAO().upsert(new StreamEntity(result)))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnError(throwable ->
|
||||
ErrorUtil.createNotification(context,
|
||||
new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
||||
"Saving stream info to database", result)))
|
||||
.subscribe();
|
||||
|
||||
// call callback on main thread with the obtained result
|
||||
callback.accept(result);
|
||||
}, throwable -> ErrorUtil.createNotification(context,
|
||||
new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
||||
"Loading stream info: " + url, serviceId)
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user