mirror of
https://github.com/FreeTubeApp/FreeTube
synced 2024-11-22 01:45:40 +01:00
Compare commits
4 Commits
02a4795812
...
46266ea337
Author | SHA1 | Date | |
---|---|---|---|
|
46266ea337 | ||
|
a70a5c6ab2 | ||
|
d838fbe290 | ||
|
a2f5d9bf38 |
@ -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'
|
||||
])
|
||||
}
|
||||
})
|
@ -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" />
|
||||
|
@ -686,20 +686,23 @@ Channel:
|
||||
Shorts:
|
||||
This channel does not currently have any shorts: ''
|
||||
Live:
|
||||
Live: ''
|
||||
This channel does not currently have any live streams: ''
|
||||
Live: 'Regstreeks'
|
||||
This channel does not currently have any live streams: 'Hierdie kanaal het tans
|
||||
geen rekstreekse stromings nie'
|
||||
Playlists:
|
||||
Playlists: ''
|
||||
This channel does not currently have any playlists: ''
|
||||
Playlists: 'Afspeellyste'
|
||||
This channel does not currently have any playlists: 'Hierdie kanaal het tans geen
|
||||
afspeellyste nie'
|
||||
Sort Types:
|
||||
Last Video Added: ''
|
||||
Newest: ''
|
||||
Oldest: ''
|
||||
Last Video Added: 'Laaste toegevoegde video'
|
||||
Newest: 'Nuutste'
|
||||
Oldest: 'Oudste'
|
||||
Podcasts:
|
||||
Podcasts: ''
|
||||
This channel does not currently have any podcasts: ''
|
||||
Podcasts: 'Podsendings'
|
||||
This channel does not currently have any podcasts: 'Hierdie kanaal het tans geen
|
||||
podsendings nie'
|
||||
Releases:
|
||||
Releases: ''
|
||||
Releases: 'Vrystellings'
|
||||
This channel does not currently have any releases: 'Hierdie kanaal het nie tans
|
||||
enige vrystellings nie'
|
||||
About:
|
||||
|
Loading…
Reference in New Issue
Block a user