curl --request GET \
--url https://api.example.com/api/thumbnails/{task_id}{
"404": {},
"Content-Type": "<string>",
"Content-Length": 123,
"body": {}
}Retrieve locally stored thumbnail images for downloaded videos with automatic format detection
curl --request GET \
--url https://api.example.com/api/thumbnails/{task_id}{
"404": {},
"Content-Type": "<string>",
"Content-Length": 123,
"body": {}
}POST /api/download.image/jpg - JPEG format (most common)image/webp - WebP format (modern, efficient)image/png - PNG format (lossless)for ext in ("jpg", "webp", "png"):
thumb = THUMBNAILS_DIR / f"{task_id}.{ext}"
if thumb.exists():
return FileResponse(thumb, media_type=f"image/{ext}")
writethumbnail: True option tells yt-dlp to download the video’s thumbnailhttps://i.ytimg.com/vi/{video_id}/hqdefault.jpg
/api/thumbnails/{task_id} endpoint only serves locally stored thumbnails.
curl http://localhost:8001/api/thumbnails/123e4567-e89b-12d3-a456-426614174000
HTTP/1.1 200 OK
Content-Type: image/jpg
Content-Length: 48291
[binary JPEG data...]
<img src="http://localhost:8001/api/thumbnails/123e4567-e89b-12d3-a456-426614174000"
alt="Video thumbnail"
width="320"
height="180">
<video controls
poster="http://localhost:8001/api/thumbnails/123e4567-e89b-12d3-a456-426614174000">
<source src="/api/stream/video__123e4567-e89b-12d3.mp4" type="video/mp4">
</video>
const taskId = "123e4567-e89b-12d3-a456-426614174000";
const thumbnailUrl = `http://localhost:8001/api/thumbnails/${taskId}`;
fetch(thumbnailUrl)
.then(response => {
if (response.ok) {
return response.blob();
}
throw new Error('Thumbnail not found');
})
.then(blob => {
const imgUrl = URL.createObjectURL(blob);
document.getElementById('thumbnail').src = imgUrl;
})
.catch(error => {
console.error('Error loading thumbnail:', error);
});
function VideoThumbnail({ taskId }) {
const [thumbnailUrl, setThumbnailUrl] = useState(null);
const [error, setError] = useState(false);
useEffect(() => {
const url = `http://localhost:8001/api/thumbnails/${taskId}`;
fetch(url)
.then(response => {
if (response.ok) {
setThumbnailUrl(url);
} else {
setError(true);
}
})
.catch(() => setError(true));
}, [taskId]);
if (error) {
return <div>No thumbnail available</div>;
}
return thumbnailUrl ? (
<img src={thumbnailUrl} alt="Video thumbnail" />
) : (
<div>Loading...</div>
);
}
{
"detail": "Miniatura no encontrada"
}
/path/to/offlinetube-api/thumbnails/
├── 123e4567-e89b-12d3-a456-426614174000.jpg
├── 234e5678-f90c-23e4-b567-537725285111.webp
└── 345f6789-g01d-34f5-c678-648836396222.png
{task_id}.{ext}
DELETE /api/downloads/{download_id}/remove removes both the video file and thumbnailfor ext in ("jpg", "webp", "png"):
try:
(THUMBNAILS_DIR / f"{download_id}.{ext}").unlink(missing_ok=True)
except Exception:
pass
GET /api/downloads endpoint returns thumbnail URLs that automatically use this endpoint for local thumbnails:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"title": "Example Video",
"thumbnail": "/api/thumbnails/123e4567-e89b-12d3-a456-426614174000",
"status": "completed",
...
}
https://i.ytimg.com/vi/...).