Finish Core Subscriptions logic and add more locales to profiles page
This commit is contained in:
		
							parent
							
								
									1e035105d1
								
							
						
					
					
						commit
						8f35f95a5b
					
				|  | @ -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", | ||||||
|  |  | ||||||
|  | @ -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", | ||||||
|  |  | ||||||
|  | @ -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) => { | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -44,7 +44,9 @@ | ||||||
|               {{ profileInitials[index] }} |               {{ profileInitials[index] }} | ||||||
|             </p> |             </p> | ||||||
|           </div> |           </div> | ||||||
|           <p> |           <p | ||||||
|  |             class="profileName" | ||||||
|  |           > | ||||||
|             {{ profile.name }} |             {{ profile.name }} | ||||||
|           </p> |           </p> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  | @ -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' | ||||||
|  |     ]) | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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') | ||||||
|  |  | ||||||
|  | @ -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) { | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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%; | ||||||
|  |  | ||||||
|  | @ -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' | ||||||
|     ]), |     ]), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue