Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
741 changes: 741 additions & 0 deletions app/schemas/org.schabi.newpipe.database.AppDatabase/10.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion app/src/main/java/org/schabi/newpipe/NewPipeDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.schabi.newpipe.database.Migrations.MIGRATION_5_6
import org.schabi.newpipe.database.Migrations.MIGRATION_6_7
import org.schabi.newpipe.database.Migrations.MIGRATION_7_8
import org.schabi.newpipe.database.Migrations.MIGRATION_8_9
import org.schabi.newpipe.database.Migrations.MIGRATION_9_10

object NewPipeDatabase {

Expand All @@ -37,7 +38,8 @@ object NewPipeDatabase {
MIGRATION_5_6,
MIGRATION_6_7,
MIGRATION_7_8,
MIGRATION_8_9
MIGRATION_8_9,
MIGRATION_9_10
).build()
}

Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/org/schabi/newpipe/database/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package org.schabi.newpipe.database
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import org.schabi.newpipe.database.block.BlockedChannelDAO
import org.schabi.newpipe.database.block.BlockedChannelEntity
import org.schabi.newpipe.database.feed.dao.FeedDAO
import org.schabi.newpipe.database.feed.dao.FeedGroupDAO
import org.schabi.newpipe.database.feed.model.FeedEntity
Expand All @@ -34,8 +36,9 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity

@TypeConverters(Converters::class)
@Database(
version = Migrations.DB_VER_9,
version = Migrations.DB_VER_10,
entities = [
BlockedChannelEntity::class,
SubscriptionEntity::class,
SearchHistoryEntry::class,
StreamEntity::class,
Expand All @@ -51,6 +54,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity
]
)
abstract class AppDatabase : RoomDatabase() {
abstract fun blockedChannelDAO(): BlockedChannelDAO
abstract fun feedDAO(): FeedDAO
abstract fun feedGroupDAO(): FeedGroupDAO
abstract fun playlistDAO(): PlaylistDAO
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/org/schabi/newpipe/database/Migrations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ object Migrations {
const val DB_VER_7 = 7
const val DB_VER_8 = 8
const val DB_VER_9 = 9
const val DB_VER_10 = 10

private val TAG = Migrations::class.java.getName()
private val isDebug = MainActivity.DEBUG
Expand Down Expand Up @@ -348,4 +349,16 @@ object Migrations {
db.endTransaction()
}
}

val MIGRATION_9_10 = Migration(DB_VER_9, DB_VER_10) { db ->
db.execSQL(
"CREATE TABLE IF NOT EXISTS `blocked_channels` " +
"(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
"`url` TEXT NOT NULL, `name` TEXT NOT NULL)"
)
db.execSQL(
"CREATE UNIQUE INDEX IF NOT EXISTS `index_blocked_channels_url` " +
"ON `blocked_channels` (`url`)"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe contributors <https://newpipe.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package org.schabi.newpipe.database.block

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.reactivex.rxjava3.core.Flowable

@Dao
abstract class BlockedChannelDAO {
@Query(
"""
SELECT * FROM blocked_channels
ORDER BY name COLLATE NOCASE ASC, url COLLATE NOCASE ASC
"""
)
abstract fun getAll(): Flowable<List<BlockedChannelEntity>>

@Query("SELECT url FROM blocked_channels")
abstract fun getBlockedUrls(): List<String>

@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun upsert(blockedChannel: BlockedChannelEntity): Long

@Query("DELETE FROM blocked_channels WHERE url = :url")
abstract fun deleteByUrl(url: String): Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe contributors <https://newpipe.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package org.schabi.newpipe.database.block

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey

@Entity(
tableName = BlockedChannelEntity.BLOCKED_CHANNEL_TABLE,
indices = [Index(value = [BlockedChannelEntity.BLOCKED_CHANNEL_URL], unique = true)]
)
data class BlockedChannelEntity(
@PrimaryKey(autoGenerate = true)
val uid: Long = 0,

@ColumnInfo(name = BLOCKED_CHANNEL_URL)
val url: String,

@ColumnInfo(name = BLOCKED_CHANNEL_NAME)
val name: String
) {
companion object {
const val BLOCKED_CHANNEL_TABLE = "blocked_channels"
const val BLOCKED_CHANNEL_URL = "url"
const val BLOCKED_CHANNEL_NAME = "name"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment.Companion.g
import org.schabi.newpipe.ktx.AnimationType
import org.schabi.newpipe.ktx.animate
import org.schabi.newpipe.ktx.animateRotation
import org.schabi.newpipe.local.channel.BlockedChannelManager
import org.schabi.newpipe.local.dialog.PlaylistDialog
import org.schabi.newpipe.local.history.HistoryRecordManager
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment
Expand Down Expand Up @@ -203,6 +204,9 @@ class VideoDetailFragment :
private var currentWorker: Disposable? = null
private val disposables = CompositeDisposable()
private var positionSubscriber: Disposable? = null
private val blockedChannelManager by lazy(LazyThreadSafetyMode.NONE) {
BlockedChannelManager(requireContext())
}

/*//////////////////////////////////////////////////////////////////////////
// Service management
Expand Down Expand Up @@ -780,6 +784,7 @@ class VideoDetailFragment :
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
.subscribeOn(Schedulers.io())
.flatMap { blockedChannelManager.filterStreamInfo(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.local.channel.BlockedChannelManager;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.views.NewPipeRecyclerView;

Expand All @@ -46,6 +47,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
@Nullable
protected Page currentNextPage;
protected Disposable currentWorker;
private BlockedChannelManager blockedChannelManager;

protected BaseListInfoFragment(final UserAction errorUserAction) {
this.errorUserAction = errorUserAction;
Expand All @@ -54,6 +56,7 @@ protected BaseListInfoFragment(final UserAction errorUserAction) {
@Override
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
blockedChannelManager = new BlockedChannelManager(rootView.getContext());
setTitle(name);
showListFooter(hasMoreItems());
}
Expand Down Expand Up @@ -145,6 +148,11 @@ public void startLoading(final boolean forceLoad) {
}
currentWorker = loadResult(forceLoad)
.subscribeOn(Schedulers.io())
.flatMap(result -> blockedChannelManager.filterList(result.getRelatedItems())
.map(filteredItems -> {
result.setRelatedItems(filteredItems);
return result;
}))
.observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull final L result) -> {
isLoading.set(false);
Expand Down Expand Up @@ -177,6 +185,12 @@ protected void loadMoreItems() {

currentWorker = loadMoreItemsLogic()
.subscribeOn(Schedulers.io())
.flatMap(result -> blockedChannelManager.filterList(result.getItems())
.map(filteredItems -> new ListExtractor.InfoItemsPage<>(
filteredItems,
result.getNextPage(),
result.getErrors()
)))
.observeOn(AndroidSchedulers.mainThread())
.doFinally(this::allowDownwardFocusScroll)
.subscribe(infoItemsPage -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.channel.BlockedChannelManager;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
Expand Down Expand Up @@ -158,6 +159,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I

private SuggestionListAdapter suggestionListAdapter;
private HistoryRecordManager historyRecordManager;
private BlockedChannelManager blockedChannelManager;

/*//////////////////////////////////////////////////////////////////////////
// Views
Expand Down Expand Up @@ -211,6 +213,7 @@ public void onAttach(@NonNull final Context context) {

suggestionListAdapter = new SuggestionListAdapter();
historyRecordManager = new HistoryRecordManager(context);
blockedChannelManager = new BlockedChannelManager(context);
}

@Override
Expand Down Expand Up @@ -895,6 +898,11 @@ public void startLoading(final boolean forceLoad) {
Arrays.asList(contentFilter),
sortFilter)
.subscribeOn(Schedulers.io())
.flatMap(result -> blockedChannelManager.filterList(result.getRelatedItems())
.map(filteredItems -> {
result.setRelatedItems(filteredItems);
return result;
}))
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
.subscribe(this::handleResult, this::onItemError);
Expand All @@ -918,6 +926,12 @@ protected void loadMoreItems() {
sortFilter,
nextPage)
.subscribeOn(Schedulers.io())
.flatMap(result -> blockedChannelManager.filterList(result.getItems())
.map(filteredItems -> new ListExtractor.InfoItemsPage<>(
filteredItems,
result.getNextPage(),
result.getErrors()
)))
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
.subscribe(this::handleNextItems, this::onItemError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ public Builder addDefaultEndEntries() {
StreamDialogDefaultEntry.DOWNLOAD,
StreamDialogDefaultEntry.APPEND_PLAYLIST,
StreamDialogDefaultEntry.SHARE,
StreamDialogDefaultEntry.OPEN_IN_BROWSER
StreamDialogDefaultEntry.OPEN_IN_BROWSER,
StreamDialogDefaultEntry.BLOCK_CHANNEL
);
addPlayWithKodiEntryIfNeeded();
addMarkAsWatchedEntryIfNeeded();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
import org.schabi.newpipe.local.channel.BlockedChannelUiHelper;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.external_communication.KoreUtils;
Expand Down Expand Up @@ -138,6 +139,8 @@ public enum StreamDialogDefaultEntry {
OPEN_IN_BROWSER(R.string.open_in_browser, (fragment, item) ->
ShareUtils.openUrlInBrowser(fragment.requireContext(), item.getUrl())),

BLOCK_CHANNEL(R.string.block_channel, (fragment, item) ->
BlockedChannelUiHelper.blockChannel(fragment, item)),

MARK_AS_WATCHED(R.string.mark_as_watched, (fragment, item) ->
new HistoryRecordManager(fragment.getContext())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe contributors <https://newpipe.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package org.schabi.newpipe.local.channel

import android.content.Context
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single
import org.schabi.newpipe.NewPipeDatabase
import org.schabi.newpipe.database.block.BlockedChannelDAO
import org.schabi.newpipe.database.block.BlockedChannelEntity
import org.schabi.newpipe.extractor.InfoItem
import org.schabi.newpipe.extractor.channel.ChannelInfoItem
import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem

class BlockedChannelManager(context: Context) {
private val blockedChannelTable: BlockedChannelDAO =
NewPipeDatabase.getInstance(context).blockedChannelDAO()

fun blockedChannels(): Flowable<List<BlockedChannelEntity>> = blockedChannelTable.getAll()

fun blockChannel(url: String, name: String): Completable = Completable.fromAction {
blockedChannelTable.upsert(
BlockedChannelEntity(
url = url,
name = name.ifBlank { url }
)
)
}

fun unblockChannel(url: String): Completable = Completable.fromAction {
blockedChannelTable.deleteByUrl(url)
}

fun <T : InfoItem> filterList(items: List<T>): Single<List<T>> = Single.fromCallable {
val blockedUrls = blockedChannelTable.getBlockedUrls().toHashSet()
if (blockedUrls.isEmpty()) {
items
} else {
items.filterNot { it.belongsToBlockedChannel(blockedUrls) }
}
}

fun filterStreamInfo(info: StreamInfo): Single<StreamInfo> = filterList(info.relatedItems).map { filteredItems ->
info.relatedItems = filteredItems
info
}

private fun InfoItem.belongsToBlockedChannel(blockedUrls: Set<String>): Boolean {
return when (this) {
is StreamInfoItem -> !uploaderUrl.isNullOrBlank() && blockedUrls.contains(uploaderUrl)
is ChannelInfoItem -> !url.isNullOrBlank() && blockedUrls.contains(url)
else -> false
}
}
}
Loading
Loading