This commit is contained in:
ChunkyProgrammer 2024-11-21 09:00:40 +00:00 committed by GitHub
commit 46266ea337
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 166 additions and 173 deletions

View File

@ -1,172 +0,0 @@
import { defineComponent } from 'vue'
import { mapActions } from 'vuex'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtInput from '../../components/ft-input/ft-input.vue'
import FtSubscribeButton from '../../components/ft-subscribe-button/ft-subscribe-button.vue'
import { invidiousGetChannelInfo, youtubeImageUrlToInvidious, invidiousImageUrlToInvidious } from '../../helpers/api/invidious'
import { getLocalChannel, parseLocalChannelHeader } from '../../helpers/api/local'
import { ctrlFHandler } from '../../helpers/utils'
export default defineComponent({
name: 'SubscribedChannels',
components: {
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
'ft-input': FtInput,
'ft-subscribe-button': FtSubscribeButton
},
data: function () {
return {
query: '',
subscribedChannels: [],
filteredChannels: [],
re: {
url: /(.+=\w)\d+(.+)/,
ivToYt: /^.+ggpht\/(.+)/
},
thumbnailSize: 176,
ytBaseURL: 'https://yt3.ggpht.com',
errorCount: 0,
}
},
computed: {
activeProfile: function () {
return this.$store.getters.getActiveProfile
},
activeProfileId: function () {
return this.activeProfile._id
},
activeSubscriptionList: function () {
return this.activeProfile.subscriptions
},
channelList: function () {
if (this.query !== '') {
return this.filteredChannels
} else {
return this.subscribedChannels
}
},
hideUnsubscribeButton: function() {
return this.$store.getters.getHideUnsubscribeButton
},
locale: function () {
return this.$i18n.locale
},
backendPreference: function () {
return this.$store.getters.getBackendPreference
},
currentInvidiousInstanceUrl: function () {
return this.$store.getters.getCurrentInvidiousInstanceUrl
}
},
watch: {
activeProfileId: function() {
this.query = ''
this.getSubscription()
},
activeSubscriptionList: function() {
this.getSubscription()
this.filterChannels()
}
},
mounted: function () {
document.addEventListener('keydown', this.keyboardShortcutHandler)
this.getSubscription()
},
beforeDestroy: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
},
methods: {
getSubscription: function () {
this.subscribedChannels = this.activeSubscriptionList.slice().sort((a, b) => {
return a.name?.toLowerCase().localeCompare(b.name?.toLowerCase(), this.locale)
})
},
handleInput: function(input) {
this.query = input
this.filterChannels()
},
filterChannels: function () {
if (this.query === '') {
this.filteredChannels = []
return
}
const escapedQuery = this.query.replaceAll(/[$()*+.?[\\\]^{|}]/g, '\\$&')
const re = new RegExp(escapedQuery, 'i')
this.filteredChannels = this.subscribedChannels.filter(channel => {
return re.test(channel.name)
})
},
thumbnailURL: function(originalURL) {
if (originalURL == null) { return null }
let newURL = originalURL
// Sometimes relative protocol URLs are passed in
if (originalURL.startsWith('//')) {
newURL = `https:${originalURL}`
}
const hostname = new URL(newURL).hostname
if (hostname === 'yt3.ggpht.com' || hostname === 'yt3.googleusercontent.com') {
if (this.backendPreference === 'invidious') { // YT to IV
newURL = youtubeImageUrlToInvidious(newURL, this.currentInvidiousInstanceUrl)
}
} else {
if (this.backendPreference === 'local') { // IV to YT
newURL = newURL.replace(this.re.ivToYt, `${this.ytBaseURL}/$1`)
} else { // IV to IV
newURL = invidiousImageUrlToInvidious(newURL, this.currentInvidiousInstanceUrl)
}
}
return newURL.replace(this.re.url, `$1${this.thumbnailSize}$2`)
},
updateThumbnail: function(channel) {
this.errorCount += 1
if (this.backendPreference === 'local') {
// avoid too many concurrent requests
setTimeout(() => {
getLocalChannel(channel.id).then(response => {
if (!response.alert) {
this.updateSubscriptionDetails({
channelThumbnailUrl: this.thumbnailURL(parseLocalChannelHeader(response).thumbnailUrl),
channelName: channel.name,
channelId: channel.id
})
}
})
}, this.errorCount * 500)
} else {
setTimeout(() => {
invidiousGetChannelInfo(channel.id).then(response => {
this.updateSubscriptionDetails({
channelThumbnailUrl: this.thumbnailURL(response.authorThumbnails[0].url),
channelName: channel.name,
channelId: channel.id
})
})
}, this.errorCount * 500)
}
},
keyboardShortcutHandler: function (event) {
ctrlFHandler(event, this.$refs.searchBarChannels)
},
...mapActions([
'updateSubscriptionDetails'
])
}
})

View File

@ -73,5 +73,170 @@
</div>
</template>
<script src="./SubscribedChannels.js" />
<script setup>
import { computed, onMounted, onBeforeUnmount, ref, watch } from 'vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtInput from '../../components/ft-input/ft-input.vue'
import FtSubscribeButton from '../../components/ft-subscribe-button/ft-subscribe-button.vue'
import { invidiousGetChannelInfo, youtubeImageUrlToInvidious, invidiousImageUrlToInvidious } from '../../helpers/api/invidious'
import { getLocalChannel, parseLocalChannelHeader } from '../../helpers/api/local'
import { ctrlFHandler } from '../../helpers/utils'
import { useI18n } from '../../composables/use-i18n-polyfill.js'
import store from '../../store/index'
const { locale } = useI18n()
const re = {
url: /(.+=\w)\d+(.+)/,
ivToYt: /^.+ggpht\/(.+)/
}
const ytBaseURL = 'https://yt3.ggpht.com'
const thumbnailSize = 176
let errorCount = 0
const query = ref('')
const subscribedChannels = ref([])
const filteredChannels = ref([])
/** @type {import('vue').Ref<HTMLInputElement | null>} */
const searchBarChannels = ref(null)
/** @type {import('vue').ComputedRef<object>} */
const activeProfile = computed(() => {
return store.getters.getActiveProfile
})
/** @type {import('vue').ComputedRef<string>} */
const activeProfileId = computed(() => {
return activeProfile.value._id
})
/** @type {import('vue').ComputedRef<Array>} */
const activeSubscriptionList = computed(() => {
return activeProfile.value.subscriptions
})
/** @type {import('vue').ComputedRef<Array>} */
const channelList = computed(() => {
if (query.value !== '') {
return filteredChannels.value
} else {
return subscribedChannels.value
}
})
/** @type {import('vue').ComputedRef<boolean>} */
const hideUnsubscribeButton = computed(() => {
return store.getters.getHideUnsubscribeButton
})
/** @type {import('vue').ComputedRef<'local' | 'invidious'>} */
const backendPreference = computed(() => {
return store.getters.getBackendPreference
})
/** @type {import('vue').ComputedRef<string>} */
const currentInvidiousInstanceUrl = computed(() => {
return store.getters.getCurrentInvidiousInstanceUrl
})
function getSubscription() {
subscribedChannels.value = activeSubscriptionList.value.slice().sort((a, b) => {
return a.name?.toLowerCase().localeCompare(b.name?.toLowerCase(), locale.value)
})
}
function handleInput(input) {
query.value = input
filterChannels()
}
function filterChannels() {
if (query.value === '') {
filteredChannels.value = []
return
}
const escapedQuery = query.value.replaceAll(/[$()*+.?[\\\]^{|}]/g, '\\$&')
const re = new RegExp(escapedQuery, 'i')
filteredChannels.value = subscribedChannels.value.filter(channel => {
return re.test(channel.name)
})
}
function thumbnailURL(originalURL) {
if (originalURL == null) { return null }
let newURL = originalURL
// Sometimes relative protocol URLs are passed in
if (originalURL.startsWith('//')) {
newURL = `https:${originalURL}`
}
const hostname = new URL(newURL).hostname
if (hostname === 'yt3.ggpht.com' || hostname === 'yt3.googleusercontent.com') {
if (backendPreference.value === 'invidious') { // YT to IV
newURL = youtubeImageUrlToInvidious(newURL, currentInvidiousInstanceUrl.value)
}
} else {
if (backendPreference.value === 'local') { // IV to YT
newURL = newURL.replace(re.ivToYt, `${ytBaseURL}/$1`)
} else { // IV to IV
newURL = invidiousImageUrlToInvidious(newURL, currentInvidiousInstanceUrl.value)
}
}
return newURL.replace(re.url, `$1${thumbnailSize}$2`)
}
function updateThumbnail(channel) {
errorCount += 1
if (backendPreference.value === 'local') {
// avoid too many concurrent requests
setTimeout(() => {
getLocalChannel(channel.id).then(response => {
if (!response.alert) {
store.dispatch('updateSubscriptionDetails', {
channelThumbnailUrl: thumbnailURL(parseLocalChannelHeader(response).thumbnailUrl),
channelName: channel.name,
channelId: channel.id
})
}
})
}, errorCount * 500)
} else {
setTimeout(() => {
invidiousGetChannelInfo(channel.id).then(response => {
store.dispatch('updateSubscriptionDetails', {
channelThumbnailUrl: thumbnailURL(response.authorThumbnails[0].url),
channelName: channel.name,
channelId: channel.id
})
})
}, errorCount * 500)
}
}
function keyboardShortcutHandler(event) {
ctrlFHandler(event, searchBarChannels.value)
}
watch(activeProfileId, () => {
query.value = ''
getSubscription()
})
watch(activeSubscriptionList, () => {
getSubscription()
filterChannels()
})
onMounted(() => {
document.addEventListener('keydown', keyboardShortcutHandler)
getSubscription()
})
onBeforeUnmount(() => {
document.removeEventListener('keydown', keyboardShortcutHandler)
})
</script>
<style scoped src="./SubscribedChannels.css" />