SponsorBlock (#1130)
* SponsorBlock: enable/url settings * SponsorBlock: fetch and display skipped fragments * SponsorBlock: skip sponsor blocks * npm add node-forge * SponsorBlock: use hash prefix API * SponsorBlock: configurable toast on skipped segment * SponsorBlock: add /api/ to url, remove trailing slash
This commit is contained in:
		
							parent
							
								
									1096310c9b
								
							
						
					
					
						commit
						440b04bbf0
					
				|  | @ -13739,8 +13739,7 @@ | ||||||
|     "node-forge": { |     "node-forge": { | ||||||
|       "version": "0.10.0", |       "version": "0.10.0", | ||||||
|       "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", |       "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", | ||||||
|       "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", |       "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" | ||||||
|       "dev": true |  | ||||||
|     }, |     }, | ||||||
|     "node-gyp": { |     "node-gyp": { | ||||||
|       "version": "7.1.2", |       "version": "7.1.2", | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ | ||||||
|     "markdown": "^0.5.0", |     "markdown": "^0.5.0", | ||||||
|     "material-design-icons": "^3.0.1", |     "material-design-icons": "^3.0.1", | ||||||
|     "nedb": "^1.8.0", |     "nedb": "^1.8.0", | ||||||
|  |     "node-forge": "^0.10.0", | ||||||
|     "opml-to-json": "^1.0.1", |     "opml-to-json": "^1.0.1", | ||||||
|     "rss-parser": "^3.12.0", |     "rss-parser": "^3.12.0", | ||||||
|     "socks-proxy-agent": "^5.0.0", |     "socks-proxy-agent": "^5.0.0", | ||||||
|  |  | ||||||
|  | @ -66,6 +66,10 @@ export default Vue.extend({ | ||||||
|     thumbnail: { |     thumbnail: { | ||||||
|       type: String, |       type: String, | ||||||
|       default: '' |       default: '' | ||||||
|  |     }, | ||||||
|  |     videoId: { | ||||||
|  |       type: String, | ||||||
|  |       required: true | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   data: function () { |   data: function () { | ||||||
|  | @ -149,6 +153,14 @@ export default Vue.extend({ | ||||||
| 
 | 
 | ||||||
|     autoplayVideos: function () { |     autoplayVideos: function () { | ||||||
|       return this.$store.getters.getAutoplayVideos |       return this.$store.getters.getAutoplayVideos | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     useSponsorBlock: function () { | ||||||
|  |       return this.$store.getters.getUseSponsorBlock | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     sponsorBlockShowSkippedToast: function () { | ||||||
|  |       return this.$store.getters.getSponsorBlockShowSkippedToast | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mounted: function () { |   mounted: function () { | ||||||
|  | @ -274,6 +286,109 @@ export default Vue.extend({ | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
|       } |       } | ||||||
|  |       setTimeout(() => { this.fetchSponsorBlockInfo() }, 100) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     fetchSponsorBlockInfo() { | ||||||
|  |       if (this.useSponsorBlock) { | ||||||
|  |         this.$store.dispatch('sponsorBlockSkipSegments', { | ||||||
|  |           videoId: this.videoId, | ||||||
|  |           categories: ['sponsor'] | ||||||
|  |         }).then((skipSegments) => { | ||||||
|  |           this.player.on('timeupdate', () => { | ||||||
|  |             this.skipSponsorBlocks(skipSegments) | ||||||
|  |           }) | ||||||
|  |           skipSegments.forEach(({ | ||||||
|  |             category, | ||||||
|  |             segment: [startTime, endTime] | ||||||
|  |           }) => { | ||||||
|  |             this.addSponsorBlockMarker({ | ||||||
|  |               time: startTime, | ||||||
|  |               duration: endTime - startTime, | ||||||
|  |               color: this.sponsorBlockCategoryColor(category) | ||||||
|  |             }) | ||||||
|  |           }) | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     skipSponsorBlocks(skipSegments) { | ||||||
|  |       const currentTime = this.player.currentTime() | ||||||
|  |       let newTime = null | ||||||
|  |       let skippedCategory = null | ||||||
|  |       skipSegments.forEach(({ category, segment: [startTime, endTime] }) => { | ||||||
|  |         if (startTime <= currentTime && currentTime < endTime) { | ||||||
|  |           newTime = endTime | ||||||
|  |           skippedCategory = category | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       if (newTime !== null) { | ||||||
|  |         if (this.sponsorBlockShowSkippedToast) { | ||||||
|  |           this.showSkippedSponsorSegmentInformation(skippedCategory) | ||||||
|  |         } | ||||||
|  |         this.player.currentTime(newTime) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     showSkippedSponsorSegmentInformation(category) { | ||||||
|  |       const translatedCategory = this.sponsorBlockTranslatedCategory(category) | ||||||
|  |       this.showToast({ | ||||||
|  |         message: `${this.$t('Video.Skipped segment')} ${translatedCategory}` | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     sponsorBlockTranslatedCategory(category) { | ||||||
|  |       switch (category) { | ||||||
|  |         case 'sponsor': | ||||||
|  |           return this.$t('Video.Sponsor Block category.sponsor') | ||||||
|  |         case 'intro': | ||||||
|  |           return this.$t('Video.Sponsor Block category.intro') | ||||||
|  |         case 'outro': | ||||||
|  |           return this.$t('Video.Sponsor Block category.outro') | ||||||
|  |         case 'selfpromo': | ||||||
|  |           return this.$t('Video.Sponsor Block category.self-promotion') | ||||||
|  |         case 'interaction': | ||||||
|  |           return this.$t('Video.Sponsor Block category.interaction') | ||||||
|  |         case 'music_offtopic': | ||||||
|  |           return this.$t('Video.Sponsor Block category.music offtopic') | ||||||
|  |         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 '#00d400' | ||||||
|  |         case 'intro': | ||||||
|  |           return '#00ffff' | ||||||
|  |         case 'outro': | ||||||
|  |           return '#0202ed' | ||||||
|  |         case 'selfpromo': | ||||||
|  |           return '#ffff00' | ||||||
|  |         case 'interaction': | ||||||
|  |           return '#cc00ff' | ||||||
|  |         case 'music_offtopic': | ||||||
|  |           return '#ff9900' | ||||||
|  |         default: | ||||||
|  |           console.error(`Unknown SponsorBlock category ${category}`) | ||||||
|  |           return 'yellow' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     addSponsorBlockMarker(marker) { | ||||||
|  |       const markerDiv = videojs.dom.createEl('div', {}, {}) | ||||||
|  | 
 | ||||||
|  |       markerDiv.className = 'sponsorBlockMarker' | ||||||
|  |       markerDiv.style.height = '100%' | ||||||
|  |       markerDiv.style.position = 'absolute' | ||||||
|  |       markerDiv.style['background-color'] = marker.color | ||||||
|  |       markerDiv.style.width = (marker.duration / this.player.duration()) * 100 + '%' | ||||||
|  |       markerDiv.style.marginLeft = (marker.time / this.player.duration()) * 100 + '%' | ||||||
|  | 
 | ||||||
|  |       this.player.el().querySelector('.vjs-progress-holder').appendChild(markerDiv) | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     checkAspectRatio() { |     checkAspectRatio() { | ||||||
|  | @ -1186,6 +1301,7 @@ export default Vue.extend({ | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     ...mapActions([ |     ...mapActions([ | ||||||
|  |       'showToast', | ||||||
|       'calculateColorLuminance' |       'calculateColorLuminance' | ||||||
|     ]) |     ]) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | .relative { | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card { | ||||||
|  |   width: 85%; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .center { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media only screen and (max-width: 680px) { | ||||||
|  |   .card { | ||||||
|  |     width: 90%; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media only screen and (max-width: 500px) { | ||||||
|  |   .sponsorBlockSettingsFlexBox { | ||||||
|  |     justify-content: flex-start; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | import Vue from 'vue' | ||||||
|  | import { mapActions } from 'vuex' | ||||||
|  | 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' | ||||||
|  | 
 | ||||||
|  | export default Vue.extend({ | ||||||
|  |   name: 'SponsorBlockSettings', | ||||||
|  |   components: { | ||||||
|  |     'ft-card': FtCard, | ||||||
|  |     'ft-toggle-switch': FtToggleSwitch, | ||||||
|  |     'ft-input': FtInput, | ||||||
|  |     'ft-flex-box': FtFlexBox | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     useSponsorBlock: function () { | ||||||
|  |       return this.$store.getters.getUseSponsorBlock | ||||||
|  |     }, | ||||||
|  |     sponsorBlockUrl: function () { | ||||||
|  |       return this.$store.getters.getSponsorBlockUrl | ||||||
|  |     }, | ||||||
|  |     sponsorBlockShowSkippedToast: function () { | ||||||
|  |       return this.$store.getters.getSponsorBlockShowSkippedToast | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     handleUpdateSponsorBlock: function (value) { | ||||||
|  |       this.updateUseSponsorBlock(value) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     handleUpdateSponsorBlockUrl: function (value) { | ||||||
|  |       const sponsorBlockUrlWithoutTrailingSlash = value.replace(/\/$/, '') | ||||||
|  |       const sponsorBlockUrlWithoutApiSuffix = sponsorBlockUrlWithoutTrailingSlash.replace(/\/api$/, '') | ||||||
|  |       this.updateSponsorBlockUrl(sponsorBlockUrlWithoutApiSuffix) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     handleUpdateSponsorBlockShowSkippedToast: function (value) { | ||||||
|  |       this.updateSponsorBlockShowSkippedToast(value) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     ...mapActions([ | ||||||
|  |       'updateUseSponsorBlock', | ||||||
|  |       'updateSponsorBlockUrl', | ||||||
|  |       'updateSponsorBlockShowSkippedToast' | ||||||
|  |     ]) | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | <template> | ||||||
|  |   <ft-card | ||||||
|  |     class="relative card" | ||||||
|  |   > | ||||||
|  |     <h3 | ||||||
|  |       class="videoTitle" | ||||||
|  |     > | ||||||
|  |       {{ $t("Settings.SponsorBlock Settings.SponsorBlock Settings") }} | ||||||
|  |     </h3> | ||||||
|  |     <ft-flex-box class="sponsorBlockSettingsFlexBox"> | ||||||
|  |       <ft-toggle-switch | ||||||
|  |         :label="$t('Settings.SponsorBlock Settings.Enable SponsorBlock')" | ||||||
|  |         :default-value="useSponsorBlock" | ||||||
|  |         @change="handleUpdateSponsorBlock" | ||||||
|  |       /> | ||||||
|  |     </ft-flex-box> | ||||||
|  |     <ft-flex-box class="sponsorBlockSettingsFlexBox"> | ||||||
|  |       <ft-toggle-switch | ||||||
|  |         :label="$t('Settings.SponsorBlock Settings.Notify when sponsor segment is skipped')" | ||||||
|  |         :default-value="sponsorBlockShowSkippedToast" | ||||||
|  |         @change="handleUpdateSponsorBlockShowSkippedToast" | ||||||
|  |       /> | ||||||
|  |     </ft-flex-box> | ||||||
|  |     <ft-flex-box> | ||||||
|  |       <ft-input | ||||||
|  |         :placeholder="$t('Settings.SponsorBlock Settings[\'SponsorBlock API Url (Default is https://sponsor.ajay.app)\']')" | ||||||
|  |         :show-arrow="false" | ||||||
|  |         :show-label="true" | ||||||
|  |         :value="sponsorBlockUrl" | ||||||
|  |         @input="handleUpdateSponsorBlockUrl" | ||||||
|  |       /> | ||||||
|  |     </ft-flex-box> | ||||||
|  |   </ft-card> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script src="./sponsor-block-settings.js" /> | ||||||
|  | <style scoped src="./sponsor-block-settings.css" /> | ||||||
|  | @ -78,7 +78,10 @@ const state = { | ||||||
|   hidePopularVideos: false, |   hidePopularVideos: false, | ||||||
|   hidePlaylists: false, |   hidePlaylists: false, | ||||||
|   hideLiveChat: false, |   hideLiveChat: false, | ||||||
|   hideActiveSubscriptions: false |   hideActiveSubscriptions: false, | ||||||
|  |   useSponsorBlock: false, | ||||||
|  |   sponsorBlockUrl: 'https://sponsor.ajay.app', | ||||||
|  |   sponsorBlockShowSkippedToast: true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const getters = { | const getters = { | ||||||
|  | @ -264,6 +267,18 @@ const getters = { | ||||||
| 
 | 
 | ||||||
|   getHideActiveSubscriptions: () => { |   getHideActiveSubscriptions: () => { | ||||||
|     return state.hideActiveSubscriptions |     return state.hideActiveSubscriptions | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getUseSponsorBlock: () => { | ||||||
|  |     return state.useSponsorBlock | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getSponsorBlockUrl: () => { | ||||||
|  |     return state.sponsorBlockUrl | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getSponsorBlockShowSkippedToast: () => { | ||||||
|  |     return state.sponsorBlockShowSkippedToast | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -417,6 +432,14 @@ const actions = { | ||||||
|               case 'hideActiveSubscriptions': |               case 'hideActiveSubscriptions': | ||||||
|                 commit('setHideActiveSubscriptions', result.value) |                 commit('setHideActiveSubscriptions', result.value) | ||||||
|                 break |                 break | ||||||
|  |               case 'useSponsorBlock': | ||||||
|  |                 commit('setUseSponsorBlock', result.value) | ||||||
|  |                 break | ||||||
|  |               case 'sponsorBlockUrl': | ||||||
|  |                 commit('setSponsorBlockUrl', result.value) | ||||||
|  |                 break | ||||||
|  |               case 'sponsorBlockShowSkippedToast': | ||||||
|  |                 commit('setSponsorBlockShowSkippedToast', result.value) | ||||||
|             } |             } | ||||||
|           }) |           }) | ||||||
|           resolve() |           resolve() | ||||||
|  | @ -786,6 +809,30 @@ const actions = { | ||||||
|         commit('setHideLiveChat', hideLiveChat) |         commit('setHideLiveChat', hideLiveChat) | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   updateUseSponsorBlock ({ commit }, useSponsorBlock) { | ||||||
|  |     settingsDb.update({ _id: 'useSponsorBlock' }, { _id: 'useSponsorBlock', value: useSponsorBlock }, { upsert: true }, (err, numReplaced) => { | ||||||
|  |       if (!err) { | ||||||
|  |         commit('setUseSponsorBlock', useSponsorBlock) | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   updateSponsorBlockUrl ({ commit }, sponsorBlockUrl) { | ||||||
|  |     settingsDb.update({ _id: 'sponsorBlockUrl' }, { _id: 'sponsorBlockUrl', value: sponsorBlockUrl }, { upsert: true }, (err, numReplaced) => { | ||||||
|  |       if (!err) { | ||||||
|  |         commit('setSponsorBlockUrl', sponsorBlockUrl) | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   updateSponsorBlockShowSkippedToast ({ commit }, sponsorBlockShowSkippedToast) { | ||||||
|  |     settingsDb.update({ _id: 'sponsorBlockShowSkippedToast' }, { _id: 'sponsorBlockShowSkippedToast', value: sponsorBlockShowSkippedToast }, { upsert: true }, (err, numReplaced) => { | ||||||
|  |       if (!err) { | ||||||
|  |         commit('setSponsorBlockShowSkippedToast', sponsorBlockShowSkippedToast) | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -944,6 +991,15 @@ const mutations = { | ||||||
|   }, |   }, | ||||||
|   setHideActiveSubscriptions (state, hideActiveSubscriptions) { |   setHideActiveSubscriptions (state, hideActiveSubscriptions) { | ||||||
|     state.hideActiveSubscriptions = hideActiveSubscriptions |     state.hideActiveSubscriptions = hideActiveSubscriptions | ||||||
|  |   }, | ||||||
|  |   setUseSponsorBlock (state, useSponsorBlock) { | ||||||
|  |     state.useSponsorBlock = useSponsorBlock | ||||||
|  |   }, | ||||||
|  |   setSponsorBlockUrl (state, sponsorBlockUrl) { | ||||||
|  |     state.sponsorBlockUrl = sponsorBlockUrl | ||||||
|  |   }, | ||||||
|  |   setSponsorBlockShowSkippedToast (state, sponsorBlockShowSkippedToast) { | ||||||
|  |     state.sponsorBlockShowSkippedToast = sponsorBlockShowSkippedToast | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | import $ from 'jquery' | ||||||
|  | import forge from 'node-forge' | ||||||
|  | 
 | ||||||
|  | const state = {} | ||||||
|  | const getters = {} | ||||||
|  | 
 | ||||||
|  | const actions = { | ||||||
|  |   sponsorBlockSkipSegments ({ rootState }, { videoId, categories }) { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |       const messageDigestSha256 = forge.md.sha256.create() | ||||||
|  |       messageDigestSha256.update(videoId) | ||||||
|  |       const videoIdHashPrefix = messageDigestSha256.digest().toHex().substring(0, 4) | ||||||
|  |       const requestUrl = `${rootState.settings.sponsorBlockUrl}/api/skipSegments/${videoIdHashPrefix}?categories=${JSON.stringify(categories)}` | ||||||
|  | 
 | ||||||
|  |       $.getJSON(requestUrl, (response) => { | ||||||
|  |         const segments = response | ||||||
|  |           .filter((result) => result.videoID === videoId) | ||||||
|  |           .flatMap((result) => result.segments) | ||||||
|  |         resolve(segments) | ||||||
|  |       }).fail((xhr, textStatus, error) => { | ||||||
|  |         console.log(xhr) | ||||||
|  |         console.log(textStatus) | ||||||
|  |         console.log(requestUrl) | ||||||
|  |         console.log(error) | ||||||
|  |         reject(xhr) | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const mutations = {} | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   state, | ||||||
|  |   getters, | ||||||
|  |   actions, | ||||||
|  |   mutations | ||||||
|  | } | ||||||
|  | @ -9,6 +9,7 @@ import PrivacySettings from '../../components/privacy-settings/privacy-settings. | ||||||
| import DataSettings from '../../components/data-settings/data-settings.vue' | import DataSettings from '../../components/data-settings/data-settings.vue' | ||||||
| import DistractionSettings from '../../components/distraction-settings/distraction-settings.vue' | import DistractionSettings from '../../components/distraction-settings/distraction-settings.vue' | ||||||
| import ProxySettings from '../../components/proxy-settings/proxy-settings.vue' | import ProxySettings from '../../components/proxy-settings/proxy-settings.vue' | ||||||
|  | import SponsorBlockSettings from '../../components/sponsor-block-settings/sponsor-block-settings.vue' | ||||||
| 
 | 
 | ||||||
| export default Vue.extend({ | export default Vue.extend({ | ||||||
|   name: 'Settings', |   name: 'Settings', | ||||||
|  | @ -22,6 +23,7 @@ export default Vue.extend({ | ||||||
|     'privacy-settings': PrivacySettings, |     'privacy-settings': PrivacySettings, | ||||||
|     'data-settings': DataSettings, |     'data-settings': DataSettings, | ||||||
|     'distraction-settings': DistractionSettings, |     'distraction-settings': DistractionSettings, | ||||||
|     'proxy-settings': ProxySettings |     'proxy-settings': ProxySettings, | ||||||
|  |     'sponsor-block-settings': SponsorBlockSettings | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
|     <privacy-settings /> |     <privacy-settings /> | ||||||
|     <data-settings /> |     <data-settings /> | ||||||
|     <proxy-settings /> |     <proxy-settings /> | ||||||
|  |     <sponsor-block-settings /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ | ||||||
|           :storyboard-src="videoStoryboardSrc" |           :storyboard-src="videoStoryboardSrc" | ||||||
|           :format="activeFormat" |           :format="activeFormat" | ||||||
|           :thumbnail="thumbnail" |           :thumbnail="thumbnail" | ||||||
|  |           :video-id="videoId" | ||||||
|           class="videoPlayer" |           class="videoPlayer" | ||||||
|           :class="{ theatrePlayer: useTheatreMode }" |           :class="{ theatrePlayer: useTheatreMode }" | ||||||
|           @ready="checkIfWatched" |           @ready="checkIfWatched" | ||||||
|  |  | ||||||
|  | @ -280,6 +280,11 @@ Settings: | ||||||
|     Region: Region |     Region: Region | ||||||
|     City: City |     City: City | ||||||
|     Error getting network information. Is your proxy configured properly?: Error getting network information. Is your proxy configured properly? |     Error getting network information. Is your proxy configured properly?: Error getting network information. Is your proxy configured properly? | ||||||
|  |   SponsorBlock Settings: | ||||||
|  |     SponsorBlock Settings: SponsorBlock 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 | ||||||
| About: | About: | ||||||
|   #On About page |   #On About page | ||||||
|   About: About |   About: About | ||||||
|  | @ -472,6 +477,14 @@ Video: | ||||||
|   translated from English: translated from English |   translated from English: translated from English | ||||||
|   # $ is replaced with the number and % with the unit (days, hours, minutes...) |   # $ is replaced with the number and % with the unit (days, hours, minutes...) | ||||||
|   Publicationtemplate: $ % ago |   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 | ||||||
| #& Videos | #& Videos | ||||||
| Videos: | Videos: | ||||||
|   #& Sort By |   #& Sort By | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue