Skip to content
41 changes: 39 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 @@ -316,6 +317,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 @@ -327,6 +336,34 @@ export default defineComponent({
this.checkIfWatched()
},
methods: {
getDeArrowTitle: async function() {
const data = await this.getDeArrowDataEntry()
if (data != null && data.titles.length > 0 && data.titles[0].locked) {
Comment thread
This conversation was marked as resolved.
Outdated
return data.titles[0].title
}

return null
},

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, titles: [] }
if (Array.isArray(data?.titles)) {
cacheData.titles = data.titles
}

// 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 @@ -397,9 +434,9 @@ export default defineComponent({
}
},

parseVideoData: function () {
parseVideoData: async function () {
this.id = this.data.videoId
this.title = this.data.title
this.title = (await this.getDeArrowTitle()) ?? this.data.title
Comment thread
This conversation was marked as resolved.
Outdated
// this.thumbnail = this.data.videoThumbnails[4].url

this.channelName = this.data.author
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 @@ -19,6 +19,13 @@
@change="handleUpdateSponsorBlockShowSkippedToast"
/>
</ft-flex-box>
<ft-flex-box class="settingsFlexStart500px">
<ft-toggle-switch
:label="$t('Settings.SponsorBlock Settings.UseDeArrowTitles')"
:default-value="useDeArrowTitles"
@change="handleUpdateUseDeArrowTitles"
/>
</ft-flex-box>
<ft-flex-box>
<ft-input
:placeholder="$t('Settings.SponsorBlock Settings[\'SponsorBlock API Url (Default is https://sponsor.ajay.app)\']')"
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
21 changes: 21 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 @@ -469,6 +474,10 @@ const actions = {
commit('setSessionSearchHistory', [])
},

clearDeArrowCache ({ commit }) {
Comment thread
This conversation was marked as resolved.
Outdated
commit('setDeArrowCache', [])
Comment thread
This conversation was marked as resolved.
Outdated
},

async getExternalPlayerCmdArgumentsData ({ commit }, payload) {
const fileName = 'external-player-map.json'
let fileData
Expand Down Expand Up @@ -614,6 +623,18 @@ const mutations = {
state.sessionSearchHistory = history
},

setDeArrowCache (state, cache) {
state.deArrowCache = cache
},

addVideoToDeArrowCache (state, payload) {
Comment thread
PikachuEXE marked this conversation as resolved.
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
1 change: 1 addition & 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 Titles
Skip Options:
Skip Option: Skip Option
Auto Skip: Auto Skip
Expand Down