Add thumbnail endpoint (#2468)
- Add /thumbnail collections endpoint to serve the thumbnail as an image for public collections. - Also fix uploading thumbnail images to use correct mime, if available.
This commit is contained in:
parent
13bf818914
commit
6c192df49d
@ -11,6 +11,7 @@ import os
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import pymongo
|
import pymongo
|
||||||
|
import aiohttp
|
||||||
from pymongo.collation import Collation
|
from pymongo.collation import Collation
|
||||||
from fastapi import Depends, HTTPException, Response
|
from fastapi import Depends, HTTPException, Response
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
@ -407,6 +408,34 @@ class CollectionOps:
|
|||||||
|
|
||||||
return PublicCollOut.from_dict(result)
|
return PublicCollOut.from_dict(result)
|
||||||
|
|
||||||
|
async def get_public_thumbnail(
|
||||||
|
self, slug: str, org: Organization
|
||||||
|
) -> StreamingResponse:
|
||||||
|
"""return thumbnail of public collection, if any"""
|
||||||
|
result = await self.get_collection_raw_by_slug(
|
||||||
|
slug, public_or_unlisted_only=True
|
||||||
|
)
|
||||||
|
|
||||||
|
thumbnail = result.get("thumbnail")
|
||||||
|
if not thumbnail:
|
||||||
|
raise HTTPException(status_code=404, detail="thumbnail_not_found")
|
||||||
|
|
||||||
|
image_file = ImageFile(**thumbnail)
|
||||||
|
image_file_out = await image_file.get_public_image_file_out(
|
||||||
|
org, self.storage_ops
|
||||||
|
)
|
||||||
|
|
||||||
|
path = self.storage_ops.resolve_internal_access_path(image_file_out.path)
|
||||||
|
|
||||||
|
async def reader():
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(path) as resp:
|
||||||
|
async for chunk in resp.content.iter_chunked(4096):
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
headers = {"Cache-Control": "max-age=3600, stale-while-revalidate=86400"}
|
||||||
|
return StreamingResponse(reader(), media_type=image_file.mime, headers=headers)
|
||||||
|
|
||||||
async def list_collections(
|
async def list_collections(
|
||||||
self,
|
self,
|
||||||
org: Organization,
|
org: Organization,
|
||||||
@ -852,6 +881,7 @@ class CollectionOps:
|
|||||||
file_prep.upload_name,
|
file_prep.upload_name,
|
||||||
stream_iter(),
|
stream_iter(),
|
||||||
MIN_UPLOAD_PART_SIZE,
|
MIN_UPLOAD_PART_SIZE,
|
||||||
|
mime=file_prep.mime,
|
||||||
):
|
):
|
||||||
print("Collection thumbnail stream upload failed", flush=True)
|
print("Collection thumbnail stream upload failed", flush=True)
|
||||||
raise HTTPException(status_code=400, detail="upload_failed")
|
raise HTTPException(status_code=400, detail="upload_failed")
|
||||||
@ -1175,6 +1205,24 @@ def init_collections_api(
|
|||||||
|
|
||||||
return await colls.download_collection(coll.id, org)
|
return await colls.download_collection(coll.id, org)
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
"/public/orgs/{org_slug}/collections/{coll_slug}/thumbnail",
|
||||||
|
tags=["collections", "public"],
|
||||||
|
response_class=StreamingResponse,
|
||||||
|
)
|
||||||
|
async def get_public_thumbnail(
|
||||||
|
org_slug: str,
|
||||||
|
coll_slug: str,
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
org = await colls.orgs.get_org_by_slug(org_slug)
|
||||||
|
# pylint: disable=broad-exception-caught
|
||||||
|
except Exception:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
|
raise HTTPException(status_code=404, detail="collection_not_found")
|
||||||
|
|
||||||
|
return await colls.get_public_thumbnail(coll_slug, org)
|
||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/orgs/{oid}/collections/{coll_id}/home-url",
|
"/orgs/{oid}/collections/{coll_id}/home-url",
|
||||||
tags=["collections"],
|
tags=["collections"],
|
||||||
|
@ -382,6 +382,7 @@ class StorageOps:
|
|||||||
filename: str,
|
filename: str,
|
||||||
file_: AsyncIterator,
|
file_: AsyncIterator,
|
||||||
min_size: int,
|
min_size: int,
|
||||||
|
mime: Optional[str] = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""do upload to specified key using multipart chunking"""
|
"""do upload to specified key using multipart chunking"""
|
||||||
s3storage = self.get_org_primary_storage(org)
|
s3storage = self.get_org_primary_storage(org)
|
||||||
@ -405,7 +406,10 @@ class StorageOps:
|
|||||||
key += filename
|
key += filename
|
||||||
|
|
||||||
mup_resp = await client.create_multipart_upload(
|
mup_resp = await client.create_multipart_upload(
|
||||||
ACL="bucket-owner-full-control", Bucket=bucket, Key=key
|
ACL="bucket-owner-full-control",
|
||||||
|
Bucket=bucket,
|
||||||
|
Key=key,
|
||||||
|
ContentType=mime or "",
|
||||||
)
|
)
|
||||||
|
|
||||||
upload_id = mup_resp["UploadId"]
|
upload_id = mup_resp["UploadId"]
|
||||||
|
Loading…
Reference in New Issue
Block a user