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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
compact
@change="updateUnsubscriptionPopupStatus"
/>
<FtToggleSwitch
:label="$t('Settings.Subscription Settings.Limit Request Fallback Without RSS')"
:default-value="limitRequestFallbackWithoutRss"
:tooltip="$t('Tooltips.Subscription Settings.Limit Request Fallback Without RSS')"
compact
@change="updateLimitRequestFallbackWithoutRss"
/>
</div>
<div class="switchColumn">
<FtToggleSwitch
Expand Down Expand Up @@ -87,6 +94,16 @@ function updateUnsubscriptionPopupStatus(value) {
store.dispatch('updateUnsubscriptionPopupStatus', value)
}

/** @type {import('vue').ComputedRef<boolean>} */
const limitRequestFallbackWithoutRss = computed(() => store.getters.getLimitRequestFallbackWithoutRss)

/**
* @param {boolean} value
*/
function updateLimitRequestFallbackWithoutRss(value) {
store.dispatch('updateLimitRequestFallbackWithoutRss', value)
}

/** @type {import('vue').ComputedRef<boolean>} */
const onlyShowLatestFromChannel = computed(() => store.getters.getOnlyShowLatestFromChannel)

Expand Down
47 changes: 37 additions & 10 deletions src/renderer/components/SubscriptionsLive.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const subscriptionCacheReady = computed(() => store.getters.getSubscriptionCache
/** @type {import('vue').ComputedRef<boolean>} */
const useRssFeeds = computed(() => store.getters.getUseRssFeeds)

/** @type {import('vue').ComputedRef<boolean>} */
const limitRequestFallbackWithoutRss = computed(() => store.getters.getLimitRequestFallbackWithoutRss)

/** @type {import('vue').ComputedRef<boolean>} */
const fetchSubscriptionsAutomatically = computed(() => store.getters.getFetchSubscriptionsAutomatically)

Expand Down Expand Up @@ -185,23 +188,32 @@ async function loadVideosForSubscriptionsFromRemote() {
let channelCount = 0
isLoading.value = true

let useRss = useRssFeeds.value
if (channelsToLoadFromRemote.length >= 125 && !useRss) {
showToast(
t('Subscriptions["This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting"]'),
10000
)
useRss = true
}
const useRss = limitRequestFallbackWithoutRss.value
? useRssFeeds.value
: (() => {
let rss = useRssFeeds.value
if (channelsToLoadFromRemote.length >= 125 && !rss) {
showToast(
t('Subscriptions["This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting"]'),
10000
)
rss = true
}
return rss
})()

const CHUNK_SIZE = 80
const CHUNK_DELAY_MS = 2000

store.commit('setShowProgressBar', true)
store.commit('setProgressBarPercentage', 0)
attemptedFetch.value = true

errorChannels.value = []
const subscriptionUpdates = []
const videoListFromRemote = []

const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
const processChannel = async (channel) => {
let videos, name, thumbnailUrl

if (!process.env.SUPPORTS_LOCAL_API || backendPreference.value === 'invidious') {
Expand Down Expand Up @@ -238,7 +250,22 @@ async function loadVideosForSubscriptionsFromRemote() {
}

return videos ?? []
}))).flat()
}

if (limitRequestFallbackWithoutRss.value && !useRss) {
for (let i = 0; i < channelsToLoadFromRemote.length; i += CHUNK_SIZE) {
if (i > 0) {
await new Promise(resolve => setTimeout(resolve, CHUNK_DELAY_MS))
}

const chunk = channelsToLoadFromRemote.slice(i, i + CHUNK_SIZE)
const chunkResults = await Promise.all(chunk.map(processChannel))
videoListFromRemote.push(...chunkResults.flat())
}
} else {
const results = await Promise.all(channelsToLoadFromRemote.map(processChannel))
videoListFromRemote.push(...results.flat())
}

videoList.value = updateVideoListAfterProcessing(videoListFromRemote)
isLoading.value = false
Expand Down
47 changes: 37 additions & 10 deletions src/renderer/components/SubscriptionsVideos.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const subscriptionCacheReady = computed(() => store.getters.getSubscriptionCache
/** @type {import('vue').ComputedRef<boolean>} */
const useRssFeeds = computed(() => store.getters.getUseRssFeeds)

/** @type {import('vue').ComputedRef<boolean>} */
const limitRequestFallbackWithoutRss = computed(() => store.getters.getLimitRequestFallbackWithoutRss)

/** @type {import('vue').ComputedRef<boolean>} */
const fetchSubscriptionsAutomatically = computed(() => store.getters.getFetchSubscriptionsAutomatically)

Expand Down Expand Up @@ -184,23 +187,32 @@ async function loadVideosForSubscriptionsFromRemote() {
let channelCount = 0
isLoading.value = true

let useRss = useRssFeeds.value
if (channelsToLoadFromRemote.length >= 125 && !useRss) {
showToast(
t('Subscriptions["This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting"]'),
10000
)
useRss = true
}
const useRss = limitRequestFallbackWithoutRss.value
? useRssFeeds.value
: (() => {
let rss = useRssFeeds.value
if (channelsToLoadFromRemote.length >= 125 && !rss) {
showToast(
t('Subscriptions["This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting"]'),
10000
)
rss = true
}
return rss
})()

const CHUNK_SIZE = 80
const CHUNK_DELAY_MS = 2000

store.commit('setShowProgressBar', true)
store.commit('setProgressBarPercentage', 0)
attemptedFetch.value = true

errorChannels.value = []
const subscriptionUpdates = []
const videoListFromRemote = []

const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
const processChannel = async (channel) => {
let videos, name, thumbnailUrl

if (!process.env.SUPPORTS_LOCAL_API || backendPreference.value === 'invidious') {
Expand Down Expand Up @@ -237,7 +249,22 @@ async function loadVideosForSubscriptionsFromRemote() {
}

return videos ?? []
}))).flat()
}

if (limitRequestFallbackWithoutRss.value && !useRss) {
for (let i = 0; i < channelsToLoadFromRemote.length; i += CHUNK_SIZE) {
if (i > 0) {
await new Promise(resolve => setTimeout(resolve, CHUNK_DELAY_MS))
}

const chunk = channelsToLoadFromRemote.slice(i, i + CHUNK_SIZE)
const chunkResults = await Promise.all(chunk.map(processChannel))
videoListFromRemote.push(...chunkResults.flat())
}
} else {
const results = await Promise.all(channelsToLoadFromRemote.map(processChannel))
videoListFromRemote.push(...results.flat())
}

videoList.value = updateVideoListAfterProcessing(videoListFromRemote)
isLoading.value = false
Expand Down
1 change: 1 addition & 0 deletions src/renderer/store/modules/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ const state = {
useProxy: false,
userPlaylistSortOrder: 'date_added_descending',
useRssFeeds: false,
limitRequestFallbackWithoutRss: false,
useSponsorBlock: false,
videoVolumeMouseScroll: false,
videoPlaybackRateMouseScroll: false,
Expand Down
7 changes: 6 additions & 1 deletion src/renderer/views/Subscriptions/Subscriptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ const useRssFeeds = computed(() => {
return store.getters.getUseRssFeeds
})

/** @type {import('vue').ComputedRef<boolean>} */
const limitRequestFallbackWithoutRss = computed(() => {
return store.getters.getLimitRequestFallbackWithoutRss
})

/** @type {import('vue').Ref<'videos' | 'shorts' | 'live' | 'community' | null>} */
const currentTab = ref('videos')

Expand Down Expand Up @@ -196,7 +201,7 @@ const visibleTabs = computed(() => {
}

// community does not support rss
if (!hideSubscriptionsCommunity.value && !useRssFeeds.value && activeSubscriptionList.value.length < 125) {
if (!hideSubscriptionsCommunity.value && !useRssFeeds.value && (limitRequestFallbackWithoutRss.value || activeSubscriptionList.value.length < 125)) {
tabs.push('community')
}

Expand Down
5 changes: 5 additions & 0 deletions static/locales/en-US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ Settings:
'Limit the number of videos displayed for each channel': 'Limit the number of videos displayed for each channel'
To: To
Confirm Before Unsubscribing: Confirm Before Unsubscribing
Limit Request Fallback Without RSS: Limit Request Fallback Without RSS
Distraction Free Settings:
Distraction Free Settings: Distraction Free
Sections:
Expand Down Expand Up @@ -1086,6 +1087,10 @@ Tooltips:
but doesn't provide certain information like video duration, live status or posts
Fetch Automatically: When enabled, FreeTube will automatically fetch
your subscription feed on startup and when a new window is opened.
Limit Request Fallback Without RSS: When enabled, FreeTube will split
subscription requests into smaller chunks instead of forcing RSS for large
subscription lists. This prevents rate limiting while preserving video duration,
live status and other metadata that RSS does not provide
Experimental Settings:
Replace HTTP Cache: Disables Electron's disk based HTTP cache and enables a custom in-memory image cache. Will lead to increased RAM usage.
SponsorBlock Settings:
Expand Down