refactor: improve proxy url rewriting, store only unproxied URLs in database

This commit is contained in:
Bnyro 2025-01-11 12:51:13 +01:00
parent 92dba354b6
commit c15352a33d
17 changed files with 83 additions and 103 deletions

View File

@ -3,6 +3,7 @@ package com.github.libretube.api.obj
import android.os.Parcelable import android.os.Parcelable
import com.github.libretube.db.obj.DownloadItem import com.github.libretube.db.obj.DownloadItem
import com.github.libretube.enums.FileType import com.github.libretube.enums.FileType
import com.github.libretube.helpers.ProxyHelper
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.io.path.Path import kotlin.io.path.Path
@ -40,7 +41,7 @@ data class PipedStream(
videoId = videoId, videoId = videoId,
fileName = getQualityString(fileName), fileName = getQualityString(fileName),
path = Path(""), path = Path(""),
url = url, url = url?.let { ProxyHelper.unwrapUrl(it) },
format = format, format = format,
quality = quality, quality = quality,
language = audioTrackLocale, language = audioTrackLocale,

View File

@ -1,6 +1,7 @@
package com.github.libretube.api.obj package com.github.libretube.api.obj
import com.github.libretube.db.obj.PlaylistBookmark import com.github.libretube.db.obj.PlaylistBookmark
import com.github.libretube.helpers.ProxyHelper
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -20,9 +21,9 @@ data class Playlist(
return PlaylistBookmark( return PlaylistBookmark(
playlistId = playlistId, playlistId = playlistId,
playlistName = name, playlistName = name,
thumbnailUrl = thumbnailUrl, thumbnailUrl = thumbnailUrl?.let { ProxyHelper.unwrapUrl(it) },
uploader = uploader, uploader = uploader,
uploaderAvatar = uploaderAvatar, uploaderAvatar = uploaderAvatar?.let { ProxyHelper.unwrapUrl(it) },
uploaderUrl = uploaderUrl, uploaderUrl = uploaderUrl,
videos = videos videos = videos
) )

View File

@ -3,7 +3,10 @@ package com.github.libretube.api.obj
import android.os.Parcelable import android.os.Parcelable
import com.github.libretube.db.obj.LocalPlaylistItem import com.github.libretube.db.obj.LocalPlaylistItem
import com.github.libretube.db.obj.SubscriptionsFeedItem import com.github.libretube.db.obj.SubscriptionsFeedItem
import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toLocalDate
import com.github.libretube.helpers.ProxyHelper
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -32,10 +35,10 @@ data class StreamItem(
playlistId = playlistId.toInt(), playlistId = playlistId.toInt(),
videoId = url!!.toID(), videoId = url!!.toID(),
title = title, title = title,
thumbnailUrl = thumbnail, thumbnailUrl = thumbnail?.let { ProxyHelper.unwrapUrl(it) },
uploader = uploaderName, uploader = uploaderName,
uploaderUrl = uploaderUrl, uploaderUrl = uploaderUrl,
uploaderAvatar = uploaderAvatar, uploaderAvatar = uploaderAvatar?.let { ProxyHelper.unwrapUrl(it) },
uploadDate = uploadedDate, uploadDate = uploadedDate,
duration = duration duration = duration
) )
@ -55,6 +58,17 @@ data class StreamItem(
views = views, views = views,
isShort = isShort isShort = isShort
) )
fun toWatchHistoryItem(videoId: String) = WatchHistoryItem(
videoId = videoId,
title = title,
uploadDate = uploaded.toLocalDate(),
uploader = uploaderName,
uploaderUrl = uploaderUrl?.toID(),
uploaderAvatar = uploaderAvatar?.let { ProxyHelper.unwrapUrl(it) },
thumbnailUrl = thumbnail?.let { ProxyHelper.unwrapUrl(it) },
duration = duration
)
companion object { companion object {
const val TYPE_STREAM = "stream" const val TYPE_STREAM = "stream"

View File

@ -4,7 +4,6 @@ import android.os.Parcelable
import com.github.libretube.db.obj.DownloadItem import com.github.libretube.db.obj.DownloadItem
import com.github.libretube.enums.FileType import com.github.libretube.enums.FileType
import com.github.libretube.extensions.toLocalDate import com.github.libretube.extensions.toLocalDate
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.json.SafeInstantSerializer import com.github.libretube.json.SafeInstantSerializer
import com.github.libretube.parcelable.DownloadData import com.github.libretube.parcelable.DownloadData
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
@ -12,7 +11,6 @@ import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.io.path.Path
@Serializable @Serializable
@Parcelize @Parcelize
@ -75,17 +73,8 @@ data class Streams(
} }
if (!subCode.isNullOrEmpty()) { if (!subCode.isNullOrEmpty()) {
items.add( val subtitle = subtitles.find { it.code == subCode }
DownloadItem( subtitle?.toDownloadItem(id)?.let { items.add(it) }
type = FileType.SUBTITLE,
videoId = id,
fileName = "${name}_$subCode.srt",
path = Path(""),
url = subtitles.find {
it.code == subCode
}?.url?.let { ProxyHelper.unwrapUrl(it) }
)
)
} }
return items return items

View File

@ -3,8 +3,12 @@ package com.github.libretube.api.obj
import android.content.Context import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.db.obj.DownloadItem
import com.github.libretube.enums.FileType
import com.github.libretube.helpers.ProxyHelper
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.io.path.Path
@Serializable @Serializable
@Parcelize @Parcelize
@ -20,4 +24,12 @@ data class Subtitle(
} else { } else {
"$name (${context.getString(R.string.auto_generated)})" "$name (${context.getString(R.string.auto_generated)})"
} }
fun toDownloadItem(videoId: String) = DownloadItem(
type = FileType.SUBTITLE,
videoId = videoId,
fileName = "${name}_${code}.srt",
path = Path(""),
url = url?.let { ProxyHelper.unwrapUrl(it) }
)
} }

View File

@ -1,14 +1,12 @@
package com.github.libretube.db package com.github.libretube.db
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Streams
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHolder.Database import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.db.obj.SearchHistoryItem import com.github.libretube.db.obj.SearchHistoryItem
import com.github.libretube.db.obj.WatchHistoryItem import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.enums.ContentFilter import com.github.libretube.enums.ContentFilter
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toLocalDate
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -17,26 +15,6 @@ import kotlinx.coroutines.withContext
object DatabaseHelper { object DatabaseHelper {
private const val MAX_SEARCH_HISTORY_SIZE = 20 private const val MAX_SEARCH_HISTORY_SIZE = 20
suspend fun addToWatchHistory(videoId: String, streams: Streams) = addToWatchHistory(
videoId,
streams.toStreamItem(videoId)
)
suspend fun addToWatchHistory(videoId: String, stream: StreamItem) {
val watchHistoryItem = WatchHistoryItem(
videoId,
stream.title,
stream.uploaded.toLocalDate(),
stream.uploaderName,
stream.uploaderUrl?.toID(),
stream.uploaderAvatar,
stream.thumbnail,
stream.duration
)
addToWatchHistory(watchHistoryItem)
}
suspend fun addToWatchHistory(watchHistoryItem: WatchHistoryItem) = suspend fun addToWatchHistory(watchHistoryItem: WatchHistoryItem) =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Database.watchHistoryDao().insert(watchHistoryItem) Database.watchHistoryDao().insert(watchHistoryItem)

View File

@ -4,7 +4,6 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.helpers.ProxyHelper
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -25,10 +24,10 @@ data class LocalPlaylistItem(
return StreamItem( return StreamItem(
url = videoId, url = videoId,
title = title, title = title,
thumbnail = ProxyHelper.rewriteUrl(thumbnailUrl), thumbnail = thumbnailUrl,
uploaderName = uploader, uploaderName = uploader,
uploaderUrl = uploaderUrl, uploaderUrl = uploaderUrl,
uploaderAvatar = ProxyHelper.rewriteUrl(uploaderAvatar), uploaderAvatar = uploaderAvatar,
uploadedDate = uploadDate, uploadedDate = uploadDate,
duration = duration duration = duration
) )

View File

@ -57,7 +57,7 @@ object DashHelper {
} }
// only unwraps the url if the preference is set in the settings // only unwraps the url if the preference is set in the settings
stream.url = ProxyHelper.unwrapStreamUrl(stream.url.orEmpty()) stream.url = ProxyHelper.rewriteUrlUsingProxyPreference(stream.url.orEmpty())
val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType }
if (adapSetInfo != null) { if (adapSetInfo != null) {
@ -83,7 +83,7 @@ object DashHelper {
} }
// only unwraps the url if the preference is set in the settings // only unwraps the url if the preference is set in the settings
stream.url = ProxyHelper.unwrapStreamUrl(stream.url.orEmpty()) stream.url = ProxyHelper.rewriteUrlUsingProxyPreference(stream.url.orEmpty())
adapSetInfos.add( adapSetInfos.add(
AdapSetInfo( AdapSetInfo(

View File

@ -87,7 +87,7 @@ object ImageHelper {
// only load the image if the data saver mode is disabled // only load the image if the data saver mode is disabled
if (DataSaverMode.isEnabled(target.context) || url.isNullOrEmpty()) return if (DataSaverMode.isEnabled(target.context) || url.isNullOrEmpty()) return
val urlToLoad = ProxyHelper.unwrapImageUrl(url) val urlToLoad = ProxyHelper.rewriteUrlUsingProxyPreference(url)
getImageWithCallback(target.context, urlToLoad) { result -> getImageWithCallback(target.context, urlToLoad) { result ->
// set the background to white for transparent images // set the background to white for transparent images

View File

@ -18,58 +18,50 @@ object ProxyHelper {
} }
} }
fun rewriteUrl(url: String?): String? { /**
* Decide whether the proxy should be used or not for a given stream URL based on user preferences
*/
fun rewriteUrlUsingProxyPreference(url: String): String {
if (PlayerHelper.disablePipedProxy) {
return unwrapUrl(url)
}
return proxyRewriteUrl(url) ?: url
}
/**
* Rewrite the URL to use the stored image proxy url of the selected instance.
* Can handle both Piped links and normal YouTube links.
*/
private fun proxyRewriteUrl(url: String?): String? {
if (url == null) return null if (url == null) return null
val proxyUrl = PreferenceHelper.getString(PreferenceKeys.IMAGE_PROXY_URL, "") val proxyUrl = PreferenceHelper.getString(PreferenceKeys.IMAGE_PROXY_URL, "")
.toHttpUrlOrNull() ?: return url .toHttpUrlOrNull()
val parsedUrl = url.toHttpUrlOrNull() ?: return url // parsedUrl should now be a plain YouTube URL without using any proxy
if (parsedUrl.queryParameter("host").isNullOrEmpty()) { val parsedUrl = unwrapUrl(url).toHttpUrlOrNull()
return parsedUrl.newBuilder() if (proxyUrl == null || parsedUrl == null) return null
.host(proxyUrl.host)
.port(proxyUrl.port)
.setQueryParameter("host", parsedUrl.host)
.build()
.toString()
}
return parsedUrl.newBuilder() return parsedUrl.newBuilder()
.host(proxyUrl.host) .host(proxyUrl.host)
.port(proxyUrl.port) .port(proxyUrl.port)
.setQueryParameter("host", parsedUrl.host)
.build() .build()
.toString() .toString()
} }
/**
* Detect whether the proxy should be used or not for a given stream URL based on user preferences
*/
fun unwrapStreamUrl(url: String): String {
return if (PlayerHelper.disablePipedProxy && !PlayerHelper.localStreamExtraction) {
unwrapUrl(url)
} else {
url
}
}
fun unwrapImageUrl(url: String): String {
return if (PlayerHelper.disablePipedProxy) {
unwrapUrl(url)
} else {
url
}
}
/** /**
* Convert a proxied Piped url to a YouTube url that's not proxied * Convert a proxied Piped url to a YouTube url that's not proxied
*
* Should not be called directly in most cases, use [rewriteUrlUsingProxyPreference] instead
*/ */
fun unwrapUrl(url: String, unwrap: Boolean = true): String { fun unwrapUrl(url: String): String {
val parsedUrl = url.toHttpUrlOrNull() val parsedUrl = url.toHttpUrlOrNull() ?: return url
val host = parsedUrl?.queryParameter("host") val host = parsedUrl.queryParameter("host")
// if there's no host parameter specified, there's no way to unwrap the URL // If the host is not set, the URL is probably already unwrapped
// and the proxied one must be used. That's the case if using LBRY. if (host.isNullOrEmpty()) {
if (!unwrap || parsedUrl == null || host.isNullOrEmpty()) {
return url return url
} }

View File

@ -10,7 +10,6 @@ import com.github.libretube.api.obj.StreamItem
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.LocalPlaylist import com.github.libretube.db.obj.LocalPlaylist
import com.github.libretube.extensions.parallelMap import com.github.libretube.extensions.parallelMap
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.PipedImportPlaylist import com.github.libretube.obj.PipedImportPlaylist
class LocalPlaylistsRepository: PlaylistRepository { class LocalPlaylistsRepository: PlaylistRepository {
@ -21,7 +20,7 @@ class LocalPlaylistsRepository: PlaylistRepository {
return Playlist( return Playlist(
name = relation.playlist.name, name = relation.playlist.name,
description = relation.playlist.description, description = relation.playlist.description,
thumbnailUrl = ProxyHelper.rewriteUrl(relation.playlist.thumbnailUrl), thumbnailUrl = relation.playlist.thumbnailUrl,
videos = relation.videos.size, videos = relation.videos.size,
relatedStreams = relation.videos.map { it.toStreamItem() } relatedStreams = relation.videos.map { it.toStreamItem() }
) )
@ -34,7 +33,7 @@ class LocalPlaylistsRepository: PlaylistRepository {
id = it.playlist.id.toString(), id = it.playlist.id.toString(),
name = it.playlist.name, name = it.playlist.name,
shortDescription = it.playlist.description, shortDescription = it.playlist.description,
thumbnail = ProxyHelper.rewriteUrl(it.playlist.thumbnailUrl), thumbnail = it.playlist.thumbnailUrl,
videos = it.videos.size.toLong() videos = it.videos.size.toLong()
) )
} }

View File

@ -214,7 +214,7 @@ class DownloadService : LifecycleService() {
setResumeNotification(notificationBuilder, item) setResumeNotification(notificationBuilder, item)
var totalRead = item.path.fileSize() var totalRead = item.path.fileSize()
val url = URL(ProxyHelper.unwrapStreamUrl(item.url ?: return)) val url = URL(ProxyHelper.rewriteUrlUsingProxyPreference(item.url ?: return))
// only fetch the content length if it's not been returned by the API // only fetch the content length if it's not been returned by the API
if (item.downloadSize <= 0L) { if (item.downloadSize <= 0L) {

View File

@ -90,7 +90,11 @@ open class OnlinePlayerService : AbstractPlayerService() {
// waiting for the player to be ready since the video can't be claimed to be watched // waiting for the player to be ready since the video can't be claimed to be watched
// while it did not yet start actually, but did buffer only so far // while it did not yet start actually, but did buffer only so far
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
streams?.let { DatabaseHelper.addToWatchHistory(videoId, it) } streams?.let { streams ->
val watchHistoryItem =
streams.toStreamItem(videoId).toWatchHistoryItem(videoId)
DatabaseHelper.addToWatchHistory(watchHistoryItem)
}
} }
} }
} }
@ -241,7 +245,8 @@ open class OnlinePlayerService : AbstractPlayerService() {
if (args.containsKey(PlayerCommand.SET_SB_AUTO_SKIP_ENABLED.name)) { if (args.containsKey(PlayerCommand.SET_SB_AUTO_SKIP_ENABLED.name)) {
sponsorBlockAutoSkip = args.getBoolean(PlayerCommand.SET_SB_AUTO_SKIP_ENABLED.name) sponsorBlockAutoSkip = args.getBoolean(PlayerCommand.SET_SB_AUTO_SKIP_ENABLED.name)
} else if (args.containsKey(PlayerCommand.SET_AUTOPLAY_COUNTDOWN_ENABLED.name)) { } else if (args.containsKey(PlayerCommand.SET_AUTOPLAY_COUNTDOWN_ENABLED.name)) {
autoPlayCountdownEnabled = args.getBoolean(PlayerCommand.SET_AUTOPLAY_COUNTDOWN_ENABLED.name) autoPlayCountdownEnabled =
args.getBoolean(PlayerCommand.SET_AUTOPLAY_COUNTDOWN_ENABLED.name)
} }
} }
@ -272,7 +277,7 @@ open class OnlinePlayerService : AbstractPlayerService() {
// only use the dash manifest generated by YT if either it's a livestream or no other source is available // only use the dash manifest generated by YT if either it's a livestream or no other source is available
val dashUri = val dashUri =
if (streams.isLive && streams.dash != null) { if (streams.isLive && streams.dash != null) {
ProxyHelper.unwrapStreamUrl( ProxyHelper.rewriteUrlUsingProxyPreference(
streams.dash streams.dash
).toUri() ).toUri()
} else { } else {
@ -289,7 +294,7 @@ open class OnlinePlayerService : AbstractPlayerService() {
.setPlaylistParserFactory(YoutubeHlsPlaylistParser.Factory()) .setPlaylistParserFactory(YoutubeHlsPlaylistParser.Factory())
val mediaItem = createMediaItem( val mediaItem = createMediaItem(
ProxyHelper.unwrapStreamUrl(streams.hls).toUri(), ProxyHelper.rewriteUrlUsingProxyPreference(streams.hls).toUri(),
MimeTypes.APPLICATION_M3U8, MimeTypes.APPLICATION_M3U8,
streams streams
) )

View File

@ -29,7 +29,6 @@ import com.github.libretube.extensions.ceilHalf
import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.dpToPx
import com.github.libretube.helpers.NavBarHelper import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter
import com.github.libretube.ui.adapters.PlaylistsAdapter import com.github.libretube.ui.adapters.PlaylistsAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment import com.github.libretube.ui.base.DynamicLayoutManagerFragment
@ -146,9 +145,7 @@ class LibraryFragment : DynamicLayoutManagerFragment() {
private fun initBookmarks() { private fun initBookmarks() {
lifecycleScope.launch { lifecycleScope.launch {
val bookmarks = withContext(Dispatchers.IO) { val bookmarks = withContext(Dispatchers.IO) {
DatabaseHolder.Database.playlistBookmarkDao().getAll().map { DatabaseHolder.Database.playlistBookmarkDao().getAll()
it.copy(thumbnailUrl = ProxyHelper.rewriteUrl(it.thumbnailUrl))
}
} }
val binding = _binding ?: return@launch val binding = _binding ?: return@launch

View File

@ -683,7 +683,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
val context = requireContext() val context = requireContext()
lifecycleScope.launch { lifecycleScope.launch {
val hlsStream = withContext(Dispatchers.IO) { val hlsStream = withContext(Dispatchers.IO) {
ProxyHelper.unwrapStreamUrl(streams.hls!!).toUri() ProxyHelper.rewriteUrlUsingProxyPreference(streams.hls!!).toUri()
} }
IntentHelper.openWithExternalPlayer( IntentHelper.openWithExternalPlayer(
context, context,

View File

@ -29,7 +29,6 @@ import com.github.libretube.extensions.setOnDismissListener
import com.github.libretube.helpers.NavBarHelper import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.NavigationHelper import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.ui.adapters.WatchHistoryAdapter import com.github.libretube.ui.adapters.WatchHistoryAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.addOnBottomReachedListener import com.github.libretube.ui.extensions.addOnBottomReachedListener
@ -168,12 +167,6 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
private fun showWatchHistory(history: List<WatchHistoryItem>) { private fun showWatchHistory(history: List<WatchHistoryItem>) {
val watchHistory = history.filterByStatusAndWatchPosition() val watchHistory = history.filterByStatusAndWatchPosition()
watchHistory.forEach {
it.thumbnailUrl = ProxyHelper.rewriteUrl(it.thumbnailUrl)
it.uploaderAvatar = ProxyHelper.rewriteUrl(it.uploaderAvatar)
}
val watchHistoryAdapter = WatchHistoryAdapter(watchHistory.toMutableList()) val watchHistoryAdapter = WatchHistoryAdapter(watchHistory.toMutableList())
binding.playAll.setOnClickListener { binding.playAll.setOnClickListener {

View File

@ -103,7 +103,7 @@ class VideoOptionsBottomSheet : BaseBottomSheet() {
DatabaseHolder.Database.watchPositionDao().insert(watchPosition) DatabaseHolder.Database.watchPositionDao().insert(watchPosition)
if (!PlayerHelper.watchHistoryEnabled) return@withContext if (!PlayerHelper.watchHistoryEnabled) return@withContext
// add video to watch history // add video to watch history
DatabaseHelper.addToWatchHistory(videoId, streamItem) DatabaseHelper.addToWatchHistory(streamItem.toWatchHistoryItem(videoId))
} }
if (PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)) { if (PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)) {
// get the host fragment containing the current fragment // get the host fragment containing the current fragment