mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-22 16:10:31 +00:00
Channels are now an Info
The previous "main" tab is now just a normal tab returned in getTabs(). Various part of the code that used to handle channels as ListInfo now either take the first (playable, i.e. with streams) tab (e.g. the ChannelTabPlayQueue), or take all of them combined (e.g. the feed).
This commit is contained in:
parent
dfbd39e898
commit
c076a0f771
@ -197,7 +197,7 @@ dependencies {
|
||||
// name and the commit hash with the commit hash of the (pushed) commit you want to test
|
||||
// This works thanks to JitPack: https://jitpack.io/
|
||||
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
||||
implementation 'com.github.Theta-Dev:NewPipeExtractor:e278a2d6d428dec82a304d271803d35afbd7340c'
|
||||
implementation 'com.github.Theta-Dev:NewPipeExtractor:c3651bef5c622abf0cdfc34c9985ba8c33d1491e'
|
||||
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
|
||||
|
||||
/** Checkstyle **/
|
||||
|
@ -65,6 +65,7 @@ import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
@ -72,10 +73,11 @@ import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||
import org.schabi.newpipe.player.PlayerType;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelTabPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.ChannelTabHelper;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.DeviceUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
@ -1022,7 +1024,16 @@ public class RouterActivity extends AppCompatActivity {
|
||||
}
|
||||
playQueue = new SinglePlayQueue((StreamInfo) info);
|
||||
} else if (info instanceof ChannelInfo) {
|
||||
playQueue = new ChannelPlayQueue((ChannelInfo) info);
|
||||
final Optional<ListLinkHandler> playableTab = ((ChannelInfo) info).getTabs()
|
||||
.stream()
|
||||
.filter(ChannelTabHelper::isStreamsTab)
|
||||
.findFirst();
|
||||
|
||||
if (playableTab.isPresent()) {
|
||||
playQueue = new ChannelTabPlayQueue(info.getServiceId(), playableTab.get());
|
||||
} else {
|
||||
return; // there is no playable tab
|
||||
}
|
||||
} else if (info instanceof PlaylistInfo) {
|
||||
playQueue = new PlaylistPlayQueue((PlaylistInfo) info);
|
||||
} else {
|
||||
|
@ -16,7 +16,6 @@ import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.ListInfo;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.views.NewPipeRecyclerView;
|
||||
@ -236,11 +235,9 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
|
||||
infoListAdapter.clearStreamItemList();
|
||||
// showEmptyState should be called only if there is no item as
|
||||
// well as no header in infoListAdapter
|
||||
if (!(result instanceof ChannelInfo && infoListAdapter.getItemCount() == 1)) {
|
||||
showEmptyState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
final List<Throwable> errors = new ArrayList<>(result.getErrors());
|
||||
|
@ -277,10 +277,9 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
}, onError));
|
||||
}
|
||||
|
||||
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription,
|
||||
final ChannelInfo info) {
|
||||
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) {
|
||||
return (@NonNull Object o) -> {
|
||||
subscriptionManager.insertSubscription(subscription, info);
|
||||
subscriptionManager.insertSubscription(subscription);
|
||||
return o;
|
||||
};
|
||||
}
|
||||
@ -355,7 +354,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
info.getSubscriberCount());
|
||||
channelSubscription = null;
|
||||
updateNotifyButton(null);
|
||||
subscribeButtonMonitor = monitorSubscribeButton(mapOnSubscribe(channel, info));
|
||||
subscribeButtonMonitor = monitorSubscribeButton(mapOnSubscribe(channel));
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Found subscription to this channel!");
|
||||
@ -451,8 +450,6 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
tabAdapter.clearAllItems();
|
||||
|
||||
if (currentInfo != null && !channelContentNotSupported) {
|
||||
tabAdapter.addFragment(new ChannelVideosFragment(currentInfo), "Videos");
|
||||
|
||||
final Context context = requireContext();
|
||||
final SharedPreferences preferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
|
@ -1,157 +0,0 @@
|
||||
package org.schabi.newpipe.fragments.list.channel;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.databinding.FragmentChannelVideosBinding;
|
||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.player.PlayerType;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
|
||||
public class ChannelVideosFragment extends BaseListInfoFragment<StreamInfoItem, ChannelInfo> {
|
||||
|
||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
private FragmentChannelVideosBinding channelBinding;
|
||||
private PlaylistControlBinding playlistControlBinding;
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Constructors and lifecycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
// required by the Android framework to restore fragments after saving
|
||||
public ChannelVideosFragment() {
|
||||
super(UserAction.REQUESTED_CHANNEL);
|
||||
}
|
||||
|
||||
public ChannelVideosFragment(final int serviceId, final String url, final String name) {
|
||||
this();
|
||||
setInitialData(serviceId, url, name);
|
||||
}
|
||||
|
||||
public ChannelVideosFragment(@NonNull final ChannelInfo info) {
|
||||
this(info.getServiceId(), info.getUrl(), info.getName());
|
||||
this.currentInfo = info;
|
||||
this.currentNextPage = info.getNextPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (activity != null && useAsFrontPage) {
|
||||
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
channelBinding = FragmentChannelVideosBinding.inflate(inflater, container, false);
|
||||
return channelBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposables.clear();
|
||||
channelBinding = null;
|
||||
playlistControlBinding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<View> getListHeaderSupplier() {
|
||||
playlistControlBinding = PlaylistControlBinding
|
||||
.inflate(activity.getLayoutInflater(), itemsList, false);
|
||||
return playlistControlBinding::getRoot;
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Loading
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected Single<ListExtractor.InfoItemsPage<StreamInfoItem>> loadMoreItemsLogic() {
|
||||
return ExtractorHelper.getMoreChannelItems(serviceId, url, currentNextPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<ChannelInfo> loadResult(final boolean forceLoad) {
|
||||
return ExtractorHelper.getChannelInfo(serviceId, url, forceLoad);
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull final ChannelInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
// PlaylistControls should be visible only if there is some item in
|
||||
// infoListAdapter other than header
|
||||
if (infoListAdapter.getItemCount() != 1) {
|
||||
playlistControlBinding.getRoot().setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
playlistControlBinding.getRoot().setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
disposables.clear();
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(
|
||||
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(
|
||||
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(
|
||||
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
||||
return true;
|
||||
});
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue() {
|
||||
final List<StreamInfoItem> streamItems = infoListAdapter.getItemsList().stream()
|
||||
.filter(StreamInfoItem.class::isInstance)
|
||||
.map(StreamInfoItem.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(),
|
||||
currentInfo.getNextPage(), streamItems, 0);
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ class NotificationHelper(val context: Context) {
|
||||
.setAutoCancel(true)
|
||||
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
|
||||
.setGroupSummary(true)
|
||||
.setGroup(data.listInfo.url)
|
||||
.setGroup(data.originalInfo.url)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
|
||||
// Build a summary notification for Android versions < 7.0
|
||||
@ -73,7 +73,7 @@ class NotificationHelper(val context: Context) {
|
||||
context,
|
||||
data.pseudoId,
|
||||
NavigationHelper
|
||||
.getChannelIntent(context, data.listInfo.serviceId, data.listInfo.url)
|
||||
.getChannelIntent(context, data.originalInfo.serviceId, data.originalInfo.url)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
0,
|
||||
false
|
||||
@ -88,7 +88,7 @@ class NotificationHelper(val context: Context) {
|
||||
|
||||
// Show individual stream notifications, set channel icon only if there is actually
|
||||
// one
|
||||
showStreamNotifications(newStreams, data.listInfo.serviceId, bitmap)
|
||||
showStreamNotifications(newStreams, data.originalInfo.serviceId, bitmap)
|
||||
// Show summary notification
|
||||
manager.notify(data.pseudoId, summaryBuilder.build())
|
||||
|
||||
@ -97,7 +97,7 @@ class NotificationHelper(val context: Context) {
|
||||
|
||||
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
|
||||
// Show individual stream notifications
|
||||
showStreamNotifications(newStreams, data.listInfo.serviceId, null)
|
||||
showStreamNotifications(newStreams, data.originalInfo.serviceId, null)
|
||||
// Show summary notification
|
||||
manager.notify(data.pseudoId, summaryBuilder.build())
|
||||
iconLoadingTargets.remove(this) // allow it to be garbage-collected
|
||||
|
@ -13,11 +13,16 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.database.subscription.NotificationMode
|
||||
import org.schabi.newpipe.extractor.ListInfo
|
||||
import org.schabi.newpipe.extractor.Info
|
||||
import org.schabi.newpipe.extractor.NewPipe
|
||||
import org.schabi.newpipe.extractor.feed.FeedInfo
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||
import org.schabi.newpipe.util.ExtractorHelper
|
||||
import org.schabi.newpipe.util.ChannelTabHelper
|
||||
import org.schabi.newpipe.util.ExtractorHelper.getChannelInfo
|
||||
import org.schabi.newpipe.util.ExtractorHelper.getChannelTab
|
||||
import org.schabi.newpipe.util.ExtractorHelper.getMoreChannelTabItems
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@ -102,49 +107,88 @@ class FeedLoadManager(private val context: Context) {
|
||||
.filter { !cancelSignal.get() }
|
||||
.map { subscriptionEntity ->
|
||||
var error: Throwable? = null
|
||||
val storeOriginalErrorAndRethrow = { e: Throwable ->
|
||||
// keep original to prevent blockingGet() from wrapping it into RuntimeException
|
||||
error = e
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
// check for and load new streams
|
||||
// either by using the dedicated feed method or by getting the channel info
|
||||
val listInfo = if (useFeedExtractor) {
|
||||
ExtractorHelper
|
||||
.getFeedInfoFallbackToChannelInfo(
|
||||
subscriptionEntity.serviceId,
|
||||
subscriptionEntity.url
|
||||
)
|
||||
.onErrorReturn {
|
||||
error = it // store error, otherwise wrapped into RuntimeException
|
||||
throw it
|
||||
var originalInfo: Info? = null
|
||||
var streams: List<StreamInfoItem>? = null
|
||||
val errors = ArrayList<Throwable>()
|
||||
|
||||
if (useFeedExtractor) {
|
||||
NewPipe.getService(subscriptionEntity.serviceId)
|
||||
.getFeedExtractor(subscriptionEntity.url)
|
||||
?.also { feedExtractor ->
|
||||
// the user wants to use a feed extractor and there is one, use it
|
||||
val feedInfo = FeedInfo.getInfo(feedExtractor)
|
||||
errors.addAll(feedInfo.errors)
|
||||
originalInfo = feedInfo
|
||||
streams = feedInfo.relatedItems
|
||||
}
|
||||
}
|
||||
|
||||
if (originalInfo == null) {
|
||||
// use the normal channel tabs extractor if either the user wants it, or
|
||||
// the current service does not have a dedicated feed extractor
|
||||
|
||||
val channelInfo = getChannelInfo(
|
||||
subscriptionEntity.serviceId,
|
||||
subscriptionEntity.url, true
|
||||
)
|
||||
.onErrorReturn(storeOriginalErrorAndRethrow)
|
||||
.blockingGet()
|
||||
errors.addAll(channelInfo.errors)
|
||||
originalInfo = channelInfo
|
||||
|
||||
streams = channelInfo.tabs
|
||||
.filter(ChannelTabHelper::isStreamsTab)
|
||||
.map {
|
||||
Pair(
|
||||
getChannelTab(subscriptionEntity.serviceId, it, true)
|
||||
.onErrorReturn(storeOriginalErrorAndRethrow)
|
||||
.blockingGet(),
|
||||
it
|
||||
)
|
||||
}
|
||||
.flatMap { (channelTabInfo, linkHandler) ->
|
||||
errors.addAll(channelTabInfo.errors)
|
||||
if (channelTabInfo.relatedItems.isEmpty()) {
|
||||
val infoItemsPage = getMoreChannelTabItems(
|
||||
subscriptionEntity.serviceId,
|
||||
linkHandler, channelTabInfo.nextPage
|
||||
)
|
||||
.blockingGet()
|
||||
|
||||
errors.addAll(infoItemsPage.errors)
|
||||
return@flatMap infoItemsPage.items
|
||||
} else {
|
||||
ExtractorHelper
|
||||
.getChannelInfo(
|
||||
subscriptionEntity.serviceId,
|
||||
subscriptionEntity.url,
|
||||
true
|
||||
)
|
||||
.onErrorReturn {
|
||||
error = it // store error, otherwise wrapped into RuntimeException
|
||||
throw it
|
||||
return@flatMap channelTabInfo.relatedItems
|
||||
}
|
||||
}
|
||||
.filterIsInstance<StreamInfoItem>()
|
||||
}
|
||||
.blockingGet()
|
||||
} as ListInfo<StreamInfoItem>
|
||||
|
||||
return@map Notification.createOnNext(
|
||||
FeedUpdateInfo(
|
||||
subscriptionEntity,
|
||||
listInfo
|
||||
originalInfo!!,
|
||||
streams!!,
|
||||
errors,
|
||||
)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
if (error == null) {
|
||||
// do this to prevent blockingGet() from wrapping into RuntimeException
|
||||
error = e
|
||||
}
|
||||
|
||||
val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
|
||||
val wrapper =
|
||||
FeedLoadService.RequestException(subscriptionEntity.uid, request, error!!)
|
||||
val wrapper = FeedLoadService.RequestException(
|
||||
subscriptionEntity.uid,
|
||||
request,
|
||||
// do this to prevent blockingGet() from wrapping into RuntimeException
|
||||
error ?: e
|
||||
)
|
||||
return@map Notification.createOnError<FeedUpdateInfo>(wrapper)
|
||||
}
|
||||
}
|
||||
@ -203,24 +247,24 @@ class FeedLoadManager(private val context: Context) {
|
||||
for (notification in list) {
|
||||
when {
|
||||
notification.isOnNext -> {
|
||||
val subscriptionId = notification.value!!.uid
|
||||
val info = notification.value!!.listInfo
|
||||
val info = notification.value!!
|
||||
|
||||
notification.value!!.newStreams = filterNewStreams(
|
||||
notification.value!!.listInfo.relatedItems
|
||||
)
|
||||
notification.value!!.newStreams = filterNewStreams(info.streams)
|
||||
|
||||
feedDatabaseManager.upsertAll(subscriptionId, info.relatedItems)
|
||||
subscriptionManager.updateFromInfo(subscriptionId, info)
|
||||
feedDatabaseManager.upsertAll(info.uid, info.streams)
|
||||
subscriptionManager.updateFromInfo(info.uid, info.originalInfo)
|
||||
|
||||
if (info.errors.isNotEmpty()) {
|
||||
feedResultsHolder.addErrors(
|
||||
FeedLoadService.RequestException.wrapList(
|
||||
subscriptionId,
|
||||
info
|
||||
info.errors.map {
|
||||
FeedLoadService.RequestException(
|
||||
info.uid,
|
||||
"${info.originalInfo.serviceId}:${info.originalInfo.url}",
|
||||
it
|
||||
)
|
||||
}
|
||||
)
|
||||
feedDatabaseManager.markAsOutdated(subscriptionId)
|
||||
feedDatabaseManager.markAsOutdated(info.uid)
|
||||
}
|
||||
}
|
||||
notification.isOnError -> {
|
||||
|
@ -39,8 +39,6 @@ import org.schabi.newpipe.App
|
||||
import org.schabi.newpipe.MainActivity.DEBUG
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.extractor.ListInfo
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ErrorResultEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.postEvent
|
||||
import java.util.concurrent.TimeUnit
|
||||
@ -126,17 +124,7 @@ class FeedLoadService : Service() {
|
||||
// Loading & Handling
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RequestException(val subscriptionId: Long, message: String, cause: Throwable) : Exception(message, cause) {
|
||||
companion object {
|
||||
fun wrapList(subscriptionId: Long, info: ListInfo<StreamInfoItem>): List<Throwable> {
|
||||
val toReturn = ArrayList<Throwable>(info.errors.size)
|
||||
info.errors.mapTo(toReturn) {
|
||||
RequestException(subscriptionId, info.serviceId.toString() + ":" + info.url, it)
|
||||
}
|
||||
return toReturn
|
||||
}
|
||||
}
|
||||
}
|
||||
class RequestException(val subscriptionId: Long, message: String, cause: Throwable) : Exception(message, cause)
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Notification
|
||||
|
@ -2,7 +2,7 @@ package org.schabi.newpipe.local.feed.service
|
||||
|
||||
import org.schabi.newpipe.database.subscription.NotificationMode
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import org.schabi.newpipe.extractor.ListInfo
|
||||
import org.schabi.newpipe.extractor.Info
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
|
||||
data class FeedUpdateInfo(
|
||||
@ -11,24 +11,30 @@ data class FeedUpdateInfo(
|
||||
val notificationMode: Int,
|
||||
val name: String,
|
||||
val avatarUrl: String,
|
||||
val listInfo: ListInfo<StreamInfoItem>,
|
||||
val originalInfo: Info,
|
||||
val streams: List<StreamInfoItem>,
|
||||
val errors: List<Throwable>,
|
||||
) {
|
||||
constructor(
|
||||
subscription: SubscriptionEntity,
|
||||
listInfo: ListInfo<StreamInfoItem>,
|
||||
originalInfo: Info,
|
||||
streams: List<StreamInfoItem>,
|
||||
errors: List<Throwable>,
|
||||
) : this(
|
||||
uid = subscription.uid,
|
||||
notificationMode = subscription.notificationMode,
|
||||
name = subscription.name,
|
||||
avatarUrl = subscription.avatarUrl,
|
||||
listInfo = listInfo,
|
||||
originalInfo = originalInfo,
|
||||
streams = streams,
|
||||
errors = errors,
|
||||
)
|
||||
|
||||
/**
|
||||
* Integer id, can be used as notification id, etc.
|
||||
*/
|
||||
val pseudoId: Int
|
||||
get() = listInfo.url.hashCode()
|
||||
get() = originalInfo.url.hashCode()
|
||||
|
||||
lateinit var newStreams: List<StreamInfoItem>
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.local.subscription
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Pair
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
@ -11,8 +12,9 @@ import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.database.subscription.NotificationMode
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionDAO
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import org.schabi.newpipe.extractor.ListInfo
|
||||
import org.schabi.newpipe.extractor.Info
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo
|
||||
import org.schabi.newpipe.extractor.channel.ChannelTabInfo
|
||||
import org.schabi.newpipe.extractor.feed.FeedInfo
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
@ -46,26 +48,31 @@ class SubscriptionManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
|
||||
fun upsertAll(infoList: List<Pair<ChannelInfo, List<ChannelTabInfo>>>): List<SubscriptionEntity> {
|
||||
val listEntities = subscriptionTable.upsertAll(
|
||||
infoList.map { SubscriptionEntity.from(it) }
|
||||
infoList.map { SubscriptionEntity.from(it.first) }
|
||||
)
|
||||
|
||||
database.runInTransaction {
|
||||
infoList.forEachIndexed { index, info ->
|
||||
feedDatabaseManager.upsertAll(listEntities[index].uid, info.relatedItems)
|
||||
info.second.forEach {
|
||||
feedDatabaseManager.upsertAll(
|
||||
listEntities[index].uid,
|
||||
it.relatedItems.filterIsInstance<StreamInfoItem>()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return listEntities
|
||||
}
|
||||
|
||||
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)
|
||||
fun updateChannelInfo(info: ChannelInfo): Completable =
|
||||
subscriptionTable.getSubscription(info.serviceId, info.url)
|
||||
.flatMapCompletable {
|
||||
Completable.fromRunnable {
|
||||
it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount)
|
||||
subscriptionTable.update(it)
|
||||
feedDatabaseManager.upsertAll(it.uid, info.relatedItems)
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +91,7 @@ class SubscriptionManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFromInfo(subscriptionId: Long, info: ListInfo<StreamInfoItem>) {
|
||||
fun updateFromInfo(subscriptionId: Long, info: Info) {
|
||||
val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId)
|
||||
|
||||
if (info is FeedInfo) {
|
||||
@ -107,11 +114,8 @@ class SubscriptionManager(context: Context) {
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun insertSubscription(subscriptionEntity: SubscriptionEntity, info: ChannelInfo) {
|
||||
database.runInTransaction {
|
||||
val subscriptionId = subscriptionTable.insert(subscriptionEntity)
|
||||
feedDatabaseManager.upsertAll(subscriptionId, info.relatedItems)
|
||||
}
|
||||
fun insertSubscription(subscriptionEntity: SubscriptionEntity) {
|
||||
subscriptionTable.insert(subscriptionEntity)
|
||||
}
|
||||
|
||||
fun deleteSubscription(subscriptionEntity: SubscriptionEntity) {
|
||||
@ -125,7 +129,10 @@ class SubscriptionManager(context: Context) {
|
||||
*/
|
||||
private fun rememberAllStreams(subscription: SubscriptionEntity): Completable {
|
||||
return ExtractorHelper.getChannelInfo(subscription.serviceId, subscription.url, false)
|
||||
.map { channel -> channel.relatedItems.map { stream -> StreamEntity(stream) } }
|
||||
.flatMap { info ->
|
||||
ExtractorHelper.getChannelTab(subscription.serviceId, info.tabs.first(), false)
|
||||
}
|
||||
.map { channel -> channel.relatedItems.filterIsInstance<StreamInfoItem>().map { stream -> StreamEntity(stream) } }
|
||||
.flatMapCompletable { entities ->
|
||||
Completable.fromAction {
|
||||
database.streamDAO().upsertAll(entities)
|
||||
|
@ -26,6 +26,7 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -38,6 +39,7 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelTabInfo;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.streams.io.SharpInputStream;
|
||||
@ -48,6 +50,7 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -199,12 +202,19 @@ public class SubscriptionsImportService extends BaseImportExportService {
|
||||
|
||||
.parallel(PARALLEL_EXTRACTIONS)
|
||||
.runOn(Schedulers.io())
|
||||
.map((Function<SubscriptionItem, Notification<ChannelInfo>>) subscriptionItem -> {
|
||||
.map((Function<SubscriptionItem, Notification<Pair<ChannelInfo,
|
||||
List<ChannelTabInfo>>>>) subscriptionItem -> {
|
||||
try {
|
||||
return Notification.createOnNext(ExtractorHelper
|
||||
final ChannelInfo channelInfo = ExtractorHelper
|
||||
.getChannelInfo(subscriptionItem.getServiceId(),
|
||||
subscriptionItem.getUrl(), true)
|
||||
.blockingGet());
|
||||
.blockingGet();
|
||||
return Notification.createOnNext(new Pair<>(channelInfo,
|
||||
Collections.singletonList(
|
||||
ExtractorHelper.getChannelTab(
|
||||
subscriptionItem.getServiceId(),
|
||||
channelInfo.getTabs().get(0), true).blockingGet()
|
||||
)));
|
||||
} catch (final Throwable e) {
|
||||
return Notification.createOnError(e);
|
||||
}
|
||||
@ -223,7 +233,7 @@ public class SubscriptionsImportService extends BaseImportExportService {
|
||||
}
|
||||
|
||||
private Subscriber<List<SubscriptionEntity>> getSubscriber() {
|
||||
return new Subscriber<List<SubscriptionEntity>>() {
|
||||
return new Subscriber<>() {
|
||||
@Override
|
||||
public void onSubscribe(final Subscription s) {
|
||||
subscription = s;
|
||||
@ -254,10 +264,11 @@ public class SubscriptionsImportService extends BaseImportExportService {
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<Notification<ChannelInfo>> getNotificationsConsumer() {
|
||||
private Consumer<Notification<Pair<ChannelInfo,
|
||||
List<ChannelTabInfo>>>> getNotificationsConsumer() {
|
||||
return notification -> {
|
||||
if (notification.isOnNext()) {
|
||||
final String name = notification.getValue().getName();
|
||||
final String name = notification.getValue().first.getName();
|
||||
eventListener.onItemCompleted(!TextUtils.isEmpty(name) ? name : "");
|
||||
} else if (notification.isOnError()) {
|
||||
final Throwable error = notification.getError();
|
||||
@ -275,10 +286,12 @@ public class SubscriptionsImportService extends BaseImportExportService {
|
||||
};
|
||||
}
|
||||
|
||||
private Function<List<Notification<ChannelInfo>>, List<SubscriptionEntity>> upsertBatch() {
|
||||
private Function<List<Notification<Pair<ChannelInfo, List<ChannelTabInfo>>>>,
|
||||
List<SubscriptionEntity>> upsertBatch() {
|
||||
return notificationList -> {
|
||||
final List<ChannelInfo> infoList = new ArrayList<>(notificationList.size());
|
||||
for (final Notification<ChannelInfo> n : notificationList) {
|
||||
final List<Pair<ChannelInfo, List<ChannelTabInfo>>> infoList =
|
||||
new ArrayList<>(notificationList.size());
|
||||
for (final Notification<Pair<ChannelInfo, List<ChannelTabInfo>>> n : notificationList) {
|
||||
if (n.isOnNext()) {
|
||||
infoList.add(n.getValue());
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.ListInfo;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
@ -15,7 +16,7 @@ import java.util.stream.Collectors;
|
||||
import io.reactivex.rxjava3.core.SingleObserver;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
abstract class AbstractInfoPlayQueue<T extends ListInfo<StreamInfoItem>>
|
||||
abstract class AbstractInfoPlayQueue<T extends ListInfo<? extends InfoItem>>
|
||||
extends PlayQueue {
|
||||
boolean isInitial;
|
||||
private boolean isComplete;
|
||||
@ -27,7 +28,10 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo<StreamInfoItem>>
|
||||
private transient Disposable fetchReactor;
|
||||
|
||||
protected AbstractInfoPlayQueue(final T info) {
|
||||
this(info.getServiceId(), info.getUrl(), info.getNextPage(), info.getRelatedItems(), 0);
|
||||
this(info.getServiceId(), info.getUrl(), info.getNextPage(),
|
||||
info.getRelatedItems().stream().filter(StreamInfoItem.class::isInstance)
|
||||
.map(StreamInfoItem.class::cast).collect(
|
||||
Collectors.toList()), 0);
|
||||
}
|
||||
|
||||
protected AbstractInfoPlayQueue(final int serviceId,
|
||||
@ -72,7 +76,10 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo<StreamInfoItem>>
|
||||
}
|
||||
nextPage = result.getNextPage();
|
||||
|
||||
append(extractListItems(result.getRelatedItems()));
|
||||
append(extractListItems(result.getRelatedItems().stream()
|
||||
.filter(StreamInfoItem.class::isInstance)
|
||||
.map(StreamInfoItem.class::cast).collect(
|
||||
Collectors.toList())));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
@ -87,7 +94,7 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo<StreamInfoItem>>
|
||||
};
|
||||
}
|
||||
|
||||
SingleObserver<ListExtractor.InfoItemsPage<StreamInfoItem>> getNextPageObserver() {
|
||||
SingleObserver<ListExtractor.InfoItemsPage<? extends InfoItem>> getNextPageObserver() {
|
||||
return new SingleObserver<>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull final Disposable d) {
|
||||
@ -101,13 +108,16 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo<StreamInfoItem>>
|
||||
|
||||
@Override
|
||||
public void onSuccess(
|
||||
@NonNull final ListExtractor.InfoItemsPage<StreamInfoItem> result) {
|
||||
@NonNull final ListExtractor.InfoItemsPage<? extends InfoItem> result) {
|
||||
if (!result.hasNextPage()) {
|
||||
isComplete = true;
|
||||
}
|
||||
nextPage = result.getNextPage();
|
||||
|
||||
append(extractListItems(result.getItems()));
|
||||
append(extractListItems(result.getItems().stream()
|
||||
.filter(StreamInfoItem.class::isInstance)
|
||||
.map(StreamInfoItem.class::cast).collect(
|
||||
Collectors.toList())));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
|
@ -1,47 +0,0 @@
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo> {
|
||||
|
||||
public ChannelPlayQueue(final ChannelInfo info) {
|
||||
super(info);
|
||||
}
|
||||
|
||||
public ChannelPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final Page nextPage,
|
||||
final List<StreamInfoItem> streams,
|
||||
final int index) {
|
||||
super(serviceId, url, nextPage, streams, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTag() {
|
||||
return "ChannelPlayQueue@" + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
if (this.isInitial) {
|
||||
ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getHeadListObserver());
|
||||
} else {
|
||||
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextPage)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getNextPageObserver());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelTabInfo;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class ChannelTabPlayQueue extends AbstractInfoPlayQueue<ChannelTabInfo> {
|
||||
|
||||
final ListLinkHandler linkHandler;
|
||||
|
||||
public ChannelTabPlayQueue(final int serviceId,
|
||||
final ListLinkHandler linkHandler,
|
||||
final Page nextPage,
|
||||
final List<StreamInfoItem> streams,
|
||||
final int index) {
|
||||
super(serviceId, linkHandler.getUrl(), nextPage, streams, index);
|
||||
this.linkHandler = linkHandler;
|
||||
}
|
||||
|
||||
public ChannelTabPlayQueue(final int serviceId,
|
||||
final ListLinkHandler linkHandler) {
|
||||
this(serviceId, linkHandler, null, Collections.emptyList(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTag() {
|
||||
return "ChannelTabPlayQueue@" + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
if (isInitial) {
|
||||
ExtractorHelper.getChannelTab(this.serviceId, this.linkHandler, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getHeadListObserver());
|
||||
} else {
|
||||
ExtractorHelper.getMoreChannelTabItems(this.serviceId, this.linkHandler, this.nextPage)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getNextPageObserver());
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.fragments.BlankFragment;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelVideosFragment;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||
import org.schabi.newpipe.fragments.list.kiosk.DefaultKioskFragment;
|
||||
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
|
||||
@ -432,8 +432,8 @@ public abstract class Tab {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelVideosFragment getFragment(final Context context) {
|
||||
return new ChannelVideosFragment(channelServiceId, channelUrl, channelName);
|
||||
public ChannelFragment getFragment(final Context context) {
|
||||
return ChannelFragment.getInstance(channelServiceId, channelUrl, channelName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -7,24 +7,58 @@ import androidx.annotation.StringRes;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ChannelTabs;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ChannelTabHelper {
|
||||
private ChannelTabHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tab the channel tab to check
|
||||
* @return whether the tab should contain (playable) streams or not
|
||||
*/
|
||||
public static boolean isStreamsTab(final String tab) {
|
||||
switch (tab) {
|
||||
case ChannelTabs.VIDEOS:
|
||||
case ChannelTabs.TRACKS:
|
||||
case ChannelTabs.SHORTS:
|
||||
case ChannelTabs.LIVESTREAMS:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tab the channel tab link handler to check
|
||||
* @return whether the tab should contain (playable) streams or not
|
||||
*/
|
||||
public static boolean isStreamsTab(final ListLinkHandler tab) {
|
||||
final List<String> contentFilters = tab.getContentFilters();
|
||||
if (contentFilters.isEmpty()) {
|
||||
return false; // this should never happen, but check just to be sure
|
||||
} else {
|
||||
return isStreamsTab(contentFilters.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@StringRes
|
||||
private static int getShowTabKey(final String tab) {
|
||||
switch (tab) {
|
||||
case ChannelTabs.PLAYLISTS:
|
||||
return R.string.show_channel_tabs_playlists;
|
||||
case ChannelTabs.LIVESTREAMS:
|
||||
return R.string.show_channel_tabs_livestreams;
|
||||
case ChannelTabs.VIDEOS:
|
||||
return R.string.show_channel_tabs_videos;
|
||||
case ChannelTabs.TRACKS:
|
||||
return R.string.show_channel_tabs_tracks;
|
||||
case ChannelTabs.SHORTS:
|
||||
return R.string.show_channel_tabs_shorts;
|
||||
case ChannelTabs.LIVESTREAMS:
|
||||
return R.string.show_channel_tabs_livestreams;
|
||||
case ChannelTabs.CHANNELS:
|
||||
return R.string.show_channel_tabs_channels;
|
||||
case ChannelTabs.PLAYLISTS:
|
||||
return R.string.show_channel_tabs_playlists;
|
||||
case ChannelTabs.ALBUMS:
|
||||
return R.string.show_channel_tabs_albums;
|
||||
}
|
||||
@ -34,14 +68,18 @@ public final class ChannelTabHelper {
|
||||
@StringRes
|
||||
public static int getTranslationKey(final String tab) {
|
||||
switch (tab) {
|
||||
case ChannelTabs.PLAYLISTS:
|
||||
return R.string.channel_tab_playlists;
|
||||
case ChannelTabs.LIVESTREAMS:
|
||||
return R.string.channel_tab_livestreams;
|
||||
case ChannelTabs.VIDEOS:
|
||||
return R.string.channel_tab_videos;
|
||||
case ChannelTabs.TRACKS:
|
||||
return R.string.channel_tab_tracks;
|
||||
case ChannelTabs.SHORTS:
|
||||
return R.string.channel_tab_shorts;
|
||||
case ChannelTabs.LIVESTREAMS:
|
||||
return R.string.channel_tab_livestreams;
|
||||
case ChannelTabs.CHANNELS:
|
||||
return R.string.channel_tab_channels;
|
||||
case ChannelTabs.PLAYLISTS:
|
||||
return R.string.channel_tab_playlists;
|
||||
case ChannelTabs.ALBUMS:
|
||||
return R.string.channel_tab_albums;
|
||||
}
|
||||
|
@ -36,17 +36,13 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.Info;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
|
||||
import org.schabi.newpipe.extractor.ListInfo;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelTabInfo;
|
||||
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;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
@ -129,30 +125,6 @@ public final class ExtractorHelper {
|
||||
ChannelInfo.getInfo(NewPipe.getService(serviceId), url)));
|
||||
}
|
||||
|
||||
public static Single<InfoItemsPage<StreamInfoItem>> getMoreChannelItems(final int serviceId,
|
||||
final String url,
|
||||
final Page nextPage) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(() ->
|
||||
ChannelInfo.getMoreItems(NewPipe.getService(serviceId), url, nextPage));
|
||||
}
|
||||
|
||||
public static Single<ListInfo<StreamInfoItem>> getFeedInfoFallbackToChannelInfo(
|
||||
final int serviceId, final String url) {
|
||||
final Maybe<ListInfo<StreamInfoItem>> maybeFeedInfo = Maybe.fromCallable(() -> {
|
||||
final StreamingService service = NewPipe.getService(serviceId);
|
||||
final FeedExtractor feedExtractor = service.getFeedExtractor(url);
|
||||
|
||||
if (feedExtractor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return FeedInfo.getInfo(feedExtractor);
|
||||
});
|
||||
|
||||
return maybeFeedInfo.switchIfEmpty(getChannelInfo(serviceId, url, true));
|
||||
}
|
||||
|
||||
public static Single<ChannelTabInfo> getChannelTab(final int serviceId,
|
||||
final ListLinkHandler listLinkHandler,
|
||||
final boolean forceLoad) {
|
||||
|
@ -275,25 +275,31 @@
|
||||
|
||||
<!-- Content & History -->
|
||||
<string name="show_channel_tabs_key">channel_tabs</string>
|
||||
<string name="show_channel_tabs_playlists">show_channel_tabs_playlists</string>
|
||||
<string name="show_channel_tabs_livestreams">show_channel_tabs_live</string>
|
||||
<string name="show_channel_tabs_videos">show_channel_tabs_videos</string>
|
||||
<string name="show_channel_tabs_tracks">show_channel_tabs_tracks</string>
|
||||
<string name="show_channel_tabs_shorts">show_channel_tabs_shorts</string>
|
||||
<string name="show_channel_tabs_livestreams">show_channel_tabs_live</string>
|
||||
<string name="show_channel_tabs_channels">show_channel_tabs_channels</string>
|
||||
<string name="show_channel_tabs_playlists">show_channel_tabs_playlists</string>
|
||||
<string name="show_channel_tabs_albums">show_channel_tabs_albums</string>
|
||||
<string name="show_channel_tabs_about">show_channel_tabs_about</string>
|
||||
<string-array name="show_channel_tabs_value_list">
|
||||
<item>@string/show_channel_tabs_playlists</item>
|
||||
<item>@string/show_channel_tabs_livestreams</item>
|
||||
<item>@string/show_channel_tabs_videos</item>
|
||||
<item>@string/show_channel_tabs_tracks</item>
|
||||
<item>@string/show_channel_tabs_shorts</item>
|
||||
<item>@string/show_channel_tabs_livestreams</item>
|
||||
<item>@string/show_channel_tabs_channels</item>
|
||||
<item>@string/show_channel_tabs_playlists</item>
|
||||
<item>@string/show_channel_tabs_albums</item>
|
||||
<item>@string/show_channel_tabs_about</item>
|
||||
</string-array>
|
||||
<string-array name="show_channel_tabs_description_list">
|
||||
<item>@string/channel_tab_playlists</item>
|
||||
<item>@string/channel_tab_livestreams</item>
|
||||
<item>@string/channel_tab_videos</item>
|
||||
<item>@string/channel_tab_tracks</item>
|
||||
<item>@string/channel_tab_shorts</item>
|
||||
<item>@string/channel_tab_livestreams</item>
|
||||
<item>@string/channel_tab_channels</item>
|
||||
<item>@string/channel_tab_playlists</item>
|
||||
<item>@string/channel_tab_albums</item>
|
||||
<item>@string/channel_tab_about</item>
|
||||
</string-array>
|
||||
|
@ -798,10 +798,11 @@
|
||||
<string name="audio_track_type_dubbed">dubbed</string>
|
||||
<string name="audio_track_type_descriptive">descriptive</string>
|
||||
<string name="channel_tab_videos">Videos</string>
|
||||
<string name="channel_tab_livestreams">Live</string>
|
||||
<string name="channel_tab_tracks">Tracks</string>
|
||||
<string name="channel_tab_shorts">Shorts</string>
|
||||
<string name="channel_tab_playlists">Playlists</string>
|
||||
<string name="channel_tab_livestreams">Live</string>
|
||||
<string name="channel_tab_channels">Channels</string>
|
||||
<string name="channel_tab_playlists">Playlists</string>
|
||||
<string name="channel_tab_albums">Albums</string>
|
||||
<string name="channel_tab_about">About</string>
|
||||
<string name="show_channel_tabs">Channel tabs</string>
|
||||
|
Loading…
Reference in New Issue
Block a user