mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 12:10:18 +03:00
perf: complete RSS rule settings (#731) @Larsluph
This commit is contained in:
parent
521c6e6c7f
commit
36901701c3
21 changed files with 405 additions and 215 deletions
|
@ -87,10 +87,9 @@
|
|||
<v-row no-gutters class="flex-gap">
|
||||
<v-col>
|
||||
<div class="d-flex flex-column align-center">
|
||||
<p class="subtitle-1 mb-1">{{ $t('modals.add.contentLayout') }}</p>
|
||||
<p class="subtitle-1 mb-1">{{ $t('enums.contentLayout.title') }}</p>
|
||||
<v-select
|
||||
v-model="contentLayout"
|
||||
:label="$t('modals.add.contentLayout')"
|
||||
flat
|
||||
solo
|
||||
dense
|
||||
|
@ -103,10 +102,9 @@
|
|||
</v-col>
|
||||
<v-col>
|
||||
<div class="d-flex flex-column align-center">
|
||||
<p class="subtitle-1 mb-1">{{ $t('modals.add.stopCondition') }}</p>
|
||||
<p class="subtitle-1 mb-1">{{ $t('enums.stopCondition.title') }}</p>
|
||||
<v-select
|
||||
v-model="stopCondition"
|
||||
:label="$t('modals.add.stopCondition')"
|
||||
flat
|
||||
solo
|
||||
dense
|
||||
|
@ -196,15 +194,15 @@ export default {
|
|||
skip_checking: false,
|
||||
contentLayout: 'Original',
|
||||
contentLayoutOptions: [
|
||||
{ text: this.$t('modals.add.contentLayoutOptions.original'), value: AppPreferences.ContentLayout.ORIGINAL },
|
||||
{ text: this.$t('modals.add.contentLayoutOptions.subfolder'), value: AppPreferences.ContentLayout.SUBFOLDER },
|
||||
{ text: this.$t('modals.add.contentLayoutOptions.nosubfolder'), value: AppPreferences.ContentLayout.NO_SUBFOLDER }
|
||||
{ text: this.$t('enums.contentLayout.original'), value: AppPreferences.ContentLayout.ORIGINAL },
|
||||
{ text: this.$t('enums.contentLayout.subfolder'), value: AppPreferences.ContentLayout.SUBFOLDER },
|
||||
{ text: this.$t('enums.contentLayout.nosubfolder'), value: AppPreferences.ContentLayout.NO_SUBFOLDER }
|
||||
],
|
||||
stopCondition: 'None',
|
||||
stopConditionOptions: [
|
||||
{ text: this.$t('modals.add.stopConditionOptions.none'), value: AppPreferences.StopCondition.NONE },
|
||||
{ text: this.$t('modals.add.stopConditionOptions.metadataReceived'), value: AppPreferences.StopCondition.METADATA_RECEIVED },
|
||||
{ text: this.$t('modals.add.stopConditionOptions.filesChecked'), value: AppPreferences.StopCondition.FILES_CHECKED }
|
||||
{ text: this.$t('enums.stopCondition.none'), value: AppPreferences.StopCondition.NONE },
|
||||
{ text: this.$t('enums.stopCondition.metadataReceived'), value: AppPreferences.StopCondition.METADATA_RECEIVED },
|
||||
{ text: this.$t('enums.stopCondition.filesChecked'), value: AppPreferences.StopCondition.FILES_CHECKED }
|
||||
],
|
||||
autoTMM: true,
|
||||
sequentialDownload: false,
|
||||
|
|
73
src/components/Modals/Rss/MatchingArticles.vue
Normal file
73
src/components/Modals/Rss/MatchingArticles.vue
Normal file
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="500px">
|
||||
<v-card flat>
|
||||
<v-container class="pa-0 project done">
|
||||
<v-card-title class="justify-center">
|
||||
<v-toolbar flat dense class="transparent">
|
||||
<v-toolbar-title class="mx-auto">
|
||||
<h2>{{ $t('modals.matchingArticles.title') }}</h2>
|
||||
</v-toolbar-title>
|
||||
<v-btn fab small class="transparent elevation-0" @click="close">
|
||||
<v-icon>{{ mdiClose }}</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
</v-card-title>
|
||||
<v-card-text class="pb-0">
|
||||
<v-list subheader>
|
||||
<template v-for="item in matchingArticles" :key="item">
|
||||
<v-divider inset v-if="item.type === 'divider'" />
|
||||
<v-subheader inset v-else-if="item.type === 'subheader'">{{ item.value }}</v-subheader>
|
||||
<v-list-item v-else class="mb-3">{{ item.value }}</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import qbit from '@/services/qbit'
|
||||
import { mdiClose } from '@mdi/js'
|
||||
import { Modal } from '@/mixins'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MatchingArticles',
|
||||
mixins: [Modal],
|
||||
props: {
|
||||
ruleName: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
matchingArticles: [] as { type: string, value?: string }[],
|
||||
mdiClose
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.ruleName === undefined) {
|
||||
this.close()
|
||||
return
|
||||
}
|
||||
|
||||
const articles = await qbit.getMatchingArticles(this.ruleName)
|
||||
for (const feedName in articles) {
|
||||
const feedArticles = articles[feedName]
|
||||
if (this.matchingArticles.length > 0)
|
||||
this.matchingArticles.push({ type: 'divider' })
|
||||
|
||||
this.matchingArticles.push({type: 'subheader', value: feedName})
|
||||
|
||||
for (const i in feedArticles) {
|
||||
const article = feedArticles[i]
|
||||
this.matchingArticles.push({type: 'item', value: article})
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.dialog = false
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -1,73 +1,162 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="350px">
|
||||
<v-dialog v-model="dialog" max-width="500px">
|
||||
<v-card flat>
|
||||
<v-card-title class="pa-0">
|
||||
<v-toolbar-title class="ma-4 primarytext--text">
|
||||
<h3>{{ hasInitialRule ? $t('modals.newRule.titleEdit') : $t('modals.newRule.titleCreate') }}</h3>
|
||||
</v-toolbar-title>
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-2">
|
||||
<v-form ref="ruleForm">
|
||||
<v-container>
|
||||
<v-text-field v-model="rule.name" :label="$t('modals.newRule.name')" required />
|
||||
</v-container>
|
||||
<v-container>
|
||||
<v-text-field v-model="rule.mustContain" :label="$t('modals.newRule.def.mustContain')" required />
|
||||
</v-container>
|
||||
<v-container>
|
||||
<v-subheader class="pa-0">
|
||||
{{ $t('modals.newRule.def.affectedFeeds') }}
|
||||
</v-subheader>
|
||||
<template v-for="(item, index) in availableFeeds">
|
||||
<v-checkbox :key="index" v-model="rule.affectedFeeds" hide-details :label="item.name" :value="item.url" />
|
||||
</template>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn v-if="!hasInitialRule" class="accent white--text elevation-0 px-4" @click="create">
|
||||
{{ $t('create') }}
|
||||
</v-btn>
|
||||
<v-btn v-else class="accent white--text elevation-0 px-4" @click="edit">
|
||||
{{ $t('edit') }}
|
||||
</v-btn>
|
||||
<v-btn class="error white--text elevation-0 px-4" @click="cancel">
|
||||
{{ $t('cancel') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
<v-container class="pa-0 project done">
|
||||
<v-card-title class="justify-center">
|
||||
<v-toolbar flat dense class="transparent">
|
||||
<v-toolbar-title class="mx-auto">
|
||||
<h2>{{ hasInitialRule ? $t('modals.newRule.titleEdit') : $t('modals.newRule.titleCreate') }}</h2>
|
||||
</v-toolbar-title>
|
||||
<v-btn fab small class="transparent elevation-0" @click="close">
|
||||
<v-icon>{{ mdiClose }}</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
</v-card-title>
|
||||
<v-card-text class="pb-0">
|
||||
<v-form ref="form" v-model="valid">
|
||||
<v-container>
|
||||
<v-text-field v-model="rule.name" :label="$t('modals.newRule.name')" required />
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-checkbox hide-details v-model="rule.useRegex" :label="$t('modals.newRule.def.useRegex')" />
|
||||
<v-text-field hide-details v-model="rule.mustContain" :label="$t('modals.newRule.def.mustContain')" />
|
||||
<v-text-field hide-details v-model="rule.mustNotContain" :label="$t('modals.newRule.def.mustNotContain')" />
|
||||
<v-checkbox hide-details v-model="rule.smartFilter" :label="$t('modals.newRule.def.smartFilter')" />
|
||||
<v-text-field v-model="rule.episodeFilter" :label="$t('modals.newRule.def.episodeFilter')" />
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-row>
|
||||
<p class="subtitle-1 mb-1">{{ $t('modals.newRule.def.assignedCategory') }}</p>
|
||||
<v-select flat solo dense hide-details background-color="background" class="rounded-xl" v-model="rule.assignedCategory" :items="availableCategories" />
|
||||
</v-row>
|
||||
<v-text-field hide-details v-model="rule.savePath" :label="$t('modals.newRule.def.savePath')" />
|
||||
<v-text-field hide-details v-model="rule.ignoreDays" :label="$t('modals.newRule.def.ignoreDays')" required type="number" min="0" />
|
||||
<v-text-field disabled :value="lastMatch" :label="$t('modals.newRule.def.lastMatch.title')" />
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-row no-gutters class="my-2 flex-gap">
|
||||
<v-col>
|
||||
<div class="d-flex flex-column align-center">
|
||||
<p class="subtitle-1 mb-1">{{ $t('modals.newRule.def.addPaused.title') }}</p>
|
||||
<v-select
|
||||
v-model="rule.addPaused"
|
||||
flat
|
||||
solo
|
||||
dense
|
||||
hide-details
|
||||
background-color="background"
|
||||
class="rounded-xl"
|
||||
:items="addPausedOptions"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<div class="d-flex flex-column align-center">
|
||||
<p class="subtitle-1 mb-1">{{ $t('enums.contentLayout.title') }}</p>
|
||||
<v-select
|
||||
v-model="rule.torrentContentLayout"
|
||||
flat
|
||||
solo
|
||||
dense
|
||||
hide-details
|
||||
background-color="background"
|
||||
class="rounded-xl"
|
||||
:items="contentLayoutOptions"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-subheader class="pa-0">
|
||||
{{ $t('modals.newRule.def.affectedFeeds') }}
|
||||
</v-subheader>
|
||||
<v-checkbox v-for="(item, index) in availableFeeds" :key="index" v-model="rule.affectedFeeds" hide-details :label="item.name" :value="item.url" />
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn v-if="!hasInitialRule" class="accent white--text elevation-0 px-4" @click="setRule" :disabled="!valid">
|
||||
{{ $t('create') }}
|
||||
</v-btn>
|
||||
<v-btn v-else class="accent white--text elevation-0 px-4" @click="setRule" :disabled="!valid">
|
||||
{{ $t('edit') }}
|
||||
</v-btn>
|
||||
<v-btn class="error white--text elevation-0 px-4" @click="cancel">
|
||||
{{ $t('cancel') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import { Modal } from '@/mixins'
|
||||
import { mdiCancel, mdiTagPlus, mdiPencil } from '@mdi/js'
|
||||
import { mdiClose } from '@mdi/js'
|
||||
import i18n from '@/plugins/i18n'
|
||||
import {AppPreferences} from '@/enums/qbit'
|
||||
import {Category} from "@/types/vuetorrent";
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: 'RuleForm',
|
||||
mixins: [Modal],
|
||||
props: {
|
||||
initialRule: Object
|
||||
},
|
||||
props: ['initialRule'],
|
||||
data: () => ({
|
||||
valid: false,
|
||||
rule: {
|
||||
name: '',
|
||||
mustContain: '',
|
||||
addPaused: null,
|
||||
affectedFeeds: [],
|
||||
enabled: true
|
||||
assignedCategory: '',
|
||||
enabled: true,
|
||||
episodeFilter: '',
|
||||
ignoreDays: 0,
|
||||
lastMatch: '',
|
||||
mustContain: '',
|
||||
mustNotContain: '',
|
||||
name: '',
|
||||
savePath: '',
|
||||
smartFilter: false,
|
||||
torrentContentLayout: null,
|
||||
useRegex: false
|
||||
},
|
||||
mdiCancel,
|
||||
mdiTagPlus,
|
||||
mdiPencil
|
||||
addPausedOptions: [
|
||||
{ text: i18n.t('useGlobalSettings'), value: null },
|
||||
{ text: i18n.t('modals.newRule.def.addPaused.always'), value: true },
|
||||
{ text: i18n.t('modals.newRule.def.addPaused.never'), value: false }
|
||||
],
|
||||
contentLayoutOptions: [
|
||||
{ text: i18n.t('useGlobalSettings'), value: null },
|
||||
{ text: i18n.t('enums.contentLayout.original'), value: AppPreferences.ContentLayout.ORIGINAL },
|
||||
{ text: i18n.t('enums.contentLayout.subfolder'), value: AppPreferences.ContentLayout.SUBFOLDER },
|
||||
{ text: i18n.t('enums.contentLayout.nosubfolder'), value: AppPreferences.ContentLayout.NO_SUBFOLDER }
|
||||
],
|
||||
mdiClose
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getFeeds']),
|
||||
...mapGetters(['getFeeds', 'getCategories']),
|
||||
lastMatch() {
|
||||
if (this.rule.lastMatch === '')
|
||||
return i18n.t('modals.newRule.def.lastMatch.unknownValue').toString()
|
||||
|
||||
const delta = new Date().getTime() - new Date(this.rule.lastMatch).getTime()
|
||||
return i18n.t('modals.newRule.def.lastMatch.knownValue').toString()
|
||||
.replace('%1', Math.floor(delta / (1000 * 60 * 60 * 24)).toString())
|
||||
},
|
||||
availableFeeds() {
|
||||
// @ts-expect-error: TS2349: This expression is not callable. Type 'never' has no call signatures.
|
||||
return this.getFeeds()
|
||||
},
|
||||
availableCategories() {
|
||||
// @ts-expect-error: TS2349: This expression is not callable. Type 'never' has no call signatures.
|
||||
return this.getCategories().map((c: Category) => c.name)
|
||||
},
|
||||
hasInitialRule() {
|
||||
return !!(this.initialRule && this.initialRule.name)
|
||||
}
|
||||
|
@ -75,25 +164,25 @@ export default {
|
|||
created() {
|
||||
this.$store.commit('FETCH_RULES')
|
||||
if (this.hasInitialRule) {
|
||||
this.rule = this.initialRule
|
||||
this.rule = {...this.initialRule}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
create() {
|
||||
qbit.createRule(this.rule)
|
||||
async setRule() {
|
||||
if (this.hasInitialRule && this.initialRule.name !== this.rule.name) {
|
||||
await qbit.renameRule(this.initialRule.name, this.rule.name)
|
||||
}
|
||||
await qbit.setRule(this.rule)
|
||||
this.$toast.success(this.$t('toast.ruleSaved'))
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.$store.commit('FETCH_RULES')
|
||||
this.dialog = false
|
||||
this.close()
|
||||
},
|
||||
edit() {
|
||||
qbit.renameRule(this.initialRule.name, this.rule.name)
|
||||
this.$toast.success(this.$t('toast.ruleSaved'))
|
||||
this.cancel()
|
||||
close() {
|
||||
this.dialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<v-list-item>
|
||||
<v-select
|
||||
v-model="settings.torrent_content_layout"
|
||||
:label="$t('modals.settings.pageDownloads.whenAddTorrent.contentLayout')"
|
||||
:label="$t('enums.contentLayout.title')"
|
||||
outlined
|
||||
dense
|
||||
small-chips
|
||||
|
@ -17,7 +17,7 @@
|
|||
<v-list-item>
|
||||
<v-select
|
||||
v-model="settings.torrent_stop_condition"
|
||||
:label="$t('modals.settings.pageDownloads.whenAddTorrent.stopCondition')"
|
||||
:label="$t('enums.stopCondition.title')"
|
||||
outlined
|
||||
dense
|
||||
small-chips
|
||||
|
@ -135,14 +135,14 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
contentLayoutOptions: [
|
||||
{ text: this.$t('modals.add.contentLayoutOptions.original'), value: AppPreferences.ContentLayout.ORIGINAL },
|
||||
{ text: this.$t('modals.add.contentLayoutOptions.subfolder'), value: AppPreferences.ContentLayout.SUBFOLDER },
|
||||
{ text: this.$t('modals.add.contentLayoutOptions.nosubfolder'), value: AppPreferences.ContentLayout.NO_SUBFOLDER }
|
||||
{ text: this.$t('enums.contentLayout.original'), value: AppPreferences.ContentLayout.ORIGINAL },
|
||||
{ text: this.$t('enums.contentLayout.subfolder'), value: AppPreferences.ContentLayout.SUBFOLDER },
|
||||
{ text: this.$t('enums.contentLayout.nosubfolder'), value: AppPreferences.ContentLayout.NO_SUBFOLDER }
|
||||
],
|
||||
stopConditionOptions: [
|
||||
{ text: this.$t('modals.add.stopConditionOptions.none'), value: AppPreferences.StopCondition.NONE },
|
||||
{ text: this.$t('modals.add.stopConditionOptions.metadataReceived'), value: AppPreferences.StopCondition.METADATA_RECEIVED },
|
||||
{ text: this.$t('modals.add.stopConditionOptions.filesChecked'), value: AppPreferences.StopCondition.FILES_CHECKED }
|
||||
{ text: this.$t('enums.stopCondition.none'), value: AppPreferences.StopCondition.NONE },
|
||||
{ text: this.$t('enums.stopCondition.metadataReceived'), value: AppPreferences.StopCondition.METADATA_RECEIVED },
|
||||
{ text: this.$t('enums.stopCondition.filesChecked'), value: AppPreferences.StopCondition.FILES_CHECKED }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ export default defineComponent({
|
|||
computed: {
|
||||
...mapGetters(['getFeeds']),
|
||||
availableFeeds() {
|
||||
// @ts-expect-error: TS2349: This expression is not callable. Type 'never' has no call signatures.
|
||||
return this.getFeeds()
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,6 +8,16 @@
|
|||
<v-list-item-content>
|
||||
<v-list-item-title v-text="item.name" />
|
||||
</v-list-item-content>
|
||||
<v-list-item-action class="icon">
|
||||
<v-icon @click="previewMatchingArticles(item.name)">
|
||||
{{ mdiEye }}
|
||||
</v-icon>
|
||||
</v-list-item-action>
|
||||
<v-list-item-action class="icon">
|
||||
<v-icon @click="editRule(item)">
|
||||
{{ mdiPencil }}
|
||||
</v-icon>
|
||||
</v-list-item-action>
|
||||
<v-list-item-action>
|
||||
<v-icon color="red" @click="deleteRule(item)">
|
||||
{{ mdiDelete }}
|
||||
|
@ -28,7 +38,7 @@
|
|||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import { mdiDelete } from '@mdi/js'
|
||||
import { mdiEye, mdiPencil, mdiDelete } from '@mdi/js'
|
||||
|
||||
import { Tab, General, FullScreenModal } from '@/mixins'
|
||||
|
||||
|
@ -36,6 +46,8 @@ export default {
|
|||
name: 'Rules',
|
||||
mixins: [Tab, General, FullScreenModal],
|
||||
data: () => ({
|
||||
mdiEye,
|
||||
mdiPencil,
|
||||
mdiDelete
|
||||
}),
|
||||
computed: {
|
||||
|
@ -57,7 +69,19 @@ export default {
|
|||
},
|
||||
createRule() {
|
||||
this.createModal('RuleForm')
|
||||
},
|
||||
editRule(item) {
|
||||
this.createModal('RuleForm', {initialRule: item})
|
||||
},
|
||||
previewMatchingArticles(ruleName) {
|
||||
this.createModal('MatchingArticles', {ruleName})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.icon {
|
||||
margin-left: 16px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
"no": "no",
|
||||
"filter": "Filter",
|
||||
"close": "Close",
|
||||
"useGlobalSettings": "Use Global Settings",
|
||||
"dashboard": {
|
||||
"tooltips": {
|
||||
"toggleSearch": "Toggle Search Filter",
|
||||
|
@ -163,11 +164,29 @@
|
|||
"name": "Name",
|
||||
"def": {
|
||||
"mustContain": "Must Contain",
|
||||
"mustNotContain": "Must Not Contain",
|
||||
"useRegex": "Use Regular Expressions",
|
||||
"mustNotContain": "Must Not Contain",
|
||||
"smartFilter": "Use Smart Episode Filter",
|
||||
"episodeFilter": "Episode Filter",
|
||||
"assignedCategory": "Assign Category",
|
||||
"savePath": "Save to",
|
||||
"ignoreDays": "Ignore Subsequent Matches for (0 to Disable)",
|
||||
"lastMatch": {
|
||||
"title": "Last Match",
|
||||
"knownValue": "%1 days ago",
|
||||
"unknownValue": "Unknown"
|
||||
},
|
||||
"addPaused": {
|
||||
"title": "Add Paused",
|
||||
"always": "Always",
|
||||
"never": "Never"
|
||||
},
|
||||
"affectedFeeds": "Apply Rule to Feeds"
|
||||
}
|
||||
},
|
||||
"matchingArticles": {
|
||||
"title": "Matching RSS Articles"
|
||||
},
|
||||
"pluginManager": {
|
||||
"title": "Plugin manager"
|
||||
},
|
||||
|
@ -278,9 +297,7 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "When adding a torrent",
|
||||
"whenAddTorrent": {
|
||||
"contentLayout": "Torrent content layout",
|
||||
"donotAutoStart": "Do not start the download automatically",
|
||||
"stopCondition": "Torrent stop condition",
|
||||
"autoDeleteMode": "Delete .torrent files afterwards"
|
||||
},
|
||||
"subHeaderPublicSettings": "Public Settings",
|
||||
|
@ -480,19 +497,7 @@
|
|||
"skipHashCheck": "Skip hash check",
|
||||
"automaticTorrentManagement": "Automatic Torrent Management",
|
||||
"dropHereForAdd": "Drop here for add",
|
||||
"oneOrMoreFilesInvalidTorrent": "One or more files are not valid torrents",
|
||||
"contentLayout": "Content Layout",
|
||||
"contentLayoutOptions": {
|
||||
"original": "Original",
|
||||
"subfolder": "Create Subfolder",
|
||||
"nosubfolder": "Remove Subfolder"
|
||||
},
|
||||
"stopCondition": "Stop Condition",
|
||||
"stopConditionOptions": {
|
||||
"none": "None",
|
||||
"metadataReceived": "Metadata Received",
|
||||
"filesChecked": "Files Checked"
|
||||
}
|
||||
"oneOrMoreFilesInvalidTorrent": "One or more files are not valid torrents"
|
||||
},
|
||||
"changeLocation": {
|
||||
"title": "Change Location"
|
||||
|
@ -615,5 +620,19 @@
|
|||
"copy": "Copy",
|
||||
"export": "Export Torrent | Export Torrents",
|
||||
"info": "Show Info"
|
||||
},
|
||||
"enums": {
|
||||
"contentLayout": {
|
||||
"title": "Torrent content layout",
|
||||
"original": "Original",
|
||||
"subfolder": "Create Subfolder",
|
||||
"nosubfolder": "Remove Subfolder"
|
||||
},
|
||||
"stopCondition": {
|
||||
"title": "Torrent stop condition",
|
||||
"none": "None",
|
||||
"metadataReceived": "Metadata Received",
|
||||
"filesChecked": "Files Checked"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,9 +224,7 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "Lors de l'ajout d'un torrent",
|
||||
"whenAddTorrent": {
|
||||
"contentLayout": "Disposition du contenu du torrent",
|
||||
"donotAutoStart": "Ne pas lancer le téléchargement automatiquement",
|
||||
"stopCondition": "Condition d'arrêt du torrent",
|
||||
"autoDeleteMode": "Supprimer les fichers .torrent après"
|
||||
},
|
||||
"subHeaderPublicSettings": "Paramètres publics",
|
||||
|
@ -424,19 +422,7 @@
|
|||
"skipHashCheck": "Passer la vérification du hash",
|
||||
"automaticTorrentManagement": "Gestion automatique des torrents",
|
||||
"dropHereForAdd": "Déposez ici pour ajouter",
|
||||
"oneOrMoreFilesInvalidTorrent": "Un ou plusieurs fichiers ne sont pas des torrents valides",
|
||||
"contentLayout": "Disposition des fichiers",
|
||||
"contentLayoutOptions": {
|
||||
"original": "Original",
|
||||
"subfolder": "Créer un sous-dossier",
|
||||
"nosubfolder": "Supprimer le sous-dossier"
|
||||
},
|
||||
"stopCondition": "Condition d'arrêt",
|
||||
"stopConditionOptions": {
|
||||
"none": "Aucune",
|
||||
"metadataReceived": "Metadonnées reçues",
|
||||
"filesChecked": "Fichiers vérifiés"
|
||||
}
|
||||
"oneOrMoreFilesInvalidTorrent": "Un ou plusieurs fichiers ne sont pas des torrents valides"
|
||||
},
|
||||
"changeLocation": {
|
||||
"title": "Changement d'emplacement"
|
||||
|
@ -527,5 +513,19 @@
|
|||
"copy": "Copier",
|
||||
"export": "Exporter le torrent | Exporter les torrents",
|
||||
"info": "Propriétés"
|
||||
},
|
||||
"enums": {
|
||||
"contentLayout": {
|
||||
"title": "Disposition du contenu du torrent",
|
||||
"original": "Original",
|
||||
"subfolder": "Créer un sous-dossier",
|
||||
"nosubfolder": "Supprimer le sous-dossier"
|
||||
},
|
||||
"stopCondition": {
|
||||
"title": "Condition d'arrêt du torrent",
|
||||
"none": "Aucune",
|
||||
"metadataReceived": "Metadonnées reçues",
|
||||
"filesChecked": "Fichiers vérifiés"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,6 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "Saat menambahkan torrent",
|
||||
"whenAddTorrent": {
|
||||
"createSubFolder": "Buat subfolder untuk torrent dengan banyak file",
|
||||
"donotAutoStart": "Jangan mengunduh secara otomatis"
|
||||
},
|
||||
"subHeaderPublicSettings": "Pengaturan Publik",
|
||||
|
|
|
@ -150,7 +150,6 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "Torrentを追加したとき",
|
||||
"whenAddTorrent": {
|
||||
"createSubFolder": "複数のファイルが有るときはサブフォルダーを作成する",
|
||||
"donotAutoStart": "自動でダウンロードを開始しない"
|
||||
},
|
||||
"subHeaderPublicSettings": "全体の設定",
|
||||
|
|
|
@ -179,7 +179,6 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "При добавлении торрента",
|
||||
"whenAddTorrent": {
|
||||
"createSubFolder": "Создать подкаталог для торрентов с несколькими файлами",
|
||||
"donotAutoStart": "Не начинать загрузку автоматически"
|
||||
},
|
||||
"subHeaderPublicSettings": "Общие настройки",
|
||||
|
|
|
@ -278,9 +278,7 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "Коли додається торент",
|
||||
"whenAddTorrent": {
|
||||
"contentLayout": "Оформлення контенту торрента",
|
||||
"donotAutoStart": "Не запускати завантаження автоматично",
|
||||
"stopCondition": "Умови зупинки торрентк",
|
||||
"autoDeleteMode": "Видаляти .torrent файли після завершення"
|
||||
},
|
||||
"subHeaderPublicSettings": "Загальні налаштування",
|
||||
|
@ -480,19 +478,7 @@
|
|||
"skipHashCheck": "Пропустити перевірку хешування",
|
||||
"automaticTorrentManagement": "Автоматичне керування торрентами",
|
||||
"dropHereForAdd": "Перетягнути для додавання",
|
||||
"oneOrMoreFilesInvalidTorrent": "Один або кілька файлів не є дійсним торрентом",
|
||||
"contentLayout": "Макет вмісту",
|
||||
"contentLayoutOptions": {
|
||||
"original": "Оригінал",
|
||||
"subfolder": "Створити підпапку",
|
||||
"nosubfolder": "Видалити підпапку"
|
||||
},
|
||||
"stopCondition": "Умови зупинки",
|
||||
"stopConditionOptions": {
|
||||
"none": "Немає",
|
||||
"metadataReceived": "Метадані отримано",
|
||||
"filesChecked": "Файли перевірено"
|
||||
}
|
||||
"oneOrMoreFilesInvalidTorrent": "Один або кілька файлів не є дійсним торрентом"
|
||||
},
|
||||
"changeLocation": {
|
||||
"title": "Змінити місцезнаходження"
|
||||
|
@ -615,5 +601,19 @@
|
|||
"copy": "Копіювати",
|
||||
"export": "Експортувати торент | Експортувати торенти",
|
||||
"info": "Деталі"
|
||||
},
|
||||
"enums": {
|
||||
"contentLayout": {
|
||||
"title": "Макет вмісту",
|
||||
"original": "Оригінал",
|
||||
"subfolder": "Створити підпапку",
|
||||
"nosubfolder": "Видалити підпапку"
|
||||
},
|
||||
"stopCondition": {
|
||||
"title": "Умови зупинки",
|
||||
"none": "Немає",
|
||||
"metadataReceived": "Метадані отримано",
|
||||
"filesChecked": "Файли перевірено"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,7 +206,6 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "Khi thêm một torrent",
|
||||
"whenAddTorrent": {
|
||||
"createSubFolder": "Tạo thư mục con cho torrent với nhiều tệp",
|
||||
"donotAutoStart": "Không tự động bắt đầu tải xuống"
|
||||
},
|
||||
"subHeaderPublicSettings": "Cài đặt chung",
|
||||
|
|
|
@ -237,9 +237,7 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "当添加种子时",
|
||||
"whenAddTorrent": {
|
||||
"contentLayout": "下载内容布局",
|
||||
"donotAutoStart": "不要开始自动下载",
|
||||
"stopCondition": "停止条件",
|
||||
"autoDeleteMode": "随后删除 .torrent 文件"
|
||||
},
|
||||
"subHeaderPublicSettings": "公共设置",
|
||||
|
@ -439,19 +437,7 @@
|
|||
"skipHashCheck": "跳过哈希值检查",
|
||||
"automaticTorrentManagement": "自动种子管理 (ATM)",
|
||||
"dropHereForAdd": "拖拽到此处即可添加",
|
||||
"oneOrMoreFilesInvalidTorrent": "存在无效的种子文件",
|
||||
"contentLayout": "下载内容布局",
|
||||
"contentLayoutOptions": {
|
||||
"original": "原始",
|
||||
"subfolder": "创建子文件夹",
|
||||
"nosubfolder": "移除子文件夹"
|
||||
},
|
||||
"stopCondition": "种子停止条件",
|
||||
"stopConditionOptions": {
|
||||
"none": "无",
|
||||
"metadataReceived": "接受元数据后",
|
||||
"filesChecked": "检查文件后"
|
||||
}
|
||||
"oneOrMoreFilesInvalidTorrent": "存在无效的种子文件"
|
||||
},
|
||||
"changeLocation": {
|
||||
"title": "更改位置"
|
||||
|
@ -545,5 +531,19 @@
|
|||
"copy": "复制",
|
||||
"export": "导出种子 | 导出种子",
|
||||
"info": "显示详情"
|
||||
},
|
||||
"enums": {
|
||||
"contentLayout": {
|
||||
"title": "下载内容布局",
|
||||
"original": "原始",
|
||||
"subfolder": "创建子文件夹",
|
||||
"nosubfolder": "移除子文件夹"
|
||||
},
|
||||
"stopCondition": {
|
||||
"title": "种子停止条件",
|
||||
"none": "无",
|
||||
"metadataReceived": "接受元数据后",
|
||||
"filesChecked": "检查文件后"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,9 +237,7 @@
|
|||
"pageDownloads": {
|
||||
"subHeaderWhenAddTorrent": "當新增種子時",
|
||||
"whenAddTorrent": {
|
||||
"contentLayout": "種子內容佈局",
|
||||
"donotAutoStart": "不要自動開始下載",
|
||||
"stopCondition": "種子停止條件",
|
||||
"autoDeleteMode": "事後移除 .torrent 檔案"
|
||||
},
|
||||
"subHeaderPublicSettings": "公共設定",
|
||||
|
@ -439,19 +437,7 @@
|
|||
"skipHashCheck": "跳過雜湊值檢查",
|
||||
"automaticTorrentManagement": "自動種子管理 (ATM)",
|
||||
"dropHereForAdd": "拖拽至此處新增",
|
||||
"oneOrMoreFilesInvalidTorrent": "存在無效的種子檔案",
|
||||
"contentLayout": "內容佈局",
|
||||
"contentLayoutOptions": {
|
||||
"original": "原始",
|
||||
"subfolder": "建立子資料夾",
|
||||
"nosubfolder": "移除子資料夾"
|
||||
},
|
||||
"stopCondition": "停止條件",
|
||||
"stopConditionOptions": {
|
||||
"none": "無",
|
||||
"metadataReceived": "收到元資料後",
|
||||
"filesChecked": "檢查檔案後"
|
||||
}
|
||||
"oneOrMoreFilesInvalidTorrent": "存在無效的種子檔案"
|
||||
},
|
||||
"changeLocation": {
|
||||
"title": "更改位置"
|
||||
|
@ -545,5 +531,19 @@
|
|||
"copy": "複製",
|
||||
"export": "匯出種子 | 匯出種子",
|
||||
"info": "顯示詳情"
|
||||
},
|
||||
"enums": {
|
||||
"contentLayout": {
|
||||
"title": "內容佈局",
|
||||
"original": "原始",
|
||||
"subfolder": "建立子資料夾",
|
||||
"nosubfolder": "移除子資料夾"
|
||||
},
|
||||
"stopCondition": {
|
||||
"title": "停止條件",
|
||||
"none": "無",
|
||||
"metadataReceived": "收到元資料後",
|
||||
"filesChecked": "檢查檔案後"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export default defineComponent({
|
|||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
// @ts-expect-error: TS2339: Property 'getTheme' does not exist on type 'CreateComponentPublicInstance{}, unknown, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, {}, {}, false, OptionTypesType{}, {}, {}, {}, {}, {}>, ... 5 more ..., {}>'.
|
||||
return this.getTheme()
|
||||
},
|
||||
isMobile() {
|
||||
|
|
|
@ -1,37 +1,32 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component({
|
||||
computed: mapGetters(['getModalState'])
|
||||
})
|
||||
export default class Modal extends Vue {
|
||||
//data
|
||||
hndlDialog = true
|
||||
|
||||
//props
|
||||
@Prop() guid!: string
|
||||
|
||||
// mapGetters
|
||||
getModalState!: () => any
|
||||
|
||||
// computed
|
||||
get dialog() {
|
||||
return this.hndlDialog
|
||||
}
|
||||
set dialog(val) {
|
||||
this.hndlDialog = val
|
||||
if (!val) this.deleteModal()
|
||||
}
|
||||
|
||||
// methods
|
||||
deleteModal() {
|
||||
//this.hndlDialog = false
|
||||
setTimeout(() => {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}, 300)
|
||||
}
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Modal',
|
||||
props: {
|
||||
guid: String
|
||||
},
|
||||
data: () => ({
|
||||
hndlDialog: true
|
||||
}),
|
||||
beforeDestroy() {
|
||||
this.deleteModal()
|
||||
},
|
||||
computed: {
|
||||
dialog: {
|
||||
get() {
|
||||
return this.hndlDialog
|
||||
},
|
||||
set(val: boolean) {
|
||||
this.hndlDialog = val
|
||||
if (!val) this.deleteModal()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteModal() {
|
||||
setTimeout(() => {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -16,8 +16,7 @@ import type {
|
|||
} from '@/types/qbit/models'
|
||||
import type { MainDataResponse, SearchResultsResponse, TorrentPeersResponse } from '@/types/qbit/responses'
|
||||
import type { AddTorrentPayload, AppPreferencesPayload, CreateFeedPayload, LoginPayload } from '@/types/qbit/payloads'
|
||||
import type { SortOptions } from '@/types/vuetorrent'
|
||||
import type { FeedRule as VtFeedRule } from '@/types/vuetorrent/rss'
|
||||
import type { FeedRule as VtFeedRule, SortOptions } from '@/types/vuetorrent'
|
||||
import type { Priority } from '@/enums/qbit'
|
||||
|
||||
type Parameters = Record<string, any>
|
||||
|
@ -162,10 +161,10 @@ export class QBitApi {
|
|||
})
|
||||
}
|
||||
|
||||
async createRule(rule: VtFeedRule) {
|
||||
async setRule(rule: VtFeedRule) {
|
||||
return this.execute('/rss/setRule', {
|
||||
ruleName: rule.name,
|
||||
ruleDef: JSON.stringify(rule, ['enabled', 'mustContain', 'mustNotContain', 'useRegex', 'affectedFeeds'])
|
||||
ruleDef: JSON.stringify(rule)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -216,6 +215,10 @@ export class QBitApi {
|
|||
})
|
||||
}
|
||||
|
||||
async getMatchingArticles(ruleName: string): Promise<Record<string, string[]>> {
|
||||
return this.axios.get('/rss/matchingArticles', {params: {ruleName}}).then(r => r.data)
|
||||
}
|
||||
|
||||
// Post
|
||||
|
||||
async addTorrents(params: AddTorrentPayload, torrents: File[]): Promise<void> {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { AppPreferences } from '@/enums/qbit'
|
||||
|
||||
export default interface FeedRule {
|
||||
/** Add matched torrent in paused mode */
|
||||
addPaused: boolean
|
||||
addPaused: boolean | null
|
||||
/** The feed URLs the rule applied to */
|
||||
affectedFeeds: string[]
|
||||
/** Assign category to the torrent */
|
||||
|
@ -9,7 +11,7 @@ export default interface FeedRule {
|
|||
enabled: boolean
|
||||
/** Episode filter definition */
|
||||
episodeFilter: string
|
||||
/** Ignore sunsequent rule matches */
|
||||
/** Ignore subsequent rule matches */
|
||||
ignoreDays: number
|
||||
/** The rule last match time */
|
||||
lastMatch: string
|
||||
|
@ -23,7 +25,8 @@ export default interface FeedRule {
|
|||
savePath: string
|
||||
/** Enable smart episode filter */
|
||||
smartFilter: boolean
|
||||
torrentContentLayout?: unknown
|
||||
/** Torrent content layout to use with matched torrent */
|
||||
torrentContentLayout: AppPreferences.ContentLayout | null
|
||||
/** Enable regex mode in "mustContain" and "mustNotContain" */
|
||||
useRegex: boolean
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type Category from './Category'
|
||||
import type Feed from './rss/Feed'
|
||||
import type FeedRule from './rss/FeedRule'
|
||||
import type { Feed, FeedArticle, FeedRule } from './rss'
|
||||
import type SearchStatus from './search/SearchStatus'
|
||||
import type SearchResult from './search/SearchResult'
|
||||
import type ModalTemplate from './ModalTemplate'
|
||||
|
|
|
@ -1,16 +1,5 @@
|
|||
export default interface FeedRule {
|
||||
addPaused?: boolean
|
||||
affectedFeeds?: string[]
|
||||
assignedCategory?: string
|
||||
enabled: boolean
|
||||
episodeFilter?: string
|
||||
ignoreDays?: number
|
||||
lastMatch?: string
|
||||
mustContain?: string
|
||||
mustNotContain?: string
|
||||
import { FeedRule as QbitFeedRule } from '@/types/qbit/models'
|
||||
|
||||
export default interface FeedRule extends Partial<QbitFeedRule> {
|
||||
name: string
|
||||
previouslyMatchedEpisodes?: unknown[]
|
||||
savePath?: string
|
||||
smartFilter?: boolean
|
||||
useRegex?: boolean
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue