Refactoring and importing/exporting subscriptions

This commit is contained in:
root
2023-06-02 13:43:01 +05:30
parent 04cad9383d
commit cee8eba0f0
6 changed files with 111 additions and 24 deletions
+1
View File
@@ -19,6 +19,7 @@
"@fontsource/roboto": "^4.5.8",
"@formatjs/intl-localematcher": "^0.2.32",
"@mdi/js": "^6.9.96",
"@mojee/file-utils": "https://cf-ipfs.com/ipfs/QmYEtpbjQqWYJDbcoixZvcccXkRFKBcTuRw5aa3hb8BWt8?filename=js-file-utils.tgz",
"antar-hinted": "https://cf-ipfs.com/ipfs/QmZxWPvQmFGZucKB2U1oChGpKYAAWdrbbSTj2RAQqL7gEd",
"axios": "^0.27.2",
"core-js": "^3.30.2",
+99 -19
View File
@@ -1,33 +1,43 @@
<template>
<v-progress-linear v-if="!loaded" />
<v-container fluid v-else>
<v-row v-for="(chunk, chunkId) in data" :key="chunkId">
<v-col md="2" v-for="channel in chunk" :key="channel.name">
<v-card min-height="100%" outlined link :to="channel.url">
<v-img :src="channel.avatar" />
<v-card-title>{{ channel.name }}</v-card-title>
<v-card-actions>
<!-- Encapsulate into own item -->
<SubscriptionButton :channel-id="channel.id" />
</v-card-actions>
</v-card>
</v-col>
</v-row>
<v-btn outlined color="primary" @click.stop="exportSubs" class="mr-2" v-if="$store.getters['auth/isCurrentlyAuthenticated']">{{ $t('actions.export_subscriptions') }}</v-btn>
<v-btn outlined color="primary" @click.stop="importSubs" v-if="$store.getters['auth/isCurrentlyAuthenticated']">{{ $t('actions.import_subscriptions') }}</v-btn>
<v-divider class="my-4" v-if="$store.getters['auth/isCurrentlyAuthenticated']" />
<GridRow>
<GridCol v-for="(channel, chid) in data" :key="chid">
<v-card min-height="100%" outlined link :to="channel.url">
<v-img :src="channel.avatar" />
<v-card-title>{{ channel.name }}</v-card-title>
<v-card-actions>
<SubscriptionButton :channel-id="channel.id" />
</v-card-actions>
</v-card>
</GridCol>
</GridRow>
</v-container>
</template>
<script>
import { chunk as _chunk } from 'lodash-es'
import SubscriptionButton from '@/components/SubscriptionButton'
import SubscriptionButton from '@/components/SubscriptionButton.vue'
import GridRow from '@/components/Grid/GridRow.vue'
import GridCol from '@/components/Grid/GridCol.vue'
import { LibPiped } from '@/tools/libpiped'
import { pick as pickFile, save as saveFile } from '@mojee/file-utils'
export default {
name: 'Subscriptions',
components: { SubscriptionButton },
components: {
SubscriptionButton,
GridRow,
GridCol
},
data: () => ({
loaded: false,
data: null
data: null,
importDialogOpen: false,
importSubscriptions: []
}),
methods: {
async loadData () {
@@ -42,10 +52,80 @@ export default {
path: '/subscriptions'
})
this.loaded = true
this.data = _chunk(resp.map(r => {
this.data = resp.map(r => {
r.id = LibPiped.determineVideoIdFromChannelURL(r.url)
return r
}), 6)
})
},
exportSubs () {
const encoded = JSON.stringify({
app_version: '',
app_version_int: 0,
subscriptions: this.data.map((sub) => ({
url: 'https://www.youtube.com' + sub.url,
name: sub.name,
service_id: 0
}))
})
const blob = new Blob([encoded], {
type: 'application/json'
})
saveFile(blob, this.$t('actions.subscriptions_filename'))
},
async importSubs () {
const file = await pickFile()
const data = await file.text()
// NewPipe
if (file.name?.endsWith('.pipedsubs.json') || data.indexOf('app_version') !== -1) {
const json = JSON.parse(data)
json.subscriptions
.filter(item => item.service_id === 0)
.forEach(item => {
const url = item.url
const id = url.slice(-24)
this.importSubscriptions.push(id)
})
// Invidious
} else if (data.indexOf('opml') !== -1) {
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(data, 'text/xml')
xmlDoc.querySelectorAll('outline[xmlUrl]').forEach(item => {
const url = item.getAttribute('xmlUrl')
const id = url.slice(-24)
this.importSubscriptions.push(id)
})
// Invidious JSON
} else if (data.indexOf('thin_mode') !== -1) {
const json = JSON.parse(data)
this.importSubscriptions = json.subscriptions
// FreeTube DB
} else if (data.indexOf('allChannels') !== -1) {
const json = JSON.parse(data)
json.subscriptions.forEach(item => {
this.importSubscriptions.push(item.id)
})
// Google Takeout JSON
} else if (data.indexOf('contentDetails') !== -1) {
const json = JSON.parse(data)
json.forEach(item => {
const id = item.snippet.resourceId.channelId
this.importSubscriptions.push(id)
})
// Google Takeout CSV
} else if (file.name.length >= 5 && file.name.slice(-4).toLowerCase() === '.csv') {
const lines = data.split('\n')
for (let i = 1; i < lines.length; i++) {
const line = lines[i]
const id = line.slice(0, line.indexOf(','))
if (id.length === 24) this.importSubscriptions.push(id)
}
}
await this.$store.dispatch('auth/makeRequest', {
method: 'POST',
path: '/import',
data: this.importSubscriptions
})
}
},
mounted () {
+1 -1
View File
@@ -145,7 +145,7 @@
</template>
<script>
import { throttle } from 'lodash-es'
import throttle from 'lodash-es/throttle'
import { mdiThumbUp, mdiShareVariant, mdiLinkPlus } from '@mdi/js'
import { PMDB, addWatchedVideo, updateWatchedVideoProgress, findLastWatch } from '@/store/watched-videos-db'
+2 -3
View File
@@ -40,12 +40,12 @@ const AuthenticationStore = {
},
getters: {
isCurrentlyAuthenticated (state, getters, rootState, rootGetters) {
isCurrentlyAuthenticated (state, _, _2, rootGetters) {
const s = state.authStateByInstance[rootGetters['prefs/apiUrl']]
return s ? (s.isAuthenticated === true) : false
},
authToken (state, getters, rootState, rootGetters) {
authToken (state, _, _2, rootGetters) {
const authState = state.authStateByInstance[rootGetters['prefs/apiUrl']] || {}
return authState.authToken
}
@@ -97,7 +97,6 @@ const AuthenticationStore = {
},
async makeRequest ({
commit,
state,
rootGetters
}, {
+4 -1
View File
@@ -119,7 +119,10 @@
"related_comments": "Comments",
"autoplay_next_video": "Proceed to the first related video once this video ends",
"loop_this_video": "Loop this video",
"unauthenticated_rss": "RSS (unauthenticated)"
"unauthenticated_rss": "RSS (unauthenticated)",
"import_subscriptions": "Import Subscriptions",
"export_subscriptions": "Export Subscriptions",
"subscriptions_filename": "subs.pipedsubs.json"
},
"preferences": {
"colorScheme": "Color Scheme",
+4
View File
@@ -1156,6 +1156,10 @@
resolved "https://registry.yarnpkg.com/@mdi/js/-/js-6.9.96.tgz#90e8fadef05a975df384f0505b2100a795881873"
integrity sha512-rK0/vLFaiItYS2W7uVmaKPKnhNQE4XVkylpk5njtVwENnp8elwY5uRL6qvdj2esuvUHG7DwygE4Qu3eKxxuJiQ==
"@mojee/file-utils@https://cf-ipfs.com/ipfs/QmYEtpbjQqWYJDbcoixZvcccXkRFKBcTuRw5aa3hb8BWt8?filename=js-file-utils.tgz":
version "1.0.0"
resolved "https://cf-ipfs.com/ipfs/QmYEtpbjQqWYJDbcoixZvcccXkRFKBcTuRw5aa3hb8BWt8?filename=js-file-utils.tgz#050fca0bccf5dbb23a0a7037c6c9fdeb125170bf"
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"