Add DeArrow support for ft-list-video titles (#3688)

* Add deArrow support for ft-list-video titles

* Implement requested changes

* Remove some code duplication

Co-authored-by: PikachuEXE <pikachuexe@gmail.com>

* fix lint issues + exception

* Check for vote count when title is not locked

Co-authored-by: Ajay Ramachandran <dev@ajay.app>

* simplify title

* Apply suggestions from code review

Co-authored-by: PikachuEXE <pikachuexe@gmail.com>

* remove unused clearDeArrowCache function

* Fix deArrow title

Co-authored-by: PikachuEXE <pikachuexe@gmail.com>

* allow using DeArrow without enabling SB

* add tooltip for DeArrow setting

---------

Co-authored-by: PikachuEXE <pikachuexe@gmail.com>
Co-authored-by: Ajay Ramachandran <dev@ajay.app>
This commit is contained in:
ChunkyProgrammer
2023-07-03 18:27:49 +02:00
committed by GitHub
co-authored by PikachuEXE Ajay Ramachandran
parent 649525558b
commit cf88bd77d8
7 changed files with 100 additions and 10 deletions
@@ -10,6 +10,7 @@ import {
toLocalePublicationString,
toDistractionFreeTitle
} from '../../helpers/utils'
import { deArrowData } from '../../helpers/sponsorblock'
export default defineComponent({
name: 'FtListVideo',
@@ -320,6 +321,14 @@ export default defineComponent({
currentLocale: function () {
return this.$i18n.locale.replace('_', '-')
},
useDeArrowTitles: function () {
return this.$store.getters.getUseDeArrowTitles
},
deArrowCache: function () {
return this.$store.getters.getDeArrowCache(this.id)
}
},
watch: {
historyIndex() {
@@ -331,6 +340,25 @@ export default defineComponent({
this.checkIfWatched()
},
methods: {
getDeArrowDataEntry: async function() {
// Read from local cache or remote
// Write to cache if read from remote
if (!this.useDeArrowTitles) { return null }
if (this.deArrowCache) { return this.deArrowCache }
const videoId = this.id
const data = await deArrowData(this.id)
const cacheData = { videoId, title: null }
if (Array.isArray(data?.titles) && data.titles.length > 0 && (data.titles[0].locked || data.titles[0].votes > 0)) {
cacheData.title = data.titles[0].title
}
// Save data to cache whether data available or not to prevent duplicate requests
this.$store.commit('addVideoToDeArrowCache', cacheData)
return cacheData
},
handleExternalPlayer: function () {
this.$emit('pause-player')
@@ -401,9 +429,9 @@ export default defineComponent({
}
},
parseVideoData: function () {
parseVideoData: async function () {
this.id = this.data.videoId
this.title = this.data.title
this.title = (await this.getDeArrowDataEntry())?.title ?? this.data.title
// this.thumbnail = this.data.videoThumbnails[4].url
this.channelName = this.data.author ?? null
@@ -38,6 +38,10 @@ export default defineComponent({
},
sponsorBlockShowSkippedToast: function () {
return this.$store.getters.getSponsorBlockShowSkippedToast
},
useDeArrowTitles: function () {
return this.$store.getters.getUseDeArrowTitles
}
},
methods: {
@@ -45,6 +49,10 @@ export default defineComponent({
this.updateUseSponsorBlock(value)
},
handleUpdateUseDeArrowTitles: function (value) {
this.updateUseDeArrowTitles(value)
},
handleUpdateSponsorBlockUrl: function (value) {
const sponsorBlockUrlWithoutTrailingSlash = value.replace(/\/$/, '')
const sponsorBlockUrlWithoutApiSuffix = sponsorBlockUrlWithoutTrailingSlash.replace(/\/api$/, '')
@@ -58,7 +66,8 @@ export default defineComponent({
...mapActions([
'updateUseSponsorBlock',
'updateSponsorBlockUrl',
'updateSponsorBlockShowSkippedToast'
'updateSponsorBlockShowSkippedToast',
'updateUseDeArrowTitles'
])
}
})
@@ -8,11 +8,20 @@
:default-value="useSponsorBlock"
@change="handleUpdateSponsorBlock"
/>
<ft-toggle-switch
:label="$t('Settings.SponsorBlock Settings.UseDeArrowTitles')"
:default-value="useDeArrowTitles"
:tooltip="$t('Tooltips.SponsorBlock Settings.UseDeArrowTitles')"
@change="handleUpdateUseDeArrowTitles"
/>
</ft-flex-box>
<div
v-if="useSponsorBlock"
v-if="useSponsorBlock || useDeArrowTitles"
>
<ft-flex-box class="settingsFlexStart500px">
<ft-flex-box
v-if="useSponsorBlock"
class="settingsFlexStart500px"
>
<ft-toggle-switch
:label="$t('Settings.SponsorBlock Settings.Notify when sponsor segment is skipped')"
:default-value="sponsorBlockShowSkippedToast"
@@ -28,7 +37,9 @@
@input="handleUpdateSponsorBlockUrl"
/>
</ft-flex-box>
<ft-flex-box>
<ft-flex-box
v-if="useSponsorBlock"
>
<ft-sponsor-block-category
v-for="category in categories"
:key="category"
+25 -4
View File
@@ -1,16 +1,17 @@
import store from '../store/index'
export async function sponsorBlockSkipSegments(videoId, categories) {
async function getVideoHash(videoId) {
const videoIdBuffer = new TextEncoder().encode(videoId)
const hashBuffer = await crypto.subtle.digest('SHA-256', videoIdBuffer)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const videoIdHashPrefix = hashArray
return hashArray
.map(byte => byte.toString(16).padStart(2, '0'))
.slice(0, 4)
.join('')
}
export async function sponsorBlockSkipSegments(videoId, categories) {
const videoIdHashPrefix = await getVideoHash(videoId)
const requestUrl = `${store.getters.getSponsorBlockUrl}/api/skipSegments/${videoIdHashPrefix}?categories=${JSON.stringify(categories)}`
try {
@@ -30,3 +31,23 @@ export async function sponsorBlockSkipSegments(videoId, categories) {
throw error
}
}
export async function deArrowData(videoId) {
const videoIdHashPrefix = (await getVideoHash(videoId)).substring(0, 4)
const requestUrl = `${store.getters.getSponsorBlockUrl}/api/branding/${videoIdHashPrefix}`
try {
const response = await fetch(requestUrl)
// 404 means that there are no segments registered for the video
if (response.status === 404) {
return undefined
}
const json = await response.json()
return json[videoId] ?? undefined
} catch (error) {
console.error('failed to fetch DeArrow data', requestUrl, error)
throw error
}
}
+1
View File
@@ -286,6 +286,7 @@ const state = {
settingsPassword: '',
allowDashAv1Formats: false,
commentAutoLoadEnabled: false,
useDeArrowTitles: false,
}
const stateWithSideEffects = {
+17
View File
@@ -27,6 +27,7 @@ const state = {
movies: null
},
cachedPlaylist: null,
deArrowCache: {},
showProgressBar: false,
progressBarPercentage: 0,
regionNames: [],
@@ -57,6 +58,10 @@ const getters = {
return state.sessionSearchHistory
},
getDeArrowCache: (state) => (videoId) => {
return state.deArrowCache[videoId]
},
getPopularCache () {
return state.popularCache
},
@@ -604,6 +609,18 @@ const mutations = {
state.sessionSearchHistory = history
},
setDeArrowCache (state, cache) {
state.deArrowCache = cache
},
addVideoToDeArrowCache (state, payload) {
const sameVideo = state.deArrowCache[payload.videoId]
if (!sameVideo) {
state.deArrowCache[payload.videoId] = payload
}
},
addToSessionSearchHistory (state, payload) {
const sameSearch = state.sessionSearchHistory.findIndex((search) => {
return search.query === payload.query && searchFiltersMatch(payload.searchSettings, search.searchSettings)
+3
View File
@@ -410,6 +410,7 @@ 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
UseDeArrowTitles: Use DeArrow Video Titles
Skip Options:
Skip Option: Skip Option
Auto Skip: Auto Skip
@@ -841,6 +842,8 @@ Tooltips:
when the watch page is closed.
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:
UseDeArrowTitles: Replace video titles with user-submitted titles from DeArrow.
# Toast Messages
Local API Error (Click to copy): Local API Error (Click to copy)