diff --git a/src/renderer/components/FtListVideo/FtListVideo.vue b/src/renderer/components/FtListVideo/FtListVideo.vue index ebbf6b89ed71d..b56268ebe2ae8 100644 --- a/src/renderer/components/FtListVideo/FtListVideo.vue +++ b/src/renderer/components/FtListVideo/FtListVideo.vue @@ -474,6 +474,12 @@ const progressPercentage = computed(() => { /** @type {import('vue').ComputedRef} */ const hiddenChannels = computed(() => JSON.parse(store.getters.getChannelsHidden)) +/** @type {import('vue').ComputedRef} */ +const useSponsorBlock = computed(() => store.getters.getUseSponsorBlock) + +/** @type {import('vue').ComputedRef} */ +const sponsorBlockExcludedChannels = computed(() => JSON.parse(store.getters.getSponsorBlockExcludedChannels)) + const playlistSharable = computed(() => { // `playlistId` can be undefined // User playlist ID should not be shared @@ -583,24 +589,46 @@ const dropdownOptions = computed(() => { } } - if (channelId.value !== null && !inSubscriptions.value) { - const channelShouldBeHidden = hiddenChannels.value.some(c => c.name === channelId.value) + if (channelId.value !== null) { + if (!inSubscriptions.value) { + const channelShouldBeHidden = hiddenChannels.value.some(c => c.name === channelId.value) - options.push( - { - type: 'divider' - }, + options.push( + { + type: 'divider' + }, - channelShouldBeHidden - ? { - label: t('Video.Unhide Channel'), - value: 'unhideChannel' - } - : { - label: t('Video.Hide Channel'), - value: 'hideChannel' - } - ) + channelShouldBeHidden + ? { + label: t('Video.Unhide Channel'), + value: 'unhideChannel' + } + : { + label: t('Video.Hide Channel'), + value: 'hideChannel' + } + ) + } + + if (useSponsorBlock.value) { + const isSponsorBlockChannelExcluded = sponsorBlockExcludedChannels.value.some(c => c.name === channelId.value) + + options.push( + { + type: 'divider' + }, + + isSponsorBlockChannelExcluded + ? { + label: t('Video.Enable SponsorBlock on Channel'), + value: 'enableSponsorBlockOnChannel' + } + : { + label: t('Video.Disable SponsorBlock on Channel'), + value: 'disableSponsorBlockOnChannel' + } + ) + } } return options @@ -698,6 +726,12 @@ function handleOptionsClick(option) { case 'unhideChannel': unhideChannel(channelName.value, channelId.value) break + case 'disableSponsorBlockOnChannel': + disableSponsorBlockOnChannel(channelName.value, channelId.value) + break + case 'enableSponsorBlockOnChannel': + enableSponsorBlockOnChannel(channelName.value, channelId.value) + break } } @@ -1133,6 +1167,28 @@ function unhideChannel(channelName, channelId) { showToast(t('Channel Unhidden', { channel: channelName })) } +/** + * @param {string} channelName + * @param {string} channelId + */ +function disableSponsorBlockOnChannel(channelName, channelId) { + const newExcludedChannels = [...sponsorBlockExcludedChannels.value, { name: channelId, preferredName: channelName }] + + store.dispatch('updateSponsorBlockExcludedChannels', JSON.stringify(newExcludedChannels)) + + showToast(t('SponsorBlock Disabled on Channel', { channel: channelName })) +} + +/** + * @param {string} channelName + * @param {string} channelId + */ +function enableSponsorBlockOnChannel(channelName, channelId) { + store.dispatch('updateSponsorBlockExcludedChannels', JSON.stringify(sponsorBlockExcludedChannels.value.filter(c => c.name !== channelId))) + + showToast(t('SponsorBlock Enabled on Channel', { channel: channelName })) +} + function toggleQuickBookmarked() { if (!isQuickBookmarkEnabled.value) { // This should be prevented by UI diff --git a/src/renderer/components/SponsorBlockSettings.vue b/src/renderer/components/SponsorBlockSettings.vue index c01b7f168a287..8fd392b805699 100644 --- a/src/renderer/components/SponsorBlockSettings.vue +++ b/src/renderer/components/SponsorBlockSettings.vue @@ -44,6 +44,25 @@ @blur="handleUpdateSponsorBlockUrl" /> + + + @@ -72,16 +91,23 @@ diff --git a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js index acf452532b528..ce0c632e0bef0 100644 --- a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js +++ b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js @@ -115,6 +115,10 @@ export default defineComponent({ type: String, default: '' }, + channelId: { + type: String, + default: '' + }, title: { type: String, default: '' @@ -375,11 +379,21 @@ export default defineComponent({ return store.getters.getVideoSkipMouseScroll }) + /** @type {import('vue').ComputedRef} */ + const sponsorBlockExcludedChannels = computed(() => { + return JSON.parse(store.getters.getSponsorBlockExcludedChannels) + }) + /** @type {import('vue').ComputedRef} */ const useSponsorBlock = computed(() => { return store.getters.getUseSponsorBlock }) + /** @type {import('vue').ComputedRef} */ + const shouldSkipSponsorBlockSegmentsOnChannel = computed(() => { + return useSponsorBlock.value && !sponsorBlockExcludedChannels.value.some(c => c.name === props.channelId) + }) + /** @type {import('vue').ComputedRef} */ const sponsorBlockShowSkippedToast = computed(() => { return store.getters.getSponsorBlockShowSkippedToast @@ -516,6 +530,10 @@ export default defineComponent({ * @param {number} currentTime */ function skipSponsorBlockSegments(currentTime) { + if (!shouldSkipSponsorBlockSegmentsOnChannel.value) { + return + } + const { autoSkip } = sponsorSkips.value if (autoSkip.size === 0) { diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js index 694be8ff0be32..948e76247765d 100644 --- a/src/renderer/store/modules/settings.js +++ b/src/renderer/store/modules/settings.js @@ -307,6 +307,8 @@ const state = { useDeArrowTitles: false, useDeArrowThumbnails: false, deArrowThumbnailGeneratorUrl: 'https://dearrow-thumb.ajay.app', + sponsorBlockExcludedChannels: '[]', + sponsorBlockShowExcludedChannels: true, // This makes the `favorites` playlist uses as quick bookmark target // If the playlist is removed quick bookmark is disabled quickBookmarkTargetPlaylistId: 'favorites', diff --git a/src/renderer/views/Watch/Watch.vue b/src/renderer/views/Watch/Watch.vue index 4b127bacd9962..ff3fa6359e2e8 100644 --- a/src/renderer/views/Watch/Watch.vue +++ b/src/renderer/views/Watch/Watch.vue @@ -29,6 +29,7 @@ :format="activeFormat" :thumbnail="thumbnail" :video-id="videoId" + :channel-id="channelId" :chapters="videoChapters" :current-chapter-index="videoCurrentChapterIndex" :chapters-src="chaptersSrc" diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml index 6dafdfcf6bbb3..2aaff1e0e90a2 100644 --- a/static/locales/en-US.yaml +++ b/static/locales/en-US.yaml @@ -682,6 +682,11 @@ Settings: Prompt To Skip: Prompt To Skip Do Nothing: Do Nothing Category Color: Category Color + Excluded Channels: + Disabled Message: Some channels were excluded using ID and weren't processed. Feature is blocked while those IDs are updating + Excluded Channels: Excluded Channels + Tooltip: Enter a channel ID to have SponsorBlock disabled. Sponsor segments in videos from this channel won't be skipped. + The channel ID entered must be a complete match and is case sensitive. Parental Control Settings: Parental Control Settings: Parental Control Hide Unsubscribe Button: Hide Unsubscribe Button @@ -870,6 +875,8 @@ Video: Copy Invidious Channel Link: Copy Invidious Channel Link Hide Channel: Hide Channel Unhide Channel: Show Channel + Enable SponsorBlock on Channel: Enable SponsorBlock on Channel + Disable SponsorBlock on Channel: Disable SponsorBlock on Channel Views: Views Loop Playlist: Loop Playlist Shuffle Playlist: Shuffle Playlist @@ -1144,6 +1151,8 @@ Screenshot Error: Screenshot failed. {error} Screenshot Clipboard Error: Screenshot copy to clipboard failed Channel Hidden: '{channel} added to channel filter' Channel Unhidden: '{channel} removed from channel filter' +SponsorBlock Disabled on Channel: 'SponsorBlock disabled on {channel}' +SponsorBlock Enabled on Channel: 'SponsorBlock enabled on {channel}' Trimmed input must be at least N characters long: Trimmed input must be at least 1 character long | Trimmed input must be at least {length} characters long Tag already exists: '"{tagName}" tag already exists'