Skip to content

Commit

Permalink
Add DeArrow support for ft-list-video titles (FreeTubeApp#3688)
Browse files Browse the repository at this point in the history
* Add deArrow support for ft-list-video titles

* Implement requested changes

* Remove some code duplication

Co-authored-by: PikachuEXE <[email protected]>

* fix lint issues + exception

* Check for vote count when title is not locked

Co-authored-by: Ajay Ramachandran <[email protected]>

* simplify title

* Apply suggestions from code review

Co-authored-by: PikachuEXE <[email protected]>

* remove unused clearDeArrowCache function

* Fix deArrow title

Co-authored-by: PikachuEXE <[email protected]>

* allow using DeArrow without enabling SB

* add tooltip for DeArrow setting

---------

Co-authored-by: PikachuEXE <[email protected]>
Co-authored-by: Ajay Ramachandran <[email protected]>
  • Loading branch information
3 people authored Jul 3, 2023
1 parent 6495255 commit cf88bd7
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 10 deletions.
32 changes: 30 additions & 2 deletions src/renderer/components/ft-list-video/ft-list-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
toLocalePublicationString,
toDistractionFreeTitle
} from '../../helpers/utils'
import { deArrowData } from '../../helpers/sponsorblock'

export default defineComponent({
name: 'FtListVideo',
Expand Down Expand Up @@ -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() {
Expand All @@ -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')

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ export default defineComponent({
},
sponsorBlockShowSkippedToast: function () {
return this.$store.getters.getSponsorBlockShowSkippedToast
},

useDeArrowTitles: function () {
return this.$store.getters.getUseDeArrowTitles
}
},
methods: {
handleUpdateSponsorBlock: function (value) {
this.updateUseSponsorBlock(value)
},

handleUpdateUseDeArrowTitles: function (value) {
this.updateUseDeArrowTitles(value)
},

handleUpdateSponsorBlockUrl: function (value) {
const sponsorBlockUrlWithoutTrailingSlash = value.replace(/\/$/, '')
const sponsorBlockUrlWithoutApiSuffix = sponsorBlockUrlWithoutTrailingSlash.replace(/\/api$/, '')
Expand All @@ -58,7 +66,8 @@ export default defineComponent({
...mapActions([
'updateUseSponsorBlock',
'updateSponsorBlockUrl',
'updateSponsorBlockShowSkippedToast'
'updateSponsorBlockShowSkippedToast',
'updateUseDeArrowTitles'
])
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
29 changes: 25 additions & 4 deletions src/renderer/helpers/sponsorblock.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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 change: 1 addition & 0 deletions src/renderer/store/modules/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ const state = {
settingsPassword: '',
allowDashAv1Formats: false,
commentAutoLoadEnabled: false,
useDeArrowTitles: false,
}

const stateWithSideEffects = {
Expand Down
17 changes: 17 additions & 0 deletions src/renderer/store/modules/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const state = {
movies: null
},
cachedPlaylist: null,
deArrowCache: {},
showProgressBar: false,
progressBarPercentage: 0,
regionNames: [],
Expand Down Expand Up @@ -57,6 +58,10 @@ const getters = {
return state.sessionSearchHistory
},

getDeArrowCache: (state) => (videoId) => {
return state.deArrowCache[videoId]
},

getPopularCache () {
return state.popularCache
},
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions static/locales/en-US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit cf88bd7

Please sign in to comment.