Feat: SponsorBlock improvements (#1849)
* Add setting for each SB category * Update SB Settings Component * Show other SB categories in seek bar * Use camelCase for SponsorBlock values Co-authored-by: PikachuEXE <pikachuexe@gmail.com> * change "Don't Skip" to "Do Nothing" in locale * improve styling of sponsorblock settings * add filler category, don't repeat colors * Fix JS format issue caused during code conflict resolving * make sponsor block markers transparent * change opacity to 0.6 Co-authored-by: PikachuEXE <pikachuexe@gmail.com>
This commit is contained in:
parent
052a1fb65c
commit
dfb28b1d69
|
@ -0,0 +1,142 @@
|
|||
import Vue from 'vue'
|
||||
import { mapActions } from 'vuex'
|
||||
import FtSelect from '../ft-select/ft-select.vue'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'FtSponsorBlockCategory',
|
||||
components: {
|
||||
'ft-select': FtSelect
|
||||
},
|
||||
props: {
|
||||
categoryName: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
categoryColor: '',
|
||||
skipOption: '',
|
||||
skipValues: [
|
||||
'autoSkip',
|
||||
// 'promptToSkip',
|
||||
'showInSeekBar',
|
||||
'doNothing'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
colorValues: function () {
|
||||
return this.$store.getters.getColorNames
|
||||
},
|
||||
|
||||
colorNames: function () {
|
||||
return this.colorValues.map(colorVal => {
|
||||
// add spaces before capital letters
|
||||
const colorName = colorVal.replace(/([A-Z])/g, ' $1').trim()
|
||||
return this.$t(`Settings.Theme Settings.Main Color Theme.${colorName}`)
|
||||
})
|
||||
},
|
||||
|
||||
sponsorBlockValues: function() {
|
||||
let sponsorVal = ''
|
||||
switch (this.categoryName.toLowerCase()) {
|
||||
case 'sponsor':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockSponsor
|
||||
break
|
||||
case 'self-promotion':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockSelfPromo
|
||||
break
|
||||
case 'interaction':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockInteraction
|
||||
break
|
||||
case 'intro':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockIntro
|
||||
break
|
||||
case 'outro':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockOutro
|
||||
break
|
||||
case 'recap':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockRecap
|
||||
break
|
||||
case 'music offtopic':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockMusicOffTopic
|
||||
break
|
||||
case 'filler':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockFiller
|
||||
break
|
||||
}
|
||||
return sponsorVal
|
||||
},
|
||||
|
||||
skipNames: function() {
|
||||
return [
|
||||
this.$t('Settings.SponsorBlock Settings.Skip Options.Auto Skip'),
|
||||
// this.$t('Settings.SponsorBlock Settings.Skip Options.Prompt To Skip'),
|
||||
this.$t('Settings.SponsorBlock Settings.Skip Options.Show In Seek Bar'),
|
||||
this.$t('Settings.SponsorBlock Settings.Skip Options.Do Nothing')
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateColor: function (color) {
|
||||
const payload = {
|
||||
color: color,
|
||||
skip: this.sponsorBlockValues.skip
|
||||
}
|
||||
this.updateSponsorCategory(payload)
|
||||
},
|
||||
|
||||
updateSkipOption: function (skipOption) {
|
||||
const payload = {
|
||||
color: this.sponsorBlockValues.color,
|
||||
skip: skipOption
|
||||
}
|
||||
|
||||
this.updateSponsorCategory(payload)
|
||||
},
|
||||
|
||||
updateSponsorCategory: function (payload) {
|
||||
switch (this.categoryName.toLowerCase()) {
|
||||
case 'sponsor':
|
||||
this.updateSponsorBlockSponsor(payload)
|
||||
break
|
||||
case 'self-promotion':
|
||||
this.updateSponsorBlockSelfPromo(payload)
|
||||
break
|
||||
case 'interaction':
|
||||
this.updateSponsorBlockInteraction(payload)
|
||||
break
|
||||
case 'intro':
|
||||
this.updateSponsorBlockIntro(payload)
|
||||
break
|
||||
case 'outro':
|
||||
this.updateSponsorBlockOutro(payload)
|
||||
break
|
||||
case 'recap':
|
||||
this.updateSponsorBlockRecap(payload)
|
||||
break
|
||||
case 'music offtopic':
|
||||
this.updateSponsorBlockMusicOffTopic(payload)
|
||||
break
|
||||
case 'filler':
|
||||
this.updateSponsorBlockFiller(payload)
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'showToast',
|
||||
'openExternalLink',
|
||||
'updateSponsorBlockSponsor',
|
||||
'updateSponsorBlockSelfPromo',
|
||||
'updateSponsorBlockInteraction',
|
||||
'updateSponsorBlockIntro',
|
||||
'updateSponsorBlockOutro',
|
||||
'updateSponsorBlockRecap',
|
||||
'updateSponsorBlockMusicOffTopic',
|
||||
'updateSponsorBlockFiller'
|
||||
])
|
||||
}
|
||||
})
|
|
@ -0,0 +1,6 @@
|
|||
@use "../../sass-partials/settings"
|
||||
.sponsorBlockCategory
|
||||
margin-top: 30px
|
||||
padding: 0 10px
|
||||
.sponsorTitle
|
||||
font-size: x-large
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<div class="sponsorBlockCategory">
|
||||
<div class="sponsorTitle">
|
||||
{{ $t("Video.Sponsor Block category."+categoryName) }}
|
||||
</div>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.SponsorBlock Settings.Category Color')"
|
||||
:value="sponsorBlockValues.color"
|
||||
:select-names="colorNames"
|
||||
:select-values="colorValues"
|
||||
@change="updateColor"
|
||||
/>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.SponsorBlock Settings.Skip Options.Skip Option')"
|
||||
:value="sponsorBlockValues.skip"
|
||||
:select-names="skipNames"
|
||||
:select-values="skipValues"
|
||||
@change="updateSkipOption"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script src="./ft-sponsor-block-category.js" />
|
||||
<style scoped lang="sass" src="./ft-sponsor-block-category.sass" />
|
|
@ -188,6 +188,62 @@ export default Vue.extend({
|
|||
return this.$store.getters.getDisplayVideoPlayButton
|
||||
},
|
||||
|
||||
sponsorSkips: function () {
|
||||
const sponsorCats = ['sponsor',
|
||||
'selfpromo',
|
||||
'interaction',
|
||||
'intro',
|
||||
'outro',
|
||||
'preview',
|
||||
'music_offtopic',
|
||||
'filler'
|
||||
]
|
||||
const autoSkip = {}
|
||||
const seekBar = []
|
||||
const promptSkip = {}
|
||||
const categoryData = {}
|
||||
sponsorCats.forEach(x => {
|
||||
let sponsorVal = {}
|
||||
switch (x) {
|
||||
case 'sponsor':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockSponsor
|
||||
break
|
||||
case 'selfpromo':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockSelfPromo
|
||||
break
|
||||
case 'interaction':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockInteraction
|
||||
break
|
||||
case 'intro':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockIntro
|
||||
break
|
||||
case 'outro':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockOutro
|
||||
break
|
||||
case 'preview':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockRecap
|
||||
break
|
||||
case 'music_offtopic':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockMusicOffTopic
|
||||
break
|
||||
case 'filler':
|
||||
sponsorVal = this.$store.getters.getSponsorBlockFiller
|
||||
break
|
||||
}
|
||||
if (sponsorVal.skip !== 'doNothing') {
|
||||
seekBar.push(x)
|
||||
}
|
||||
if (sponsorVal.skip === 'autoSkip') {
|
||||
autoSkip[x] = true
|
||||
}
|
||||
if (sponsorVal.skip === 'promptToSkip') {
|
||||
promptSkip[x] = true
|
||||
}
|
||||
categoryData[x] = sponsorVal
|
||||
})
|
||||
return { autoSkip, seekBar, promptSkip, categoryData }
|
||||
},
|
||||
|
||||
maxVideoPlaybackRate: function () {
|
||||
return parseInt(this.$store.getters.getMaxVideoPlaybackRate)
|
||||
},
|
||||
|
@ -459,7 +515,7 @@ export default Vue.extend({
|
|||
initializeSponsorBlock() {
|
||||
this.sponsorBlockSkipSegments({
|
||||
videoId: this.videoId,
|
||||
categories: ['sponsor']
|
||||
categories: this.sponsorSkips.seekBar
|
||||
}).then((skipSegments) => {
|
||||
if (skipSegments.length === 0) {
|
||||
return
|
||||
|
@ -477,7 +533,8 @@ export default Vue.extend({
|
|||
this.addSponsorBlockMarker({
|
||||
time: startTime,
|
||||
duration: endTime - startTime,
|
||||
color: this.sponsorBlockCategoryColor(category)
|
||||
color: 'var(--primary-color)',
|
||||
category: category
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -496,10 +553,12 @@ export default Vue.extend({
|
|||
}
|
||||
})
|
||||
if (newTime !== null && Math.abs(duration - currentTime) > 0.500) {
|
||||
if (this.sponsorBlockShowSkippedToast) {
|
||||
this.showSkippedSponsorSegmentInformation(skippedCategory)
|
||||
if (this.sponsorSkips.autoSkip[skippedCategory]) {
|
||||
if (this.sponsorBlockShowSkippedToast) {
|
||||
this.showSkippedSponsorSegmentInformation(skippedCategory)
|
||||
}
|
||||
this.player.currentTime(newTime)
|
||||
}
|
||||
this.player.currentTime(newTime)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -524,42 +583,25 @@ export default Vue.extend({
|
|||
return this.$t('Video.Sponsor Block category.interaction')
|
||||
case 'music_offtopic':
|
||||
return this.$t('Video.Sponsor Block category.music offtopic')
|
||||
case 'filler':
|
||||
return this.$t('Video.Sponsor Block category.filler')
|
||||
default:
|
||||
console.error(`Unknown translation for SponsorBlock category ${category}`)
|
||||
return category
|
||||
}
|
||||
},
|
||||
|
||||
sponsorBlockCategoryColor(category) {
|
||||
// TODO: allow to set these colors in settings
|
||||
switch (category) {
|
||||
case 'sponsor':
|
||||
return 'var(--accent-color)'
|
||||
case 'intro':
|
||||
return 'var(--accent-color)'
|
||||
case 'outro':
|
||||
return 'var(--accent-color)'
|
||||
case 'selfpromo':
|
||||
return 'var(--accent-color)'
|
||||
case 'interaction':
|
||||
return 'var(--accent-color)'
|
||||
case 'music_offtopic':
|
||||
return 'var(--accent-color)'
|
||||
default:
|
||||
console.error(`Unknown SponsorBlock category ${category}`)
|
||||
return 'var(--accent-color)'
|
||||
}
|
||||
},
|
||||
|
||||
addSponsorBlockMarker(marker) {
|
||||
const markerDiv = videojs.dom.createEl('div', {}, {})
|
||||
|
||||
markerDiv.className = 'sponsorBlockMarker'
|
||||
markerDiv.className = `sponsorBlockMarker main${this.sponsorSkips.categoryData[marker.category].color}`
|
||||
markerDiv.style.height = '100%'
|
||||
markerDiv.style.position = 'absolute'
|
||||
markerDiv.style.opacity = '0.6'
|
||||
markerDiv.style['background-color'] = marker.color
|
||||
markerDiv.style.width = (marker.duration / this.player.duration()) * 100 + '%'
|
||||
markerDiv.style.marginLeft = (marker.time / this.player.duration()) * 100 + '%'
|
||||
markerDiv.title = this.sponsorBlockTranslatedCategory(marker.category)
|
||||
|
||||
this.player.el().querySelector('.vjs-progress-holder').appendChild(markerDiv)
|
||||
},
|
||||
|
|
|
@ -4,6 +4,7 @@ import FtCard from '../ft-card/ft-card.vue'
|
|||
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
|
||||
import FtInput from '../ft-input/ft-input.vue'
|
||||
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
||||
import FtSponsorBlockCategory from '../ft-sponsor-block-category/ft-sponsor-block-category.vue'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'SponsorBlockSettings',
|
||||
|
@ -11,7 +12,22 @@ export default Vue.extend({
|
|||
'ft-card': FtCard,
|
||||
'ft-toggle-switch': FtToggleSwitch,
|
||||
'ft-input': FtInput,
|
||||
'ft-flex-box': FtFlexBox
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-sponsor-block-category': FtSponsorBlockCategory
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
categories: [
|
||||
'sponsor',
|
||||
'self-promotion',
|
||||
'interaction',
|
||||
'intro',
|
||||
'outro',
|
||||
'recap',
|
||||
'music offtopic',
|
||||
'filler'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
useSponsorBlock: function () {
|
||||
|
|
|
@ -32,6 +32,13 @@
|
|||
@input="handleUpdateSponsorBlockUrl"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-sponsor-block-category
|
||||
v-for="category in categories"
|
||||
:key="category"
|
||||
:category-name="category"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
|
|
|
@ -34,31 +34,6 @@ export default Vue.extend({
|
|||
'dark',
|
||||
'black',
|
||||
'dracula'
|
||||
],
|
||||
colorValues: [
|
||||
'Red',
|
||||
'Pink',
|
||||
'Purple',
|
||||
'DeepPurple',
|
||||
'Indigo',
|
||||
'Blue',
|
||||
'LightBlue',
|
||||
'Cyan',
|
||||
'Teal',
|
||||
'Green',
|
||||
'LightGreen',
|
||||
'Lime',
|
||||
'Yellow',
|
||||
'Amber',
|
||||
'Orange',
|
||||
'DeepOrange',
|
||||
'DraculaCyan',
|
||||
'DraculaGreen',
|
||||
'DraculaOrange',
|
||||
'DraculaPink',
|
||||
'DraculaPurple',
|
||||
'DraculaRed',
|
||||
'DraculaYellow'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -120,32 +95,16 @@ export default Vue.extend({
|
|||
]
|
||||
},
|
||||
|
||||
colorValues: function () {
|
||||
return this.$store.getters.getColorNames
|
||||
},
|
||||
|
||||
colorNames: function () {
|
||||
return [
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Red'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Pink'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Purple'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Deep Purple'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Indigo'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Blue'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Light Blue'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Cyan'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Teal'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Green'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Light Green'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Lime'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Yellow'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Amber'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Orange'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Deep Orange'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Cyan'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Green'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Orange'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Pink'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Purple'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Red'),
|
||||
this.$t('Settings.Theme Settings.Main Color Theme.Dracula Yellow')
|
||||
]
|
||||
return this.colorValues.map(colorVal => {
|
||||
// add spaces before capital letters
|
||||
const colorName = colorVal.replace(/([A-Z])/g, ' $1').trim()
|
||||
return this.$t(`Settings.Theme Settings.Main Color Theme.${colorName}`)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
|
|
|
@ -215,6 +215,38 @@ const state = {
|
|||
saveWatchedProgress: true,
|
||||
sponsorBlockShowSkippedToast: true,
|
||||
sponsorBlockUrl: 'https://sponsor.ajay.app',
|
||||
sponsorBlockSponsor: {
|
||||
color: 'Blue',
|
||||
skip: 'autoSkip'
|
||||
},
|
||||
sponsorBlockSelfPromo: {
|
||||
color: 'Yellow',
|
||||
skip: 'showInSeekBar'
|
||||
},
|
||||
sponsorBlockInteraction: {
|
||||
color: 'Green',
|
||||
skip: 'showInSeekBar'
|
||||
},
|
||||
sponsorBlockIntro: {
|
||||
color: 'Orange',
|
||||
skip: 'doNothing'
|
||||
},
|
||||
sponsorBlockOutro: {
|
||||
color: 'Orange',
|
||||
skip: 'doNothing'
|
||||
},
|
||||
sponsorBlockRecap: {
|
||||
color: 'Orange',
|
||||
skip: 'doNothing'
|
||||
},
|
||||
sponsorBlockMusicOffTopic: {
|
||||
color: 'Orange',
|
||||
skip: 'doNothing'
|
||||
},
|
||||
sponsorBlockFiller: {
|
||||
color: 'Orange',
|
||||
skip: 'doNothing'
|
||||
},
|
||||
thumbnailPreference: '',
|
||||
useProxy: false,
|
||||
useRssFeeds: false,
|
||||
|
|
|
@ -26,30 +26,30 @@ const state = {
|
|||
type: 'all',
|
||||
duration: ''
|
||||
},
|
||||
colorClasses: [
|
||||
'mainRed',
|
||||
'mainPink',
|
||||
'mainPurple',
|
||||
'mainDeepPurple',
|
||||
'mainIndigo',
|
||||
'mainBlue',
|
||||
'mainLightBlue',
|
||||
'mainCyan',
|
||||
'mainTeal',
|
||||
'mainGreen',
|
||||
'mainLightGreen',
|
||||
'mainLime',
|
||||
'mainYellow',
|
||||
'mainAmber',
|
||||
'mainOrange',
|
||||
'mainDeepOrange',
|
||||
'mainDraculaCyan',
|
||||
'mainDraculaGreen',
|
||||
'mainDraculaOrange',
|
||||
'mainDraculaPink',
|
||||
'mainDraculaPurple',
|
||||
'mainDraculaRed',
|
||||
'mainDraculaYellow'
|
||||
colorNames: [
|
||||
'Red',
|
||||
'Pink',
|
||||
'Purple',
|
||||
'DeepPurple',
|
||||
'Indigo',
|
||||
'Blue',
|
||||
'LightBlue',
|
||||
'Cyan',
|
||||
'Teal',
|
||||
'Green',
|
||||
'LightGreen',
|
||||
'Lime',
|
||||
'Yellow',
|
||||
'Amber',
|
||||
'Orange',
|
||||
'DeepOrange',
|
||||
'DraculaCyan',
|
||||
'DraculaGreen',
|
||||
'DraculaOrange',
|
||||
'DraculaPink',
|
||||
'DraculaPurple',
|
||||
'DraculaRed',
|
||||
'DraculaYellow'
|
||||
],
|
||||
colorValues: [
|
||||
'#d50000',
|
||||
|
@ -106,6 +106,10 @@ const getters = {
|
|||
return state.searchSettings
|
||||
},
|
||||
|
||||
getColorNames () {
|
||||
return state.colorNames
|
||||
},
|
||||
|
||||
getColorValues () {
|
||||
return state.colorValues
|
||||
},
|
||||
|
@ -296,8 +300,8 @@ const actions = {
|
|||
},
|
||||
|
||||
getRandomColorClass () {
|
||||
const randomInt = Math.floor(Math.random() * state.colorClasses.length)
|
||||
return state.colorClasses[randomInt]
|
||||
const randomInt = Math.floor(Math.random() * state.colorNames.length)
|
||||
return 'main' + state.colorNames[randomInt]
|
||||
},
|
||||
|
||||
getRandomColor () {
|
||||
|
|
|
@ -337,6 +337,13 @@ Settings:
|
|||
Enable SponsorBlock: Enable SponsorBlock
|
||||
'SponsorBlock API Url (Default is https://sponsor.ajay.app)': SponsorBlock API Url (Default is https://sponsor.ajay.app)
|
||||
Notify when sponsor segment is skipped: Notify when sponsor segment is skipped
|
||||
Skip Options:
|
||||
Skip Option: Skip Option
|
||||
Auto Skip: Auto Skip
|
||||
Show In Seek Bar: Show In Seek Bar
|
||||
Prompt To Skip: Prompt To Skip
|
||||
Do Nothing: Do Nothing
|
||||
Category Color: Category Color
|
||||
Download Settings:
|
||||
Download Settings: Download Settings
|
||||
Ask Download Path: Ask for download path
|
||||
|
@ -537,12 +544,14 @@ Video:
|
|||
Publicationtemplate: $ % ago
|
||||
Skipped segment: Skipped segment
|
||||
Sponsor Block category:
|
||||
sponsor: sponsor
|
||||
intro: intro
|
||||
outro: outro
|
||||
self-promotion: self-promotion
|
||||
interaction: interaction
|
||||
music offtopic: music offtopic
|
||||
sponsor: Sponsor
|
||||
intro: Intro
|
||||
outro: Outro
|
||||
self-promotion: Self-Promotion
|
||||
interaction: Interaction
|
||||
music offtopic: Music Offtopic
|
||||
recap: Recap
|
||||
filler: Filler
|
||||
External Player:
|
||||
# $ is replaced with the external player
|
||||
OpenInTemplate: Open in $
|
||||
|
|
Loading…
Reference in New Issue