8dbf29f803
Operator lever: disable a single file host (e.g. mega.nz when it's banning) without touching the others. Five booleans on import_settings (extdl_<host>_enabled, default true — works out of the box, rule #26); the worker already reads them via getattr so no worker change. Migration 0050 + model fields + settings GET/PATCH (uniform boolean validation) + a 'External file-host downloads' card in the subscriptions Settings tab. Completes Phase 4. Refs FC #830. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
249 lines
8.0 KiB
Vue
249 lines
8.0 KiB
Vue
<template>
|
|
<div>
|
|
<ExtensionKeyBar class="mb-4" />
|
|
|
|
<v-alert v-if="credentialsStore.error" type="error" variant="tonal" closable class="mb-4">
|
|
{{ String(credentialsStore.error) }}
|
|
</v-alert>
|
|
|
|
<h3 class="text-h6 mb-3">Platform credentials</h3>
|
|
<v-row>
|
|
<v-col
|
|
v-for="p in platformsStore.list"
|
|
:key="p.key"
|
|
cols="12" md="6"
|
|
>
|
|
<CredentialCard
|
|
:platform="p"
|
|
:credential="credentialsStore.byPlatform.get(p.key) || null"
|
|
@replace="openUpload"
|
|
@remove="confirmRemove"
|
|
@verified="onSaved"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<h3 class="text-h6 mb-3 mt-6">Downloader</h3>
|
|
<v-card variant="outlined">
|
|
<v-card-text v-if="importStore.settings">
|
|
<v-row>
|
|
<v-col cols="12" sm="6">
|
|
<v-text-field
|
|
v-model.number="dl.download_rate_limit_seconds"
|
|
label="Rate limit (seconds between requests)"
|
|
type="number" step="0.5" min="0"
|
|
density="compact" hide-details
|
|
@blur="saveDownloader"
|
|
/>
|
|
<div class="fc-help">gallery-dl extractor.sleep. Higher = slower but safer.</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="6">
|
|
<v-switch
|
|
v-model="dl.download_validate_files"
|
|
label="Validate downloaded files (magic-byte check)"
|
|
density="compact" hide-details color="accent"
|
|
@change="saveDownloader"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
<v-card-text v-else>
|
|
<v-skeleton-loader type="paragraph" />
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<h3 class="text-h6 mb-3 mt-6">External file-host downloads</h3>
|
|
<v-card variant="outlined">
|
|
<v-card-text v-if="importStore.settings">
|
|
<div class="fc-help mb-2">
|
|
When a creator links files on these hosts in a post, Curator downloads
|
|
them into the post automatically (tagged + provenance-linked like any
|
|
other media). Turn a host off if it starts rate-limiting or banning.
|
|
</div>
|
|
<v-row>
|
|
<v-col cols="12" sm="4">
|
|
<v-switch
|
|
v-model="dl.extdl_mega_enabled" label="mega.nz"
|
|
density="compact" hide-details color="accent" @change="saveDownloader"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<v-switch
|
|
v-model="dl.extdl_gdrive_enabled" label="Google Drive"
|
|
density="compact" hide-details color="accent" @change="saveDownloader"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<v-switch
|
|
v-model="dl.extdl_pixeldrain_enabled" label="Pixeldrain"
|
|
density="compact" hide-details color="accent" @change="saveDownloader"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<v-switch
|
|
v-model="dl.extdl_mediafire_enabled" label="MediaFire"
|
|
density="compact" hide-details color="accent" @change="saveDownloader"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<v-switch
|
|
v-model="dl.extdl_dropbox_enabled" label="Dropbox"
|
|
density="compact" hide-details color="accent" @change="saveDownloader"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
<v-card-text v-else>
|
|
<v-skeleton-loader type="paragraph" />
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<h3 class="text-h6 mb-3 mt-6">Schedule defaults</h3>
|
|
<v-card variant="outlined">
|
|
<v-card-text v-if="importStore.settings">
|
|
<v-row>
|
|
<v-col cols="12" sm="4">
|
|
<v-text-field
|
|
v-model.number="dl.download_schedule_default_seconds"
|
|
label="Default check interval (seconds)"
|
|
type="number" :min="60" :max="86400"
|
|
density="compact" hide-details
|
|
@blur="saveDownloader"
|
|
/>
|
|
<div class="fc-help">
|
|
Used when a source has no per-source or per-artist override.
|
|
Default 28800 (8 hours).
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<v-text-field
|
|
v-model.number="dl.download_event_retention_days"
|
|
label="Event retention (days)"
|
|
type="number" :min="1" :max="3650"
|
|
density="compact" hide-details
|
|
@blur="saveDownloader"
|
|
/>
|
|
<div class="fc-help">
|
|
Completed download events older than this are deleted nightly.
|
|
Default 90.
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<v-text-field
|
|
v-model.number="dl.download_failure_warning_threshold"
|
|
label="Failure warning threshold"
|
|
type="number" :min="1" :max="100"
|
|
density="compact" hide-details
|
|
@blur="saveDownloader"
|
|
/>
|
|
<div class="fc-help">
|
|
Source row badge turns red after this many consecutive
|
|
failures. Sources are never auto-disabled. Default 5.
|
|
</div>
|
|
</v-col>
|
|
</v-row>
|
|
<v-alert v-if="importStore.settingsError" type="error" variant="tonal" class="mt-2" closable>
|
|
{{ importStore.settingsError }}
|
|
</v-alert>
|
|
</v-card-text>
|
|
<v-card-text v-else>
|
|
<v-skeleton-loader type="paragraph" />
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<CredentialUploadDialog
|
|
v-model="showUpload"
|
|
:platform="uploadPlatform"
|
|
@saved="onSaved"
|
|
/>
|
|
|
|
<v-dialog v-model="removeConfirm.open" max-width="420">
|
|
<v-card>
|
|
<v-card-title>Delete {{ removeConfirm.platform?.name }} credential?</v-card-title>
|
|
<v-card-text>
|
|
The encrypted credential will be removed permanently. You'll need to
|
|
re-upload to use this platform again.
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer />
|
|
<v-btn variant="text" @click="removeConfirm.open = false">Cancel</v-btn>
|
|
<v-btn color="error" variant="flat" @click="doRemove">Delete</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, reactive, ref, watch } from 'vue'
|
|
import { usePlatformsStore } from '../../stores/platforms.js'
|
|
import { useCredentialsStore } from '../../stores/credentials.js'
|
|
import { useImportStore } from '../../stores/import.js'
|
|
import ExtensionKeyBar from '../credentials/ExtensionKeyBar.vue'
|
|
import CredentialUploadDialog from '../credentials/CredentialUploadDialog.vue'
|
|
import CredentialCard from './CredentialCard.vue'
|
|
|
|
const platformsStore = usePlatformsStore()
|
|
const credentialsStore = useCredentialsStore()
|
|
const importStore = useImportStore()
|
|
|
|
const showUpload = ref(false)
|
|
const uploadPlatform = ref(null)
|
|
const removeConfirm = reactive({ open: false, platform: null })
|
|
|
|
const dl = reactive({
|
|
download_rate_limit_seconds: 3.0,
|
|
download_validate_files: true,
|
|
download_schedule_default_seconds: 28800,
|
|
download_event_retention_days: 90,
|
|
download_failure_warning_threshold: 5,
|
|
extdl_mega_enabled: true,
|
|
extdl_gdrive_enabled: true,
|
|
extdl_mediafire_enabled: true,
|
|
extdl_dropbox_enabled: true,
|
|
extdl_pixeldrain_enabled: true,
|
|
})
|
|
|
|
watch(() => importStore.settings, (s) => { if (s) Object.assign(dl, s) }, { immediate: true })
|
|
|
|
onMounted(async () => {
|
|
await Promise.all([
|
|
platformsStore.loadAll(),
|
|
credentialsStore.loadAll(),
|
|
importStore.loadSettings(),
|
|
])
|
|
})
|
|
|
|
function openUpload(platform) {
|
|
uploadPlatform.value = platform
|
|
showUpload.value = true
|
|
}
|
|
|
|
async function onSaved() {
|
|
await credentialsStore.loadAll()
|
|
}
|
|
|
|
function confirmRemove(platform) {
|
|
removeConfirm.platform = platform
|
|
removeConfirm.open = true
|
|
}
|
|
|
|
async function doRemove() {
|
|
await credentialsStore.remove(removeConfirm.platform.key)
|
|
removeConfirm.open = false
|
|
await credentialsStore.loadAll()
|
|
}
|
|
|
|
async function saveDownloader() {
|
|
await importStore.patchSettings({ ...dl })
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.fc-help {
|
|
font-size: 12px;
|
|
color: rgb(var(--v-theme-on-surface-variant));
|
|
margin-top: 2px;
|
|
}
|
|
</style>
|