Finish Core Subscriptions logic and add more locales to profiles page

This commit is contained in:
Preston 2020-09-01 23:20:21 -04:00
parent 1e035105d1
commit 8f35f95a5b
18 changed files with 373 additions and 70 deletions

52
package-lock.json generated
View File

@ -11136,6 +11136,14 @@
} }
} }
}, },
"javascript-time-ago": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.0.13.tgz",
"integrity": "sha512-zH+obXUQ4vlc9UlERFe637rNJQaVYLizwODUfGzYN/cNW/owkk5wzb327gAfEXFpI4yhFcStEaoqoJtMGAmrAg==",
"requires": {
"relative-time-format": "^0.1.3"
}
},
"jest": { "jest": {
"version": "26.4.2", "version": "26.4.2",
"resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz", "resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz",
@ -16240,6 +16248,11 @@
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
"dev": true "dev": true
}, },
"relative-time-format": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-0.1.3.tgz",
"integrity": "sha512-0O6i4fKjsx8qhz57zorG+LrIDnF9pSvP5s7H9R1Nb5nSqih5dvRyKzNKs6MxhL3bv4iwsz4DuDwAyw+c47QFIA=="
},
"remove-trailing-separator": { "remove-trailing-separator": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@ -16522,6 +16535,31 @@
"sprintf-js": "^1.1.2" "sprintf-js": "^1.1.2"
} }
}, },
"rss-parser": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.9.0.tgz",
"integrity": "sha512-wlRSfGrotOXuWo19Dtl2KmQt7o9i5zzCExUrxpechE0O54BAx7JD+xhWyGumPPqiJj771ndflV3sE3bTHen0HQ==",
"requires": {
"entities": "^2.0.3",
"xml2js": "^0.4.19"
},
"dependencies": {
"entities": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
}
}
},
"rss-to-json": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/rss-to-json/-/rss-to-json-1.1.1.tgz",
"integrity": "sha512-d+TwrFI5wAHbZ/fTd3Pvty14tadBjKHAjfMcUam9FWoWrC9g5rHJN9Slw10OZwk6Mey+hqdXwdmymO7d8ebVmw==",
"requires": {
"axios": "^0.19.2",
"xml2json": "^0.12.0"
}
},
"rsvp": { "rsvp": {
"version": "4.8.5", "version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@ -20249,6 +20287,15 @@
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
"dev": true "dev": true
}, },
"xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
}
},
"xml2json": { "xml2json": {
"version": "0.12.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.12.0.tgz", "resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.12.0.tgz",
@ -20259,6 +20306,11 @@
"node-expat": "^2.3.18" "node-expat": "^2.3.18"
} }
}, },
"xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
},
"xmlchars": { "xmlchars": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",

View File

@ -25,6 +25,7 @@
"mediaelement": "^4.2.16", "mediaelement": "^4.2.16",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"opml-to-json": "0.0.3", "opml-to-json": "0.0.3",
"rss-parser": "^3.9.0",
"video.js": "7.6.6", "video.js": "7.6.6",
"videojs-abloop": "^1.1.2", "videojs-abloop": "^1.1.2",
"videojs-contrib-quality-levels": "^2.0.9", "videojs-contrib-quality-levels": "^2.0.9",

View File

@ -255,7 +255,8 @@ export default Vue.extend({
liveStreamString: this.$t('Video.Watching'), liveStreamString: this.$t('Video.Watching'),
upcomingString: this.$t('Video.Published.Upcoming'), upcomingString: this.$t('Video.Published.Upcoming'),
isLive: this.isLive, isLive: this.isLive,
isUpcoming: this.data.isUpcoming isUpcoming: this.data.isUpcoming,
isRSS: this.data.isRSS
}).then((data) => { }).then((data) => {
this.uploadedTime = data this.uploadedTime = data
}).catch((error) => { }).catch((error) => {

View File

@ -1,6 +1,6 @@
.colorOption { .colorOption {
width: 50px; width: 40px;
height: 50px; height: 40px;
margin: 10px; margin: 10px;
cursor: pointer; cursor: pointer;
border-radius: 200px 200px 200px 200px; border-radius: 200px 200px 200px 200px;
@ -8,10 +8,10 @@
} }
.initial { .initial {
font-size: 25px; font-size: 20px;
text-align: center; text-align: center;
position: relative; position: relative;
bottom: 27px; bottom: 30px;
} }
#profileList { #profileList {
@ -39,10 +39,18 @@
.profile { .profile {
cursor: pointer; cursor: pointer;
height: 50px;
-webkit-transition: background 0.2s ease-out;
-moz-transition: background 0.2s ease-out;
-o-transition: background 0.2s ease-out;
transition: background 0.2s ease-out;
} }
.profile:hover { .profile:hover {
background-color: var(--side-nav-hover-color); background-color: var(--side-nav-hover-color);
-moz-transition: background 0.2s ease-in;
-o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
} }
.profile .colorOption { .profile .colorOption {
@ -51,6 +59,10 @@
bottom: 5px; bottom: 5px;
} }
.profileName {
line-height: 50px;
}
.profileListTitle { .profileListTitle {
position: absolute; position: absolute;
top: -15px; top: -15px;

View File

@ -36,6 +36,16 @@ export default Vue.extend({
} }
}, },
mounted: function () { mounted: function () {
setTimeout(() => {
const profileIndex = this.profileList.findIndex((profile) => {
return profile._id === this.defaultProfile
})
if (profileIndex !== -1) {
this.updateActiveProfile(profileIndex)
}
}, 100)
$('#profileList').focusout(() => { $('#profileList').focusout(() => {
$('#profileList')[0].style.display = 'none' $('#profileList')[0].style.display = 'none'
}) })
@ -65,8 +75,9 @@ export default Vue.extend({
return return
} }
this.updateActiveProfile(index) this.updateActiveProfile(index)
const message = this.$t('Profile.$ is now the active profile').replace('$', profile.name)
this.showToast({ this.showToast({
message: `${profile.name} is now the active profile` message: message
}) })
$('#profileList').focusout() $('#profileList').focusout()
}, },

View File

@ -44,7 +44,9 @@
{{ profileInitials[index] }} {{ profileInitials[index] }}
</p> </p>
</div> </div>
<p> <p
class="profileName"
>
{{ profile.name }} {{ profile.name }}
</p> </p>
</div> </div>

View File

@ -1,4 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue' import FtCard from '../ft-card/ft-card.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue' import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import FtButton from '../ft-button/ft-button.vue' import FtButton from '../ft-button/ft-button.vue'
@ -27,9 +28,18 @@ export default Vue.extend({
] ]
} }
}, },
methods: { computed: {
goToChannel: function () { hideWatchedSubs: function () {
console.log('TODO: Handle goToChannel') return this.$store.getters.getHideWatchedSubs
},
useRssFeeds: function () {
return this.$store.getters.getUseRssFeeds
} }
},
methods: {
...mapActions([
'updateHideWatchedSubs',
'updateUseRssFeeds'
])
} }
}) })

View File

@ -5,16 +5,24 @@
<h3 <h3
class="videoTitle" class="videoTitle"
> >
{{ title }} {{ $t("Settings.Subscription Settings.Subscription Settings") }}
</h3> </h3>
<ft-flex-box class="subscriptionSettingsFlexBox"> <ft-flex-box class="subscriptionSettingsFlexBox">
<ft-toggle-switch <ft-toggle-switch
label="Hide Videos When Watched" :label="$t('Settings.Subscription Settings.Hide Videos on Watch')"
:default-value="hideWatchedSubs"
@change="updateHideWatchedSubs"
/>
<ft-toggle-switch
:label="$t('Settings.Subscription Settings.Fetch Feeds from RSS')"
:default-value="useRssFeeds"
@change="updateUseRssFeeds"
/> />
</ft-flex-box> </ft-flex-box>
<br> <br>
<ft-flex-box> <ft-flex-box>
<ft-select <ft-select
v-if="false"
placeholder="Subscription View Type" placeholder="Subscription View Type"
:value="viewValues[0]" :value="viewValues[0]"
:select-names="viewNames" :select-names="viewNames"
@ -24,6 +32,7 @@
<br> <br>
<ft-flex-box> <ft-flex-box>
<ft-button <ft-button
v-if="false"
label="Manage My Subscriptions" label="Manage My Subscriptions"
/> />
</ft-flex-box> </ft-flex-box>

View File

@ -57,6 +57,7 @@ const state = {
debugMode: false, debugMode: false,
disctractionFreeMode: false, disctractionFreeMode: false,
hideWatchedSubs: false, hideWatchedSubs: false,
useRssFeeds: false,
usingElectron: true usingElectron: true
} }
@ -157,13 +158,21 @@ const getters = {
return state.defaultQuality return state.defaultQuality
}, },
getHideWatchedSubs: () => {
return state.hideWatchedSubs
},
getUseRssFeeds: () => {
return state.useRssFeeds
},
getUsingElectron: () => { getUsingElectron: () => {
return state.usingElectron return state.usingElectron
} }
} }
const actions = { const actions = {
grabUserSettings ({ dispatch, commit }) { grabUserSettings ({ dispatch, commit, rootState }) {
settingsDb.find({}, (err, results) => { settingsDb.find({}, (err, results) => {
if (!err) { if (!err) {
console.log(results) console.log(results)
@ -206,6 +215,12 @@ const actions = {
case 'barColor': case 'barColor':
commit('setBarColor', result.value) commit('setBarColor', result.value)
break break
case 'hideWatchedSubs':
commit('setHideWatchedSubs', result.value)
break
case 'useRssFeeds':
commit('setUseRssFeeds', result.value)
break
case 'rememberHistory': case 'rememberHistory':
commit('setRememberHistory', result.value) commit('setRememberHistory', result.value)
break break
@ -341,6 +356,22 @@ const actions = {
}) })
}, },
updateHideWatchedSubs ({ commit }, hideWatchedSubs) {
settingsDb.update({ _id: 'hideWatchedSubs' }, { _id: 'hideWatchedSubs', value: hideWatchedSubs }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setHideWatchedSubs', hideWatchedSubs)
}
})
},
updateUseRssFeeds ({ commit }, useRssFeeds) {
settingsDb.update({ _id: 'useRssFeeds' }, { _id: 'useRssFeeds', value: useRssFeeds }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setUseRssFeeds', useRssFeeds)
}
})
},
updateRememberHistory ({ commit }, history) { updateRememberHistory ({ commit }, history) {
settingsDb.update({ _id: 'rememberHistory' }, { _id: 'rememberHistory', value: history }, { upsert: true }, (err, numReplaced) => { settingsDb.update({ _id: 'rememberHistory' }, { _id: 'rememberHistory', value: history }, { upsert: true }, (err, numReplaced) => {
if (!err) { if (!err) {
@ -546,6 +577,9 @@ const mutations = {
setHideWatchedSubs (state, hideWatchedSubs) { setHideWatchedSubs (state, hideWatchedSubs) {
state.hideWatchedSubs = hideWatchedSubs state.hideWatchedSubs = hideWatchedSubs
}, },
setUseRssFeeds (state, useRssFeeds) {
state.useRssFeeds = useRssFeeds
},
setUsingElectron (state, usingElectron) { setUsingElectron (state, usingElectron) {
state.usingElectron = usingElectron state.usingElectron = usingElectron
}, },

View File

@ -1,7 +1,5 @@
import ytch from 'yt-channel-info'
const state = { const state = {
subscriptions: [], allSubscriptionsList: [],
profileSubscriptions: { profileSubscriptions: {
activeProfile: 0, activeProfile: 0,
videoList: [] videoList: []
@ -9,8 +7,8 @@ const state = {
} }
const getters = { const getters = {
getSubscriptions: () => { getAllSubscriptionsList: () => {
return state.subscriptions return state.allSubscriptionsList
}, },
getProfileSubscriptions: () => { getProfileSubscriptions: () => {
return state.profileSubscriptions return state.profileSubscriptions
@ -18,8 +16,8 @@ const getters = {
} }
const actions = { const actions = {
updateSubscriptions ({ commit }, subscriptions) { updateAllSubscriptionsList ({ commit }, subscriptions) {
commit('setSubscriptions', subscriptions) commit('setAllSubscriptionsList', subscriptions)
}, },
updateProfileSubscriptions ({ commit }, subscriptions) { updateProfileSubscriptions ({ commit }, subscriptions) {
commit('setProfileSubscriptions', subscriptions) commit('setProfileSubscriptions', subscriptions)
@ -27,8 +25,8 @@ const actions = {
} }
const mutations = { const mutations = {
setSubscriptions (state, subscriptions) { setAllSubscriptionsList (state, allSubscriptionsList) {
state.subscriptions = subscriptions state.allSubscriptionsList = allSubscriptionsList
}, },
setProfileSubscriptions (state, profileSubscriptions) { setProfileSubscriptions (state, profileSubscriptions) {
state.profileSubscriptions = profileSubscriptions state.profileSubscriptions = profileSubscriptions

View File

@ -265,6 +265,8 @@ const actions = {
} else if (payload.isUpcoming || payload.publishText === null) { } else if (payload.isUpcoming || payload.publishText === null) {
// the check for null is currently just an inferring of knowledge, because there is no other possibility left // the check for null is currently just an inferring of knowledge, because there is no other possibility left
return payload.upcomingString return payload.upcomingString
} else if (payload.isRSS) {
return payload.publishText
} }
const strings = payload.publishText.split(' ') const strings = payload.publishText.split(' ')
const singular = (strings[0] === '1') const singular = (strings[0] === '1')

View File

@ -63,7 +63,7 @@ export default Vue.extend({
this.isLoading = true this.isLoading = true
const profileType = this.$route.name const profileType = this.$route.name
this.deletePromptLabel = 'Are you sure you want to delete this profile? All subscriptions in this profile will also be deleted.' this.deletePromptLabel = `${this.$t('Profile.Are you sure you want to delete this profile?')} ${this.$t('Profile["All subscriptions will also be deleted."]')}`
if (profileType === 'newProfile') { if (profileType === 'newProfile') {
this.isNew = true this.isNew = true
@ -78,7 +78,7 @@ export default Vue.extend({
this.grabProfileInfo(this.profileId).then((profile) => { this.grabProfileInfo(this.profileId).then((profile) => {
if (profile === null) { if (profile === null) {
this.showToast({ this.showToast({
message: 'Profile could not be found' message: this.$t('Profile.Profile could not be found')
}) })
this.$router.push({ this.$router.push({
path: '/settings/profile/' path: '/settings/profile/'
@ -108,7 +108,7 @@ export default Vue.extend({
saveProfile: function () { saveProfile: function () {
if (this.profileName === '') { if (this.profileName === '') {
this.showToast({ this.showToast({
message: 'Your profile name cannot be empty' message: this.$t('Profile.Your profile name cannot be empty')
}) })
return return
} }
@ -129,34 +129,36 @@ export default Vue.extend({
if (this.isNew) { if (this.isNew) {
this.showToast({ this.showToast({
message: 'Profile has been created' message: this.$t('Profile.Profile has been created')
}) })
this.$router.push({ this.$router.push({
path: '/settings/profile/' path: '/settings/profile/'
}) })
} else { } else {
this.showToast({ this.showToast({
message: 'Profile has been updated' message: this.$t('Profile.Profile has been updated')
}) })
} }
}, },
setDefaultProfile: function () { setDefaultProfile: function () {
this.updateDefaultProfile(this.profileId) this.updateDefaultProfile(this.profileId)
const message = this.$t('Profile.Your default profile has been set to $').replace('$', this.profileName)
this.showToast({ this.showToast({
message: `Your default profile has been set to ${this.profileName}` message: message
}) })
}, },
deleteProfile: function () { deleteProfile: function () {
this.removeProfile(this.profileId) this.removeProfile(this.profileId)
const message = this.$t('Profile.Removed $ from your profiles').replace('$', this.profileName)
this.showToast({ this.showToast({
message: `Removed ${this.profileName} from your profiles` message: message
}) })
if (this.defaultProfile === this.profileId) { if (this.defaultProfile === this.profileId) {
this.updateDefaultProfile('allChannels') this.updateDefaultProfile('allChannels')
this.showToast({ this.showToast({
message: 'Your default profile has been set your Primary profile' message: this.$t('Profile.Your default profile has been changed to your primary profile')
}) })
} }
if (this.activeProfile._id === this.profileId) { if (this.activeProfile._id === this.profileId) {

View File

@ -16,7 +16,7 @@
</ft-flex-box> </ft-flex-box>
<ft-flex-box> <ft-flex-box>
<ft-button <ft-button
label="Create New Profile" :label="$t('Profile.Create New Profile')"
@click="newProfile" @click="newProfile"
/> />
</ft-flex-box> </ft-flex-box>

View File

@ -3,6 +3,7 @@
<general-settings /> <general-settings />
<theme-settings /> <theme-settings />
<player-settings /> <player-settings />
<subscription-settings />
<privacy-settings /> <privacy-settings />
</div> </div>
</template> </template>

View File

@ -8,6 +8,12 @@
color: var(--tertiary-text-color); color: var(--tertiary-text-color);
} }
.floatingTopButton {
position: absolute;
top: 70px;
right: 10px;
}
@media only screen and (max-width: 680px) { @media only screen and (max-width: 680px) {
.card { .card {
width: 90%; width: 90%;

View File

@ -2,16 +2,21 @@ import Vue from 'vue'
import { mapActions, mapMutations } from 'vuex' import { mapActions, mapMutations } from 'vuex'
import FtLoader from '../../components/ft-loader/ft-loader.vue' import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtCard from '../../components/ft-card/ft-card.vue' import FtCard from '../../components/ft-card/ft-card.vue'
import FtButton from '../../components/ft-button/ft-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 ytch from 'yt-channel-info' import ytch from 'yt-channel-info'
import Parser from 'rss-parser'
export default Vue.extend({ export default Vue.extend({
name: 'Subscriptions', name: 'Subscriptions',
components: { components: {
'ft-loader': FtLoader, 'ft-loader': FtLoader,
'ft-card': FtCard, 'ft-card': FtCard,
'ft-button': FtButton,
'ft-icon-button': FtIconButton,
'ft-flex-box': FtFlexBox, 'ft-flex-box': FtFlexBox,
'ft-element-list': FtElementList 'ft-element-list': FtElementList
}, },
@ -23,6 +28,10 @@ export default Vue.extend({
} }
}, },
computed: { computed: {
usingElectron: function () {
return this.$store.getters.getUsingElectron
},
backendPreference: function () { backendPreference: function () {
return this.$store.getters.getBackendPreference return this.$store.getters.getBackendPreference
}, },
@ -31,10 +40,30 @@ export default Vue.extend({
return this.$store.getters.getBackendFallback return this.$store.getters.getBackendFallback
}, },
invidiousInstance: function () {
return this.$store.getters.getInvidiousInstance
},
hideWatchedSubs: function () {
return this.$store.getters.getHideWatchedSubs
},
useRssFeeds: function () {
return this.$store.getters.getUseRssFeeds
},
profileList: function () { profileList: function () {
return this.$store.getters.getProfileList return this.$store.getters.getProfileList
}, },
activeVideoList: function () {
if (this.videoList.length < this.dataLimit) {
return this.videoList
} else {
return this.videoList.slice(0, this.dataLimit)
}
},
activeProfile: function () { activeProfile: function () {
return this.$store.getters.getActiveProfile return this.$store.getters.getActiveProfile
}, },
@ -43,50 +72,99 @@ export default Vue.extend({
return this.$store.getters.getProfileSubscriptions return this.$store.getters.getProfileSubscriptions
}, },
allSubscriptionsList: function () {
return this.$store.getters.getAllSubscriptionsList
},
historyCache: function () {
return this.$store.getters.getHistoryCache
},
activeSubscriptionList: function () { activeSubscriptionList: function () {
return this.profileList[this.activeProfile].subscriptions return this.profileList[this.activeProfile].subscriptions
},
allSubscriptionsList: function () {
return this.profileList[0].subscriptions
},
sortedVideoList: function () {
const profileSubscriptions = JSON.parse(JSON.stringify(this.profileSubscriptions))
return profileSubscriptions.videoList.sort((a, b) => {
if (a.title.toLowerCase() > b.title.toLowerCase()) {
return -1
} }
},
if (a.title.toLowerCase() < b.title.toLowerCase()) { watch: {
return 1 activeProfile: async function (val) {
} if (this.allSubscriptionsList.length !== 0) {
this.isLoading = true
console.log(a.title) this.videoList = await Promise.all(this.allSubscriptionsList.filter((video) => {
const channelIndex = this.activeSubscriptionList.findIndex((x) => {
return 0 return x.id === video.authorId
}) })
const historyIndex = this.historyCache.findIndex((x) => {
return x.videoId === video.videoId
})
if (this.hideWatchedSubs) {
return channelIndex !== -1 && historyIndex === -1
} else {
return channelIndex !== -1
}
}))
this.isLoading = false
} else {
this.getSubscriptions()
}
} }
}, },
mounted: function () { mounted: async function () {
setTimeout(() => { this.isLoading = true
this.fetchActiveSubscriptionsLocal() const dataLimit = sessionStorage.getItem('subscriptionLimit')
}, 1000) if (dataLimit !== null) {
this.dataLimit = dataLimit
}
setTimeout(async () => {
if (this.profileSubscriptions.videoList.length === 0) {
this.getSubscriptions()
} else {
const subscriptionList = JSON.parse(JSON.stringify(this.profileSubscriptions))
if (this.hideWatchedSubs) {
this.videoList = await Promise.all(subscriptionList.videoList.filter((video) => {
const historyIndex = this.historyCache.findIndex((x) => {
return x.videoId === video.videoId
})
return historyIndex === -1
}))
} else {
this.videoList = subscriptionList.videoList
}
this.isLoading = false
}
}, 200)
}, },
methods: { methods: {
fetchActiveSubscriptionsLocal: function () { getSubscriptions: function () {
if (this.activeSubscriptionList.length === 0) { if (this.activeSubscriptionList.length === 0) {
this.isLoading = false
this.videoList = []
return return
} }
this.isLoading = true this.isLoading = true
this.updateShowProgressBar(true) this.updateShowProgressBar(true)
this.setProgressBarPercentage(0)
let videoList = [] let videoList = []
let channelCount = 0 let channelCount = 0
this.activeSubscriptionList.forEach(async (channel) => { this.activeSubscriptionList.forEach(async (channel) => {
const videos = await this.getChannelVideosLocalScraper(channel.id) let videos = []
console.log(videos)
if (!this.usingElectron || this.backendPreference === 'invidious') {
if (this.useRssFeeds) {
videos = await this.getChannelVideosInvidiousRSS(channel.id)
} else {
videos = await this.getChannelVideosInvidiousScraper(channel.id)
}
} else {
if (this.useRssFeeds) {
videos = await this.getChannelVideosLocalRSS(channel.id)
} else {
videos = await this.getChannelVideosLocalScraper(channel.id)
}
}
videoList = videoList.concat(videos) videoList = videoList.concat(videos)
channelCount++ channelCount++
@ -103,9 +181,24 @@ export default Vue.extend({
videoList: videoList videoList: videoList
} }
this.videoList = await Promise.all(videoList.filter((video) => {
if (this.hideWatchedSubs) {
const historyIndex = this.historyCache.findIndex((x) => {
return x.videoId === video.videoId
})
return historyIndex === -1
} else {
return true
}
}))
this.updateProfileSubscriptions(profileSubscriptions) this.updateProfileSubscriptions(profileSubscriptions)
this.isLoading = false this.isLoading = false
this.updateShowProgressBar(false) this.updateShowProgressBar(false)
if (this.activeProfile === 0) {
this.updateAllSubscriptionsList(profileSubscriptions.videoList)
}
} }
}) })
}, },
@ -135,25 +228,75 @@ export default Vue.extend({
}, },
getChannelVideosLocalRSS: function (channelId) { getChannelVideosLocalRSS: function (channelId) {
console.log('TODO') return new Promise((resolve, reject) => {
}, const parser = new Parser()
const feedUrl = `https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`
fetchActiveSubscriptionsInvidious: function () { parser.parseURL(feedUrl).then(async (feed) => {
console.log('TODO') resolve(await Promise.all(feed.items.map((video) => {
video.authorId = channelId
video.videoId = video.id.replace('yt:video:', '')
video.type = 'video'
video.publishedDate = new Date(video.pubDate)
video.publishedText = video.publishedDate.toLocaleString()
video.lengthSeconds = '0:00'
video.isRSS = true
return video
})))
}).catch((err) => {
console.log(err)
resolve([])
})
})
}, },
getChannelVideosInvidiousScraper: function (channelId) { getChannelVideosInvidiousScraper: function (channelId) {
console.log('TODO') return new Promise((resolve, reject) => {
const subscriptionsPayload = {
resource: 'channels/latest',
id: channelId,
params: {}
}
this.invidiousAPICall(subscriptionsPayload).then((result) => {
resolve(result)
})
})
}, },
getChannelVideosInvidiousRSS: function (channelId) { getChannelVideosInvidiousRSS: function (channelId) {
console.log('TODO') return new Promise((resolve, reject) => {
const parser = new Parser()
const feedUrl = `${this.invidiousInstance}/feed/channel/${channelId}`
parser.parseURL(feedUrl).then(async (feed) => {
resolve(await Promise.all(feed.items.map((video) => {
video.authorId = channelId
video.videoId = video.id.replace('yt:video:', '')
video.type = 'video'
video.publishedDate = new Date(video.pubDate)
video.publishedText = video.publishedDate.toLocaleString()
video.lengthSeconds = '0:00'
video.isRSS = true
return video
})))
})
})
},
increaseLimit: function () {
this.dataLimit += 100
sessionStorage.setItem('subscriptionLimit', this.dataLimit)
}, },
...mapActions([ ...mapActions([
'showToast', 'showToast',
'invidiousAPICall',
'updateShowProgressBar', 'updateShowProgressBar',
'updateProfileSubscriptions', 'updateProfileSubscriptions',
'updateAllSubscriptionsList',
'calculatePublishedDate' 'calculatePublishedDate'
]), ]),

View File

@ -10,20 +10,20 @@
> >
<h3>{{ $t("Subscriptions.Subscriptions") }}</h3> <h3>{{ $t("Subscriptions.Subscriptions") }}</h3>
<ft-flex-box <ft-flex-box
v-if="profileSubscriptions.videoList.length === 0" v-if="activeVideoList.length === 0"
> >
<p class="message"> <p class="message">
{{ $t("History['Your history list is currently empty.']") }} {{ $t("Subscriptions['Your Subscription list is currently empty. Start adding subscriptions to see them here.']") }}
</p> </p>
</ft-flex-box> </ft-flex-box>
<ft-element-list <ft-element-list
v-else v-else
:data="profileSubscriptions.videoList" :data="activeVideoList"
/> />
<ft-flex-box <ft-flex-box
v-if="false"
> >
<ft-button <ft-button
v-if="videoList.length > dataLimit"
label="Load More" label="Load More"
background-color="var(--primary-color)" background-color="var(--primary-color)"
text-color="var(--text-with-main-color)" text-color="var(--text-with-main-color)"
@ -31,6 +31,15 @@
/> />
</ft-flex-box> </ft-flex-box>
</ft-card> </ft-card>
<ft-icon-button
v-if="!isLoading"
icon="sync"
class="floatingTopButton"
:title="$t('Subscriptions.Refresh Subscriptions')"
:size="12"
theme="primary"
@click="getSubscriptions"
/>
</div> </div>
</template> </template>

View File

@ -71,6 +71,7 @@ Subscriptions:
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': Your 'Your Subscription list is currently empty. Start adding subscriptions to see them here.': Your
Subscription list is currently empty. Start adding subscriptions to see them here. Subscription list is currently empty. Start adding subscriptions to see them here.
'Getting Subscriptions. Please wait.': Getting Subscriptions. Please wait. 'Getting Subscriptions. Please wait.': Getting Subscriptions. Please wait.
Refresh Subscriptions: Refresh Subscriptions
Trending: Trending Trending: Trending
Most Popular: Most Popular Most Popular: Most Popular
Playlists: Playlists Playlists: Playlists
@ -180,6 +181,7 @@ Settings:
Subscription Settings: Subscription Settings:
Subscription Settings: Subscription Settings Subscription Settings: Subscription Settings
Hide Videos on Watch: Hide Videos on Watch Hide Videos on Watch: Hide Videos on Watch
Fetch Feeds from RSS: Fetch Feeds from RSS
Subscriptions Export Format: Subscriptions Export Format:
Subscriptions Export Format: Subscriptions Export Format Subscriptions Export Format: Subscriptions Export Format
#& Freetube #& Freetube
@ -260,6 +262,14 @@ Profile:
Delete Profile: Delete Profile Delete Profile: Delete Profile
Are you sure you want to delete this profile?: Are you sure you want to delete this profile? Are you sure you want to delete this profile?: Are you sure you want to delete this profile?
All subscriptions will also be deleted.: All subscriptions will also be deleted. All subscriptions will also be deleted.: All subscriptions will also be deleted.
Profile could not be found: Profile could not be found
Your profile name cannot be empty: Your profile name cannot be empty
Profile has been created: Profile has been created
Profile has been updated: Profile has been updated
Your default profile has been set to $: Your default profile has been set to $
Removed $ from your profiles: Removed $ from your profiles
Your default profile has been changed to your primary profile: Your default profile has been changed to your primary profile
$ is now the active profile: $ is now the active profile
#On Channel Page #On Channel Page
Channel: Channel:
Subscriber: Subscriber Subscriber: Subscriber