mirror of
https://github.com/xpltdco/media-rip.git
synced 2026-04-03 02:53:58 -06:00
Dynamic app version from git tag + file size display in queue
Version: - New app/__version__.py with 'dev' fallback for local dev - Dockerfile injects APP_VERSION build arg from CI tag - Health endpoint and footer now show actual release version - Test updated to accept 'dev' in non-Docker environments File size: - Capture filesize/filesize_approx from yt-dlp extract_info - Write to DB via update_job_progress and broadcast via SSE - New 'Size' column in download table (hidden on mobile) - formatSize helper: bytes → human-readable (KB/MB/GB) - Frontend store picks up filesize from SSE events
This commit is contained in:
parent
6f20d29151
commit
04f7fd09f3
8 changed files with 65 additions and 13 deletions
|
|
@ -45,6 +45,10 @@ RUN useradd --create-home --shell /bin/bash mediarip
|
|||
WORKDIR /app
|
||||
COPY backend/ ./
|
||||
|
||||
# Inject version from build arg (set by CI from git tag)
|
||||
ARG APP_VERSION=dev
|
||||
RUN echo "__version__ = \"${APP_VERSION}\"" > app/__version__.py
|
||||
|
||||
# Copy built frontend into backend static dir
|
||||
COPY --from=frontend-builder /build/frontend/dist ./static
|
||||
|
||||
|
|
|
|||
2
backend/app/__version__.py
Normal file
2
backend/app/__version__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Auto-generated at build time. Fallback for local dev.
|
||||
__version__ = "dev"
|
||||
|
|
@ -289,16 +289,27 @@ async def update_job_progress(
|
|||
speed: str | None = None,
|
||||
eta: str | None = None,
|
||||
filename: str | None = None,
|
||||
filesize: int | None = None,
|
||||
) -> None:
|
||||
"""Update live progress fields for a running download."""
|
||||
await db.execute(
|
||||
"""
|
||||
UPDATE jobs
|
||||
SET progress_percent = ?, speed = ?, eta = ?, filename = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(progress_percent, speed, eta, filename, job_id),
|
||||
)
|
||||
if filesize is not None:
|
||||
await db.execute(
|
||||
"""
|
||||
UPDATE jobs
|
||||
SET progress_percent = ?, speed = ?, eta = ?, filename = ?, filesize = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(progress_percent, speed, eta, filename, filesize, job_id),
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"""
|
||||
UPDATE jobs
|
||||
SET progress_percent = ?, speed = ?, eta = ?, filename = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(progress_percent, speed, eta, filename, job_id),
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ try:
|
|||
except ImportError: # pragma: no cover
|
||||
_yt_dlp_version = "unknown"
|
||||
|
||||
_APP_VERSION = "0.1.0"
|
||||
try:
|
||||
from app.__version__ import __version__ as _APP_VERSION
|
||||
except ImportError: # pragma: no cover
|
||||
_APP_VERSION = "dev"
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
|
|
|
|||
|
|
@ -430,6 +430,23 @@ class DownloadService:
|
|||
relative_fn = str(abs_path.relative_to(out_dir))
|
||||
except ValueError:
|
||||
relative_fn = abs_path.name
|
||||
|
||||
# Capture filesize from metadata
|
||||
file_size = info.get("filesize") or info.get("filesize_approx")
|
||||
if file_size:
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
update_job_progress(
|
||||
self._db, job_id, 0, None, None, relative_fn,
|
||||
filesize=int(file_size),
|
||||
),
|
||||
self._loop,
|
||||
).result(timeout=10)
|
||||
self._broker.publish(session_id, {
|
||||
"event": "job_update",
|
||||
"data": {"job_id": job_id, "status": "downloading",
|
||||
"percent": 0, "filename": relative_fn,
|
||||
"filesize": int(file_size)},
|
||||
})
|
||||
else:
|
||||
relative_fn = None
|
||||
|
||||
|
|
|
|||
|
|
@ -63,11 +63,11 @@ class TestHealthEndpoint:
|
|||
assert isinstance(data["queue_depth"], int) and data["queue_depth"] >= 0
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_health_version_is_semver(self, client):
|
||||
async def test_health_version_format(self, client):
|
||||
resp = await client.get("/api/health")
|
||||
version = resp.json()["version"]
|
||||
parts = version.split(".")
|
||||
assert len(parts) == 3, f"Expected semver, got {version}"
|
||||
# In Docker: semver (e.g. "1.1.4"). Locally: "dev".
|
||||
assert version == "dev" or len(version.split(".")) == 3, f"Unexpected version: {version}"
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_health_queue_depth_reflects_active_jobs(self, db):
|
||||
|
|
|
|||
|
|
@ -112,6 +112,13 @@ function isCompleted(job: Job): boolean {
|
|||
return job.status === 'completed'
|
||||
}
|
||||
|
||||
function formatSize(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`
|
||||
}
|
||||
|
||||
/** Infer whether the job is audio or video from quality/filename. */
|
||||
function isAudioJob(job: Job): boolean {
|
||||
if (job.quality === 'bestaudio') return true
|
||||
|
|
@ -184,6 +191,9 @@ async function clearJob(jobId: string): Promise<void> {
|
|||
<th class="col-speed sortable hide-mobile" @click="toggleSort('speed')">
|
||||
Speed{{ sortIndicator('speed') }}
|
||||
</th>
|
||||
<th class="col-size hide-mobile">
|
||||
Size
|
||||
</th>
|
||||
<th class="col-eta sortable hide-mobile" @click="toggleSort('eta')">
|
||||
ETA{{ sortIndicator('eta') }}
|
||||
</th>
|
||||
|
|
@ -227,6 +237,10 @@ async function clearJob(jobId: string): Promise<void> {
|
|||
<span v-if="job.speed" class="mono">{{ job.speed }}</span>
|
||||
<span v-else class="text-muted">—</span>
|
||||
</td>
|
||||
<td class="col-size hide-mobile">
|
||||
<span v-if="job.filesize" class="mono">{{ formatSize(job.filesize) }}</span>
|
||||
<span v-else class="text-muted">—</span>
|
||||
</td>
|
||||
<td class="col-eta hide-mobile">
|
||||
<span v-if="job.eta" class="mono">{{ job.eta }}</span>
|
||||
<span v-else class="text-muted">—</span>
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ export const useDownloadsStore = defineStore('downloads', () => {
|
|||
if (event.speed !== null) existing.speed = event.speed
|
||||
if (event.eta !== null) existing.eta = event.eta
|
||||
if (event.filename !== null) existing.filename = event.filename
|
||||
if (event.filesize) existing.filesize = event.filesize
|
||||
if (event.error_message) existing.error_message = event.error_message
|
||||
// Trigger reactivity by re-setting the map entry
|
||||
jobs.value.set(event.job_id, { ...existing })
|
||||
|
|
@ -111,7 +112,7 @@ export const useDownloadsStore = defineStore('downloads', () => {
|
|||
quality: null,
|
||||
output_template: null,
|
||||
filename: event.filename ?? null,
|
||||
filesize: null,
|
||||
filesize: event.filesize ?? null,
|
||||
progress_percent: event.percent,
|
||||
speed: event.speed ?? null,
|
||||
eta: event.eta ?? null,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue