feat(subscriptions): dry-run backfill preview — B4 preview (plan #708)
Owning the walk lets an operator gauge "is this source worth a backfill?" before
arming one. ingest_core.Ingester.preview walks the first few feed pages and
counts media NOT already in the seen/dead ledgers, downloading nothing
(read-only). download_backends.preview_source resolves the campaign id + runs it
(native-only, mirrors verify_source_credential / run_download); POST
/api/sources/{id}/preview returns {total_new, posts_scanned, has_more, sample[]}
(409 on auth/drift/unresolvable, 400 for gallery-dl platforms). PatreonClient
gains post_meta(post) for the sample's title/date.
UI: a Patreon-only Preview button (mdi-eye-outline) on SourceRow + SourceCard
opens PreviewDialog — self-fetches with loading / error / empty / result states
and a "Start backfill" shortcut. Store action previewSource.
Tests: preview counts new media without downloading + samples only posts with
new items; page_limit caps the walk + flags has_more.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -176,6 +176,7 @@
|
||||
@check="onCheck"
|
||||
@backfill="onBackfill"
|
||||
@recover="onRecover"
|
||||
@preview="onPreview"
|
||||
/>
|
||||
<tr v-if="item.sources.length === 0">
|
||||
<td colspan="8" class="fc-subs__sources-empty">
|
||||
@@ -253,6 +254,7 @@
|
||||
@check="onCheck"
|
||||
@backfill="onBackfill"
|
||||
@recover="onRecover"
|
||||
@preview="onPreview"
|
||||
/>
|
||||
<div v-if="item.sources.length === 0" class="fc-subs__sources-empty">
|
||||
No sources yet. Tap + to add one.
|
||||
@@ -268,6 +270,11 @@
|
||||
@saved="onSourceSaved"
|
||||
/>
|
||||
<ArtistCreateDialog v-model="showArtistDialog" @created="onArtistCreated" />
|
||||
<PreviewDialog
|
||||
v-model="showPreviewDialog"
|
||||
:source="previewTarget"
|
||||
@backfill="onPreviewBackfill"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -284,6 +291,7 @@ import SourceCard from './SourceCard.vue'
|
||||
import SourceHealthDot from './SourceHealthDot.vue'
|
||||
import SourceFormDialog from './SourceFormDialog.vue'
|
||||
import ArtistCreateDialog from './ArtistCreateDialog.vue'
|
||||
import PreviewDialog from './PreviewDialog.vue'
|
||||
import PlatformChip from './PlatformChip.vue'
|
||||
import SchedulerStatusBar from './SchedulerStatusBar.vue'
|
||||
import { formatRelative } from '../../utils/date.js'
|
||||
@@ -334,6 +342,8 @@ const showSourceDialog = ref(false)
|
||||
const editingSource = ref(null)
|
||||
const editingArtist = ref(null)
|
||||
const showArtistDialog = ref(false)
|
||||
const showPreviewDialog = ref(false)
|
||||
const previewTarget = ref(null)
|
||||
|
||||
const artistFilter = computed(() => {
|
||||
const raw = route.query.artist_id
|
||||
@@ -572,6 +582,18 @@ async function onRecover(source) {
|
||||
}
|
||||
}
|
||||
|
||||
// Plan #708 B4: open the dry-run preview dialog (it self-fetches on open).
|
||||
function onPreview(source) {
|
||||
previewTarget.value = source
|
||||
showPreviewDialog.value = true
|
||||
}
|
||||
|
||||
// "Start backfill" from inside the preview dialog → arm it + close.
|
||||
async function onPreviewBackfill(source) {
|
||||
showPreviewDialog.value = false
|
||||
await onBackfill(source)
|
||||
}
|
||||
|
||||
async function checkAll(group) {
|
||||
let ok = 0
|
||||
let conflict = 0
|
||||
|
||||
Reference in New Issue
Block a user