251 lines
12 KiB
PHP
251 lines
12 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/app.php';
|
|
|
|
app_boot();
|
|
$jobId = trim((string)($_GET['id'] ?? ''));
|
|
$job = $jobId !== '' ? find_job($jobId) : null;
|
|
$notice = trim((string)($_GET['notice'] ?? ''));
|
|
$noticeType = trim((string)($_GET['type'] ?? 'info')) ?: 'info';
|
|
$toolLabel = $job ? job_tool_label($job) : 'Conversion';
|
|
$conversionSummary = $job ? job_conversion_summary($job) : 'Job summary';
|
|
$meta = page_meta(project_name() . ' — ' . $toolLabel . ' job', 'Review the result, status, and download link for a completed conversion job.');
|
|
|
|
$statusTitle = 'Conversion in progress';
|
|
$statusCopy = 'This job is still processing. Refresh the page in a few seconds if needed.';
|
|
if ($job) {
|
|
if (($job['status'] ?? '') === 'completed') {
|
|
$statusTitle = 'Download ready';
|
|
$statusCopy = 'The conversion finished successfully and the output file is ready for download.';
|
|
} elseif (($job['status'] ?? '') === 'failed') {
|
|
$statusTitle = 'Conversion failed';
|
|
$statusCopy = 'The job did not finish cleanly. Review the error details below and try again.';
|
|
}
|
|
}
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title><?= h($meta['title']) ?></title>
|
|
<meta name="description" content="<?= h($meta['description']) ?>">
|
|
<?php if ($meta['description'] !== ''): ?>
|
|
<meta property="og:description" content="<?= h($meta['description']) ?>">
|
|
<meta property="twitter:description" content="<?= h($meta['description']) ?>">
|
|
<?php endif; ?>
|
|
<?php if ($meta['image'] !== ''): ?>
|
|
<meta property="og:image" content="<?= h($meta['image']) ?>">
|
|
<meta property="twitter:image" content="<?= h($meta['image']) ?>">
|
|
<?php endif; ?>
|
|
<meta property="og:title" content="<?= h($meta['title']) ?>">
|
|
<meta property="twitter:title" content="<?= h($meta['title']) ?>">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<script>
|
|
(() => {
|
|
try {
|
|
const savedTheme = localStorage.getItem('fs-theme');
|
|
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
const theme = savedTheme || (prefersDark ? 'dark' : 'light');
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
document.documentElement.setAttribute('data-bs-theme', theme);
|
|
} catch (error) {
|
|
document.documentElement.setAttribute('data-theme', 'light');
|
|
document.documentElement.setAttribute('data-bs-theme', 'light');
|
|
}
|
|
})();
|
|
</script>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="/assets/css/custom.css?v=<?= time() ?>">
|
|
</head>
|
|
<body data-app-state="<?= ffmpeg_is_available() ? 'ready' : 'offline' ?>" data-upload-limit-mb="<?= h((string)effective_upload_limit_mb()) ?>">
|
|
<nav class="navbar navbar-expand-lg app-nav sticky-top">
|
|
<div class="container">
|
|
<div class="d-flex align-items-center justify-content-between w-100 gap-3 flex-wrap">
|
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/" aria-label="<?= h(project_name()) ?> home">
|
|
<span class="brand-mark">FS</span>
|
|
<span><?= h(project_name()) ?></span>
|
|
</a>
|
|
<div class="nav-actions d-flex align-items-center gap-2 gap-lg-3 small text-secondary">
|
|
<a class="nav-link px-0" href="/">Dashboard</a>
|
|
<a class="nav-link px-0" href="/healthz.php">Health</a>
|
|
<button class="btn btn-sm btn-soft theme-toggle" id="theme-toggle" type="button" aria-label="Toggle color theme" aria-pressed="false">
|
|
<span class="theme-toggle-icon" id="theme-toggle-icon" aria-hidden="true">🌙</span>
|
|
<span class="theme-toggle-label" id="theme-toggle-label">Dark mode</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="py-4 py-lg-5">
|
|
<div class="page-orb orb-left" aria-hidden="true"></div>
|
|
<div class="page-orb orb-right" aria-hidden="true"></div>
|
|
<div class="container position-relative">
|
|
<?php if ($notice !== ''): ?>
|
|
<div class="alert alert-<?= h($noticeType) ?> app-alert shadow-sm" role="alert">
|
|
<?= h($notice) ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!$job): ?>
|
|
<section class="app-card empty-state-card">
|
|
<h1 class="detail-title">Job not found</h1>
|
|
<p class="hero-copy mb-4">The requested conversion record is missing or has already expired from the retention window.</p>
|
|
<a class="btn btn-dark" href="/">Back to dashboard</a>
|
|
</section>
|
|
<?php else: ?>
|
|
<section class="hero-shell mb-4">
|
|
<div class="row g-4 align-items-stretch">
|
|
<div class="col-xl-8">
|
|
<div class="hero-panel h-100">
|
|
<span class="eyebrow"><?= h($toolLabel) ?></span>
|
|
<h1 class="detail-title"><?= h($conversionSummary) ?></h1>
|
|
<p class="hero-copy mb-4 text-break">Original file: <strong><?= h((string)$job['original_name']) ?></strong></p>
|
|
<div class="hero-actions">
|
|
<?php if (($job['status'] ?? '') === 'completed' && job_output_exists($job)): ?>
|
|
<a class="btn btn-dark btn-lg" href="/download.php?id=<?= urlencode((string)$job['public_id']) ?>"><?= h(job_download_label($job)) ?></a>
|
|
<?php endif; ?>
|
|
<a class="btn btn-soft btn-lg" href="/">Run another conversion</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-4">
|
|
<aside class="app-card app-card-highlight h-100">
|
|
<div class="status-summary status-summary-<?= h((string)$job['status']) ?>">
|
|
<div class="status-summary-label">Current state</div>
|
|
<div class="status-summary-title"><?= h($statusTitle) ?></div>
|
|
<p class="mb-0"><?= h($statusCopy) ?></p>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-lg-8">
|
|
<section class="app-card mb-4">
|
|
<div class="card-header-row mb-4">
|
|
<div>
|
|
<h2 class="section-title mb-1">Job metrics</h2>
|
|
<p class="section-subtitle mb-0">A concise view of the source, output, preset, and runtime status for this job.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="detail-grid">
|
|
<div class="detail-item">
|
|
<span>Converter</span>
|
|
<strong><?= h($toolLabel) ?></strong>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span>Formats</span>
|
|
<strong><?= h($conversionSummary) ?></strong>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span>Input size</span>
|
|
<strong><?= h(format_bytes(isset($job['input_size']) ? (int)$job['input_size'] : null)) ?></strong>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span>Output size</span>
|
|
<strong><?= h(format_bytes(isset($job['output_size']) ? (int)$job['output_size'] : null)) ?></strong>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span>Started</span>
|
|
<strong><?= h(format_datetime((string)$job['created_at'])) ?></strong>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span>Completed</span>
|
|
<strong><?= h(format_datetime((string)($job['completed_at'] ?? ''))) ?></strong>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (($job['status'] ?? '') === 'completed' && job_output_exists($job)): ?>
|
|
<div class="download-panel mt-4">
|
|
<div class="download-panel-copy">
|
|
<h2 class="section-title mb-2"><?= h(job_download_label($job)) ?></h2>
|
|
<p class="mb-0">The output file is available now. Downloads remain available until the <?= h((string)APP_RETENTION_HOURS) ?>-hour retention window expires.</p>
|
|
</div>
|
|
<div class="d-flex flex-column flex-sm-row gap-2 mt-3">
|
|
<a class="btn btn-dark" href="/download.php?id=<?= urlencode((string)$job['public_id']) ?>"><?= h(job_download_label($job)) ?></a>
|
|
<a class="btn btn-soft" href="/">Go back to dashboard</a>
|
|
</div>
|
|
</div>
|
|
<?php elseif (($job['status'] ?? '') === 'failed'): ?>
|
|
<div class="alert alert-danger mt-4 mb-0 app-alert-block">
|
|
<strong>Conversion failed.</strong>
|
|
<div class="small mt-2"><?= nl2br(h((string)($job['error_message'] ?? 'The converter did not finish successfully.'))) ?></div>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="alert alert-warning mt-4 mb-0 app-alert-block">
|
|
This job is still marked as processing. Refresh the page in a few seconds if needed.
|
|
</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
|
|
<section class="workflow-grid workflow-grid-2">
|
|
<article class="app-card flow-card">
|
|
<span class="flow-step">A</span>
|
|
<h2 class="section-title mb-2">Input captured</h2>
|
|
<p class="mb-0">The source file and requested tool settings were stored before processing started, so the result can be tracked even if the run fails.</p>
|
|
</article>
|
|
<article class="app-card flow-card">
|
|
<span class="flow-step">B</span>
|
|
<h2 class="section-title mb-2">Output retained</h2>
|
|
<p class="mb-0">Generated files stay available for <?= h((string)APP_RETENTION_HOURS) ?> hours, after which cleanup removes the temporary artifacts.</p>
|
|
</article>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<aside class="app-card h-100">
|
|
<h2 class="section-title mb-3">Run details</h2>
|
|
<dl class="meta-list mb-4">
|
|
<div>
|
|
<dt>Original file</dt>
|
|
<dd class="text-break"><?= h((string)$job['original_name']) ?></dd>
|
|
</div>
|
|
<div>
|
|
<dt>Status</dt>
|
|
<dd><?= h(ucfirst((string)$job['status'])) ?></dd>
|
|
</div>
|
|
<div>
|
|
<dt>Preset</dt>
|
|
<dd><?= h(job_preset_label($job) !== '' ? job_preset_label($job) : '—') ?></dd>
|
|
</div>
|
|
<div>
|
|
<dt>Download name</dt>
|
|
<dd class="text-break"><?= h(job_download_name($job)) ?></dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<div class="mini-note">
|
|
<div class="mini-note-title">Next move</div>
|
|
<ul class="mini-steps mb-0 ps-3">
|
|
<?php if (($job['status'] ?? '') === 'completed'): ?>
|
|
<li>Download the generated file while it is still within the retention window.</li>
|
|
<li>Return to the dashboard if you want to run another tool or preset.</li>
|
|
<?php elseif (($job['status'] ?? '') === 'failed'): ?>
|
|
<li>Review the error output above for clues.</li>
|
|
<li>Try a different source file, output choice, or a smaller upload.</li>
|
|
<?php else: ?>
|
|
<li>Refresh the page after a short wait.</li>
|
|
<li>If it stays stuck, start a fresh run from the dashboard.</li>
|
|
<?php endif; ?>
|
|
</ul>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</main>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" defer></script>
|
|
<script src="/assets/js/main.js?v=<?= time() ?>" defer></script>
|
|
</body>
|
|
</html>
|