feat: add media resolution updates & API endpoints
Introduced a new API endpoint to retrieve media for a given category, enhancing the user experience by dynamically updating thumbnails post-upload based on available resolutions. This connects the frontend to the backend efficiently, ensuring users can see the immediate impact of their uploads. Additionally, updated serializers and views in the backend to support this functionality, alongside improving code comments for future development clarity. - Implemented `getCategoryMedia` in the JS API for fetching media resolutions. - Enabled dynamic thumbnail updates in the user area post file upload, improving visual feedback. - Extended the Django REST framework routers to include the new media endpoint, facilitating easier access to media items. - Updated the `OriginalMediaSerializer` to include `category` for more detailed media information. - Enriched task and views files with necessary TODO comments to guide future enhancements and exception handling. This change significantly improves the content management workflow, making it more interactive and user-friendly.
This commit is contained in:
parent
6dc56e02bd
commit
d921c2dcad
6 changed files with 56 additions and 12 deletions
|
@ -50,6 +50,22 @@ function getCategory(category) {
|
|||
);
|
||||
}
|
||||
|
||||
function getCategoryMedia(category_uuid, uuid) {
|
||||
return api
|
||||
.then(
|
||||
(client) =>
|
||||
client.apis.tours.tours_api_category_media_retrieve({
|
||||
category: category_uuid,
|
||||
id: uuid,
|
||||
}),
|
||||
(reason) => console.error("Failed to load OpenAPI spec: " + reason)
|
||||
)
|
||||
.then(
|
||||
(result) => result,
|
||||
(reason) => console.error("Failed to execute API call: " + reason)
|
||||
);
|
||||
}
|
||||
|
||||
// Function to get the CSRF token cookie. Not exactly "API", but fits here best.
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
|
@ -66,4 +82,4 @@ function getCookie(name) {
|
|||
return cookieValue;
|
||||
}
|
||||
|
||||
export { getScene, getSceneElement, getCategory, getCookie };
|
||||
export { getScene, getSceneElement, getCategory, getCategoryMedia, getCookie };
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import "../scss/frontend.scss";
|
||||
import "../css/userarea.css";
|
||||
|
||||
import { getCookie, getElementById } from "./api";
|
||||
import { getCategoryMedia, getCookie, getElementById } from "./api";
|
||||
|
||||
import { Tab } from "bootstrap";
|
||||
import DataTable from "datatables.net-dt";
|
||||
|
@ -61,7 +61,7 @@ function handleFiles(files) {
|
|||
thumbnailCol.classList.add("col-2");
|
||||
const thumbnail = document.createElement("img");
|
||||
thumbnail.classList.add("img-fluid", "thumbnail");
|
||||
thumbnail.src = ""; // Placeholder until upload completes
|
||||
thumbnail.src = ""; // TODO: Add a loading spinner or something here
|
||||
thumbnailCol.appendChild(thumbnail);
|
||||
|
||||
const fileNameCol = document.createElement("div");
|
||||
|
@ -129,11 +129,19 @@ function uploadFile(file, progressBar, thumbnail) {
|
|||
xhr.onload = () => {
|
||||
if (xhr.status === 201) {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
progressBar.classList.add("bg-success");
|
||||
progressBar.textContent = "Success!";
|
||||
progressBar.textContent = "Converting...";
|
||||
|
||||
// TODO: Check API until the first resolution is available
|
||||
let thumbnailCheck = setInterval(() => {
|
||||
getCategoryMedia(response.category, response.id).then((media) => {
|
||||
if (media.obj.resolutions.length > 0) {
|
||||
clearInterval(thumbnailCheck);
|
||||
thumbnail.src = media.obj.resolutions[0].file;
|
||||
|
||||
progressBar.classList.add("bg-success");
|
||||
progressBar.textContent = "Done!";
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
} else {
|
||||
progressBar.classList.add("bg-danger");
|
||||
progressBar.textContent = "Error!";
|
||||
|
|
|
@ -76,7 +76,15 @@ class OriginalMediaSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = OriginalMedia
|
||||
fields = ["id", "title", "media_type", "width", "height", "resolutions"]
|
||||
fields = [
|
||||
"id",
|
||||
"category",
|
||||
"title",
|
||||
"media_type",
|
||||
"width",
|
||||
"height",
|
||||
"resolutions",
|
||||
]
|
||||
|
||||
def to_representation(self, instance):
|
||||
request = self.context.get("request")
|
||||
|
@ -138,6 +146,6 @@ class CategorySerializer(serializers.ModelSerializer):
|
|||
|
||||
def to_representation(self, instance):
|
||||
ret = super().to_representation(instance)
|
||||
ret['media'] = [m for m in ret['media'] if m is not None]
|
||||
ret['scenes'] = [s for s in ret['scenes'] if s is not None]
|
||||
return ret
|
||||
ret["media"] = [m for m in ret["media"] if m is not None]
|
||||
ret["scenes"] = [s for s in ret["scenes"] if s is not None]
|
||||
return ret
|
||||
|
|
|
@ -22,6 +22,8 @@ def create_image_resolutions(image: "OriginalImage"):
|
|||
if isinstance(image, (str, uuid.UUID)):
|
||||
image = OriginalImage.objects.get(id=image)
|
||||
|
||||
# TODO: Exception handling
|
||||
|
||||
with Image.open(image.file) as img:
|
||||
resolutions = settings.QUACKSCAPE_CONTENT_RESOLUTIONS
|
||||
|
||||
|
@ -51,6 +53,9 @@ def create_video_resolutions(video: "OriginalVideo"):
|
|||
|
||||
if isinstance(video, (str, uuid.UUID)):
|
||||
video = OriginalVideo.objects.get(id=video)
|
||||
|
||||
# TODO: Testing
|
||||
# TODO: Exception handling
|
||||
|
||||
resolutions = settings.QUACKSCAPE_CONTENT_RESOLUTIONS
|
||||
ffmpeg_options = settings.FFMPEG_OPTIONS[settings.FFMPEG_DEFAULT_OPTION]
|
||||
|
|
|
@ -2,13 +2,14 @@ from django.urls import path, include
|
|||
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from .views import SceneAPIViewSet, SceneView, SceneEditView, SceneEmbedView, ElementAPIViewSet, CategoryAPIViewSet
|
||||
from .views import SceneAPIViewSet, SceneView, SceneEditView, SceneEmbedView, ElementAPIViewSet, CategoryAPIViewSet, MediaAPIViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'scenes', SceneAPIViewSet, "scene")
|
||||
router.register(r'scene/(?P<scene>[^/.]+)/elements', ElementAPIViewSet, "element")
|
||||
router.register(r'categories', CategoryAPIViewSet, "category")
|
||||
router.register(r'category/(?P<category>[^/.]+)/scenes', SceneAPIViewSet, "category-scene")
|
||||
router.register(r'category/(?P<category>[^/.]+)/media', MediaAPIViewSet, "category-media")
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include(router.urls)),
|
||||
|
|
|
@ -5,11 +5,12 @@ from django.core.exceptions import PermissionDenied
|
|||
|
||||
from rest_framework import viewsets
|
||||
|
||||
from .models import Scene, Element, Category
|
||||
from .models import Scene, Element, Category, OriginalMedia
|
||||
from .serializers import (
|
||||
SceneSerializer,
|
||||
ElementPolymorphicSerializer,
|
||||
CategorySerializer,
|
||||
OriginalMediaSerializer,
|
||||
)
|
||||
|
||||
|
||||
|
@ -63,3 +64,8 @@ class SceneEmbedView(SceneView):
|
|||
class CategoryAPIViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = CategorySerializer
|
||||
queryset = Category.objects.all()
|
||||
|
||||
|
||||
class MediaAPIViewSet(viewsets.GenericViewSet, viewsets.mixins.RetrieveModelMixin):
|
||||
serializer_class = OriginalMediaSerializer
|
||||
queryset = OriginalMedia.objects.all()
|
Loading…
Reference in a new issue