polish for failed retry mechanic and consistent sub naming
This commit is contained in:
@@ -17,7 +17,8 @@
|
|||||||
"WebFetch(domain:raw.githubusercontent.com)",
|
"WebFetch(domain:raw.githubusercontent.com)",
|
||||||
"Bash(python -m py_compile:*)",
|
"Bash(python -m py_compile:*)",
|
||||||
"Bash(powershell:*)",
|
"Bash(powershell:*)",
|
||||||
"Bash(python:*)"
|
"Bash(python:*)",
|
||||||
|
"Bash(xargs basename:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ async def list_downloads():
|
|||||||
async with current_app.db_engine.connect() as conn:
|
async with current_app.db_engine.connect() as conn:
|
||||||
session = AsyncSession(bind=conn)
|
session = AsyncSession(bind=conn)
|
||||||
|
|
||||||
# Build query
|
# Build query - eager load source and subscription for labeling
|
||||||
query = select(Download)
|
query = select(Download).options(
|
||||||
|
selectinload(Download.source).selectinload(Source.subscription)
|
||||||
|
)
|
||||||
count_query = select(func.count()).select_from(Download)
|
count_query = select(func.count()).select_from(Download)
|
||||||
|
|
||||||
filters = []
|
filters = []
|
||||||
@@ -58,8 +60,20 @@ async def list_downloads():
|
|||||||
result = await session.execute(query)
|
result = await session.execute(query)
|
||||||
downloads = result.scalars().all()
|
downloads = result.scalars().all()
|
||||||
|
|
||||||
|
# Include source info with each download
|
||||||
|
items = []
|
||||||
|
for d in downloads:
|
||||||
|
item = d.to_dict()
|
||||||
|
if d.source:
|
||||||
|
item["subscription_name"] = d.source.subscription.name if d.source.subscription else None
|
||||||
|
item["platform"] = d.source.platform
|
||||||
|
else:
|
||||||
|
item["subscription_name"] = None
|
||||||
|
item["platform"] = None
|
||||||
|
items.append(item)
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"items": [d.to_dict() for d in downloads],
|
"items": items,
|
||||||
"total": total,
|
"total": total,
|
||||||
"page": page,
|
"page": page,
|
||||||
"per_page": per_page,
|
"per_page": per_page,
|
||||||
@@ -147,8 +161,11 @@ async def recent_activity():
|
|||||||
session = AsyncSession(bind=conn)
|
session = AsyncSession(bind=conn)
|
||||||
|
|
||||||
# Get failed downloads OR completed with files (noteworthy activity)
|
# Get failed downloads OR completed with files (noteworthy activity)
|
||||||
|
# Eager load source and subscription for labeling
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
query = select(Download).where(
|
query = select(Download).options(
|
||||||
|
selectinload(Download.source).selectinload(Source.subscription)
|
||||||
|
).where(
|
||||||
and_(
|
and_(
|
||||||
Download.created_at >= cutoff,
|
Download.created_at >= cutoff,
|
||||||
or_(
|
or_(
|
||||||
@@ -164,8 +181,20 @@ async def recent_activity():
|
|||||||
result = await session.execute(query)
|
result = await session.execute(query)
|
||||||
downloads = result.scalars().all()
|
downloads = result.scalars().all()
|
||||||
|
|
||||||
|
# Include source info with each download
|
||||||
|
items = []
|
||||||
|
for d in downloads:
|
||||||
|
item = d.to_dict()
|
||||||
|
if d.source:
|
||||||
|
item["subscription_name"] = d.source.subscription.name if d.source.subscription else None
|
||||||
|
item["platform"] = d.source.platform
|
||||||
|
else:
|
||||||
|
item["subscription_name"] = None
|
||||||
|
item["platform"] = None
|
||||||
|
items.append(item)
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"items": [d.to_dict() for d in downloads],
|
"items": items,
|
||||||
"count": len(downloads),
|
"count": len(downloads),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "GallerySubscriber",
|
"name": "GallerySubscriber",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"description": "Export cookies from supported platforms to GallerySubscriber for automated downloads",
|
"description": "Export cookies from supported platforms to GallerySubscriber for automated downloads",
|
||||||
|
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
|
|||||||
Binary file not shown.
@@ -173,7 +173,7 @@
|
|||||||
</v-icon>
|
</v-icon>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title class="text-body-2">
|
<v-list-item-title class="text-body-2">
|
||||||
{{ truncate(dl.url, 30) }}
|
{{ getDownloadLabel(dl) }}
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle>
|
<v-list-item-subtitle>
|
||||||
<v-chip size="x-small" :color="dl.status === 'running' ? 'info' : 'warning'" variant="tonal">
|
<v-chip size="x-small" :color="dl.status === 'running' ? 'info' : 'warning'" variant="tonal">
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
</v-icon>
|
</v-icon>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
{{ truncate(download.url, 40) }}
|
{{ getDownloadLabel(download) }}
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle>
|
<v-list-item-subtitle>
|
||||||
{{ formatRelativeTime(download.created_at) }}
|
{{ formatRelativeTime(download.created_at) }}
|
||||||
@@ -255,20 +255,18 @@
|
|||||||
<v-table v-if="sourcesWithErrors.length">
|
<v-table v-if="sourcesWithErrors.length">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Source</th>
|
||||||
<th>Platform</th>
|
<th>Last Error</th>
|
||||||
<th>Error Count</th>
|
|
||||||
<th>Last Check</th>
|
<th>Last Check</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="source in sourcesWithErrors" :key="source.id">
|
<tr v-for="source in sourcesWithErrors" :key="source.id">
|
||||||
<td>{{ source.subscription_name || source.subscription?.name || 'Unknown' }}</td>
|
<td>{{ getSourceLabel(source) }}</td>
|
||||||
<td class="text-capitalize">{{ source.platform }}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<v-chip color="error" size="small">
|
<v-chip color="error" size="small">
|
||||||
{{ source.error_count }} errors
|
{{ source.last_error_type || 'failed' }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ formatDate(source.last_check) }}</td>
|
<td>{{ formatDate(source.last_check) }}</td>
|
||||||
@@ -318,7 +316,7 @@ const credentialsStore = useCredentialsStore()
|
|||||||
const notifications = useNotificationStore()
|
const notifications = useNotificationStore()
|
||||||
|
|
||||||
// Supported platforms for credential status
|
// Supported platforms for credential status
|
||||||
const supportedPlatforms = ['patreon', 'subscribestar', 'hentaifoundry', 'discord']
|
const supportedPlatforms = ['patreon', 'subscribestar', 'hentaifoundry', 'discord', 'pixiv', 'deviantart']
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const checkingAll = ref(false)
|
const checkingAll = ref(false)
|
||||||
@@ -335,9 +333,27 @@ let refreshInterval = null
|
|||||||
// Computed
|
// Computed
|
||||||
const stats = computed(() => downloadsStore.stats)
|
const stats = computed(() => downloadsStore.stats)
|
||||||
const recentActivity = computed(() => downloadsStore.recentActivity)
|
const recentActivity = computed(() => downloadsStore.recentActivity)
|
||||||
const sourcesWithErrors = computed(() =>
|
|
||||||
sourcesStore.sources.filter(s => s.error_count > 0).slice(0, 5)
|
// Sources with recent failures - derived from recent activity
|
||||||
)
|
// Only shows sources where a recent download failed (not cumulative error_count)
|
||||||
|
const sourcesWithErrors = computed(() => {
|
||||||
|
const failedDownloads = recentActivity.value.filter(d => d.status === 'failed')
|
||||||
|
// Group by source_id and keep unique sources with their most recent failure
|
||||||
|
const sourceMap = new Map()
|
||||||
|
for (const dl of failedDownloads) {
|
||||||
|
if (dl.source_id && !sourceMap.has(dl.source_id)) {
|
||||||
|
sourceMap.set(dl.source_id, {
|
||||||
|
id: dl.source_id,
|
||||||
|
subscription_name: dl.subscription_name,
|
||||||
|
platform: dl.platform,
|
||||||
|
last_error_type: dl.error_type,
|
||||||
|
last_check: dl.created_at,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(sourceMap.values()).slice(0, 5)
|
||||||
|
})
|
||||||
|
|
||||||
const failedCount = computed(() => stats.value?.failed || 0)
|
const failedCount = computed(() => stats.value?.failed || 0)
|
||||||
const activeDownloadsCount = computed(() =>
|
const activeDownloadsCount = computed(() =>
|
||||||
(stats.value?.queued || 0) + (stats.value?.running || 0)
|
(stats.value?.queued || 0) + (stats.value?.running || 0)
|
||||||
@@ -565,10 +581,25 @@ function truncate(str, length) {
|
|||||||
return str.substring(0, length) + '...'
|
return str.substring(0, length) + '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDownloadLabel(download) {
|
||||||
|
if (download.subscription_name && download.platform) {
|
||||||
|
return `${download.subscription_name}:${download.platform}`
|
||||||
|
}
|
||||||
|
// Fallback to truncated URL if source info not available
|
||||||
|
return truncate(download.url, 35)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSourceLabel(source) {
|
||||||
|
const name = source.subscription_name || source.subscription?.name || 'Unknown'
|
||||||
|
return `${name}:${source.platform}`
|
||||||
|
}
|
||||||
|
|
||||||
async function retrySource(source) {
|
async function retrySource(source) {
|
||||||
try {
|
try {
|
||||||
await sourcesStore.triggerCheck(source.id)
|
await sourcesStore.triggerCheck(source.id)
|
||||||
notifications.success(`Queued check for ${source.subscription_name || source.platform}`)
|
notifications.success(`Queued check for ${getSourceLabel(source)}`)
|
||||||
|
// Refresh recent activity after retrying
|
||||||
|
await downloadsStore.fetchRecentActivity({ limit: 10 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Failed to queue check: ${error.message}`)
|
notifications.error(`Failed to queue check: ${error.message}`)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# GallerySubscriber - Project Summary
|
# GallerySubscriber - Project Summary
|
||||||
|
|
||||||
**Updated:** 2026-02-04 (Fixed Pixiv authentication - requires OAuth refresh token)
|
**Updated:** 2026-02-04 (Pixiv OAuth in extension, improved failed/naming display)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user