diff --git a/.github/nightly-notes/2026-10-24-fix-subscription-shorts-rss-link.md b/.github/nightly-notes/2026-10-24-fix-subscription-shorts-rss-link.md new file mode 100644 index 0000000000..cb49140cf5 --- /dev/null +++ b/.github/nightly-notes/2026-10-24-fix-subscription-shorts-rss-link.md @@ -0,0 +1,3 @@ +## Fixed +- Shorts no longer show up in Subscriptions when Subscription shorts is turned off (#1393) +- Shorts no longer slip through as new video notifications when Shorts notifications are off diff --git a/app/src/main/java/io/github/aedev/flow/data/innertube/RssSubscriptionService.kt b/app/src/main/java/io/github/aedev/flow/data/innertube/RssSubscriptionService.kt index 3289fac57b..7e5bb0d123 100644 --- a/app/src/main/java/io/github/aedev/flow/data/innertube/RssSubscriptionService.kt +++ b/app/src/main/java/io/github/aedev/flow/data/innertube/RssSubscriptionService.kt @@ -6,6 +6,7 @@ import io.github.aedev.flow.data.shorts.ChannelReelIndex import io.github.aedev.flow.data.subscriptions.ChannelRssClient import io.github.aedev.flow.data.subscriptions.ChannelRssEntry import io.github.aedev.flow.data.subscriptions.ChannelUploadsClient +import io.github.aedev.flow.data.subscriptions.classifiedFromVerdicts import io.github.aedev.flow.utils.ThumbnailUrlResolver import io.github.aedev.flow.utils.formatYouTubeRelativeTime import io.github.aedev.flow.utils.parsePremiereTimestamp @@ -59,15 +60,20 @@ data class SubscriptionFeedChunk( * on its own. No later chunk changes their slice, so the caller can store them right away. */ val rssSettledChannelIds: Set? = null, + /** + * Every upload this sweep knows is a reel, including the older ones the feed leaves out (it keeps + * a channel's newest reel only). A cached row of one of them is filed as a Short again. + */ + val reelIds: Set = emptySet(), ) /** * Two-stage subscription extraction. * * The RSS stage reads every channel's feed: cheap, and the only source with a trustworthy publish - * timestamp. RSS lists Shorts alongside ordinary uploads without marking either, so an entry is only - * shown once [ChannelReelIndex] or a stored verdict says what it is; a channel whose verdict is not - * at hand is held back for the checks stage rather than shown with its reels as videos (#903). + * timestamp. Each entry's link says whether it is a reel (#1393); an entry without one is only shown + * once [ChannelReelIndex] or a stored verdict says what it is, and a channel whose verdict is not at + * hand is held back for the checks stage rather than shown with its reels as videos (#903). * * The checks stage asks the Shorts tab for those held channels, then reads the channel tabs for the * channels RSS could not satisfy, which is also the only way to see livestreams — their upload dates @@ -111,7 +117,7 @@ class RssSubscriptionService .map { channelId -> async(Dispatchers.IO) { val result = fetchRssVideos(channelId, minimumDateMillis, knownVideoIds) - Triple(channelId, result, knownReels(channelId, result.videos, storedReelVerdicts)) + Triple(channelId, result, knownReels(channelId, result, storedReelVerdicts)) } }.awaitAll() } @@ -241,6 +247,7 @@ class RssSubscriptionService private inner class Sweep { val regular = mutableListOf