Allow Unsubscribing from Deleted Channels (#2283)
* unsub from deleted * reset error message on invidious channel load * fix error channels not showing * Use errorMessage instead of isErrorMessage Co-authored-by: absidue <48293849+absidue@users.noreply.github.com> * Change "Error Channels" to "Channels with Errors" Co-authored-by: absidue <48293849+absidue@users.noreply.github.com> * use find instead of find index Co-Authored-By: absidue <48293849+absidue@users.noreply.github.com> Co-Authored-By: PikachuEXE <pikachuexe@gmail.com> Co-authored-by: absidue <48293849+absidue@users.noreply.github.com> Co-authored-by: PikachuEXE <pikachuexe@gmail.com>
This commit is contained in:
parent
74dc309803
commit
da095adc8c
|
@ -4,7 +4,8 @@ const state = {
|
||||||
allSubscriptionsList: [],
|
allSubscriptionsList: [],
|
||||||
profileSubscriptions: {
|
profileSubscriptions: {
|
||||||
activeProfile: MAIN_PROFILE_ID,
|
activeProfile: MAIN_PROFILE_ID,
|
||||||
videoList: []
|
videoList: [],
|
||||||
|
errorChannels: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ export default Vue.extend({
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
shownElementList: [],
|
shownElementList: [],
|
||||||
apiUsed: '',
|
apiUsed: '',
|
||||||
|
errorMessage: '',
|
||||||
videoSelectValues: [
|
videoSelectValues: [
|
||||||
'newest',
|
'newest',
|
||||||
'oldest',
|
'oldest',
|
||||||
|
@ -90,16 +91,14 @@ export default Vue.extend({
|
||||||
return this.$store.getters.getActiveProfile
|
return this.$store.getters.getActiveProfile
|
||||||
},
|
},
|
||||||
|
|
||||||
isSubscribed: function () {
|
subscriptionInfo: function () {
|
||||||
const subIndex = this.activeProfile.subscriptions.findIndex((channel) => {
|
return this.activeProfile.subscriptions.find((channel) => {
|
||||||
return channel.id === this.id
|
return channel.id === this.id
|
||||||
})
|
}) ?? null
|
||||||
|
},
|
||||||
|
|
||||||
if (subIndex === -1) {
|
isSubscribed: function () {
|
||||||
return false
|
return this.subscriptionInfo !== null
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
subscribedText: function () {
|
subscribedText: function () {
|
||||||
|
@ -251,6 +250,11 @@ export default Vue.extend({
|
||||||
this.apiUsed = 'local'
|
this.apiUsed = 'local'
|
||||||
const expectedId = this.id
|
const expectedId = this.id
|
||||||
ytch.getChannelInfo({ channelId: expectedId }).then((response) => {
|
ytch.getChannelInfo({ channelId: expectedId }).then((response) => {
|
||||||
|
if (response.alertMessage) {
|
||||||
|
this.setErrorMessage(response.alertMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.errorMessage = ''
|
||||||
if (expectedId !== this.id) {
|
if (expectedId !== this.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -401,8 +405,10 @@ export default Vue.extend({
|
||||||
this.bannerUrl = null
|
this.bannerUrl = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.errorMessage = ''
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
this.setErrorMessage(err.responseJSON.error)
|
||||||
console.log(err)
|
console.log(err)
|
||||||
const errorMessage = this.$t('Invidious API Error (Click to copy)')
|
const errorMessage = this.$t('Invidious API Error (Click to copy)')
|
||||||
this.showToast({
|
this.showToast({
|
||||||
|
@ -645,6 +651,16 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setErrorMessage: function (errorMessage) {
|
||||||
|
this.isLoading = false
|
||||||
|
this.errorMessage = errorMessage
|
||||||
|
this.id = this.subscriptionInfo.id
|
||||||
|
this.channelName = this.subscriptionInfo.name
|
||||||
|
this.thumbnailUrl = this.subscriptionInfo.thumbnail
|
||||||
|
this.bannerUrl = null
|
||||||
|
this.subCount = null
|
||||||
|
},
|
||||||
|
|
||||||
handleFetchMore: function () {
|
handleFetchMore: function () {
|
||||||
switch (this.currentTab) {
|
switch (this.currentTab) {
|
||||||
case 'videos':
|
case 'videos':
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
ref="search"
|
ref="search"
|
||||||
>
|
>
|
||||||
<ft-loader
|
<ft-loader
|
||||||
v-if="isLoading"
|
v-if="isLoading && !errorMessage"
|
||||||
:fullscreen="true"
|
:fullscreen="true"
|
||||||
/>
|
/>
|
||||||
<ft-card
|
<ft-card
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ft-flex-box
|
<ft-flex-box
|
||||||
|
v-if="!errorMessage"
|
||||||
class="channelInfoTabs"
|
class="channelInfoTabs"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -112,7 +113,7 @@
|
||||||
</div>
|
</div>
|
||||||
</ft-card>
|
</ft-card>
|
||||||
<ft-card
|
<ft-card
|
||||||
v-if="!isLoading"
|
v-if="!isLoading && !errorMessage"
|
||||||
class="card"
|
class="card"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -194,6 +195,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ft-card>
|
</ft-card>
|
||||||
|
<ft-card
|
||||||
|
v-if="errorMessage"
|
||||||
|
class="card"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{{ errorMessage }}
|
||||||
|
</p>
|
||||||
|
</ft-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
right: 10px;
|
right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channelBubble {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 350px) {
|
@media only screen and (max-width: 350px) {
|
||||||
.floatingTopButton {
|
.floatingTopButton {
|
||||||
position: absolute
|
position: absolute
|
||||||
|
|
|
@ -6,6 +6,7 @@ import FtButton from '../../components/ft-button/ft-button.vue'
|
||||||
import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
|
import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
|
||||||
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||||
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
||||||
|
import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue'
|
||||||
|
|
||||||
import ytch from 'yt-channel-info'
|
import ytch from 'yt-channel-info'
|
||||||
import Parser from 'rss-parser'
|
import Parser from 'rss-parser'
|
||||||
|
@ -19,13 +20,15 @@ export default Vue.extend({
|
||||||
'ft-button': FtButton,
|
'ft-button': FtButton,
|
||||||
'ft-icon-button': FtIconButton,
|
'ft-icon-button': FtIconButton,
|
||||||
'ft-flex-box': FtFlexBox,
|
'ft-flex-box': FtFlexBox,
|
||||||
'ft-element-list': FtElementList
|
'ft-element-list': FtElementList,
|
||||||
|
'ft-channel-bubble': FtChannelBubble
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
dataLimit: 100,
|
dataLimit: 100,
|
||||||
videoList: []
|
videoList: [],
|
||||||
|
errorChannels: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -110,6 +113,7 @@ export default Vue.extend({
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
this.videoList = subscriptionList.videoList
|
this.videoList = subscriptionList.videoList
|
||||||
|
this.errorChannels = subscriptionList.errorChannels
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.getProfileSubscriptions()
|
this.getProfileSubscriptions()
|
||||||
|
@ -123,6 +127,10 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
goToChannel: function (id) {
|
||||||
|
this.$router.push({ path: `/channel/${id}` })
|
||||||
|
},
|
||||||
|
|
||||||
getSubscriptions: function () {
|
getSubscriptions: function () {
|
||||||
if (this.activeSubscriptionList.length === 0) {
|
if (this.activeSubscriptionList.length === 0) {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
|
@ -144,10 +152,9 @@ export default Vue.extend({
|
||||||
|
|
||||||
let videoList = []
|
let videoList = []
|
||||||
let channelCount = 0
|
let channelCount = 0
|
||||||
|
this.errorChannels = []
|
||||||
this.activeSubscriptionList.forEach(async (channel) => {
|
this.activeSubscriptionList.forEach(async (channel) => {
|
||||||
let videos = []
|
let videos = []
|
||||||
|
|
||||||
if (!this.usingElectron || this.backendPreference === 'invidious') {
|
if (!this.usingElectron || this.backendPreference === 'invidious') {
|
||||||
if (useRss) {
|
if (useRss) {
|
||||||
videos = await this.getChannelVideosInvidiousRSS(channel)
|
videos = await this.getChannelVideosInvidiousRSS(channel)
|
||||||
|
@ -174,7 +181,8 @@ export default Vue.extend({
|
||||||
|
|
||||||
const profileSubscriptions = {
|
const profileSubscriptions = {
|
||||||
activeProfile: this.activeProfile._id,
|
activeProfile: this.activeProfile._id,
|
||||||
videoList: videoList
|
videoList: videoList,
|
||||||
|
errorChannels: this.errorChannels
|
||||||
}
|
}
|
||||||
|
|
||||||
this.videoList = await Promise.all(videoList.filter((video) => {
|
this.videoList = await Promise.all(videoList.filter((video) => {
|
||||||
|
@ -226,6 +234,11 @@ export default Vue.extend({
|
||||||
getChannelVideosLocalScraper: function (channel, failedAttempts = 0) {
|
getChannelVideosLocalScraper: function (channel, failedAttempts = 0) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ytch.getChannelVideos({ channelId: channel.id, sortBy: 'latest' }).then(async (response) => {
|
ytch.getChannelVideos({ channelId: channel.id, sortBy: 'latest' }).then(async (response) => {
|
||||||
|
if (response.alertMessage) {
|
||||||
|
this.errorChannels.push(channel)
|
||||||
|
resolve([])
|
||||||
|
return
|
||||||
|
}
|
||||||
const videos = await Promise.all(response.items.map(async (video) => {
|
const videos = await Promise.all(response.items.map(async (video) => {
|
||||||
if (video.liveNow) {
|
if (video.liveNow) {
|
||||||
video.publishedDate = new Date().getTime()
|
video.publishedDate = new Date().getTime()
|
||||||
|
@ -297,33 +310,38 @@ export default Vue.extend({
|
||||||
resolve(items)
|
resolve(items)
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
const errorMessage = this.$t('Local API Error (Click to copy)')
|
if (err.toString().match(/404/)) {
|
||||||
this.showToast({
|
this.errorChannels.push(channel)
|
||||||
message: `${errorMessage}: ${err}`,
|
resolve([])
|
||||||
time: 10000,
|
} else {
|
||||||
action: () => {
|
const errorMessage = this.$t('Local API Error (Click to copy)')
|
||||||
navigator.clipboard.writeText(err)
|
this.showToast({
|
||||||
}
|
message: `${errorMessage}: ${err}`,
|
||||||
})
|
time: 10000,
|
||||||
switch (failedAttempts) {
|
action: () => {
|
||||||
case 0:
|
navigator.clipboard.writeText(err)
|
||||||
resolve(this.getChannelVideosLocalScraper(channel, failedAttempts + 1))
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
if (this.backendFallback) {
|
|
||||||
this.showToast({
|
|
||||||
message: this.$t('Falling back to Invidious API')
|
|
||||||
})
|
|
||||||
resolve(this.getChannelVideosInvidiousRSS(channel, failedAttempts + 1))
|
|
||||||
} else {
|
|
||||||
resolve([])
|
|
||||||
}
|
}
|
||||||
break
|
})
|
||||||
case 2:
|
switch (failedAttempts) {
|
||||||
resolve(this.getChannelVideosLocalScraper(channel, failedAttempts + 1))
|
case 0:
|
||||||
break
|
resolve(this.getChannelVideosLocalScraper(channel, failedAttempts + 1))
|
||||||
default:
|
break
|
||||||
resolve([])
|
case 1:
|
||||||
|
if (this.backendFallback) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Falling back to Invidious API')
|
||||||
|
})
|
||||||
|
resolve(this.getChannelVideosInvidiousRSS(channel, failedAttempts + 1))
|
||||||
|
} else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
resolve(this.getChannelVideosLocalScraper(channel, failedAttempts + 1))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -403,25 +421,30 @@ export default Vue.extend({
|
||||||
navigator.clipboard.writeText(err)
|
navigator.clipboard.writeText(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
switch (failedAttempts) {
|
if (err.toString().match(/500/)) {
|
||||||
case 0:
|
this.errorChannels.push(channel)
|
||||||
resolve(this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1))
|
resolve([])
|
||||||
break
|
} else {
|
||||||
case 1:
|
switch (failedAttempts) {
|
||||||
if (this.backendFallback) {
|
case 0:
|
||||||
this.showToast({
|
resolve(this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1))
|
||||||
message: this.$t('Falling back to the local API')
|
break
|
||||||
})
|
case 1:
|
||||||
resolve(this.getChannelVideosLocalRSS(channel, failedAttempts + 1))
|
if (this.backendFallback) {
|
||||||
} else {
|
this.showToast({
|
||||||
|
message: this.$t('Falling back to the local API')
|
||||||
|
})
|
||||||
|
resolve(this.getChannelVideosLocalRSS(channel, failedAttempts + 1))
|
||||||
|
} else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
resolve(this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1))
|
||||||
|
break
|
||||||
|
default:
|
||||||
resolve([])
|
resolve([])
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
resolve(this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1))
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
resolve([])
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,22 @@
|
||||||
v-else
|
v-else
|
||||||
class="card"
|
class="card"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="errorChannels.length !== 0"
|
||||||
|
>
|
||||||
|
<h3> {{ $t("Subscriptions.Error Channels") }}</h3>
|
||||||
|
<div>
|
||||||
|
<ft-channel-bubble
|
||||||
|
v-for="(channel, index) in errorChannels"
|
||||||
|
:key="index"
|
||||||
|
:channel-name="channel.name"
|
||||||
|
:channel-id="channel.id"
|
||||||
|
:channel-thumbnail="channel.thumbnail"
|
||||||
|
class="channelBubble"
|
||||||
|
@click="goToChannel(channel.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h3>{{ $t("Subscriptions.Subscriptions") }}</h3>
|
<h3>{{ $t("Subscriptions.Subscriptions") }}</h3>
|
||||||
<ft-flex-box
|
<ft-flex-box
|
||||||
v-if="activeVideoList.length === 0"
|
v-if="activeVideoList.length === 0"
|
||||||
|
|
|
@ -78,6 +78,8 @@ Search Filters:
|
||||||
Subscriptions:
|
Subscriptions:
|
||||||
# On Subscriptions Page
|
# On Subscriptions Page
|
||||||
Subscriptions: Subscriptions
|
Subscriptions: Subscriptions
|
||||||
|
# channels that were likely deleted
|
||||||
|
Error Channels: Channels with Errors
|
||||||
Latest Subscriptions: Latest Subscriptions
|
Latest Subscriptions: Latest Subscriptions
|
||||||
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: This
|
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: This
|
||||||
profile has a large number of subscriptions. Forcing RSS to avoid rate limiting
|
profile has a large number of subscriptions. Forcing RSS to avoid rate limiting
|
||||||
|
|
Loading…
Reference in New Issue