ALL PROJECTS/GLOBAL MEDIA PLATFORM

Overview

Global Media Platform is a media publishing and distribution system built around a modular Next.js/TypeScript front end and a Django + DRF backend. It serves editorial and media teams who need to upload, process, organise and publish media assets, with AI assistance woven into the content workflow rather than bolted on afterwards. The backend holds the storage, processing and AI integration surface; the front end is a component-driven presentation and authoring layer deployed independently.

Problem

Managing and distributing media at scale needs more than a CMS. Once assets live in object storage rather than on a local disk, the concerns start to pull apart: uploads have to be streamed somewhere durable, images have to be derived into usable renditions, metadata has to stay queryable, and the front end has to remain fast while consuming all of it over an API boundary. Layering AI assistance on top of that adds a further constraint — model calls are slow, fallible and external, so they cannot sit on the critical path of a page render. The design problem was therefore less about features and more about boundaries: where storage ends and processing begins, which parts of the system are allowed to talk to OpenAI, and how a typed front end consumes a Django API without either side leaking the other's shape.

System Design

The backend is a Django 4.2 project structured around a deliberate core/ and apps/ split, with config/ holding settings, URL roots and WSGI/ASGI entry points. core/ carries the shared substrate — base models, common serialiser and view mixins, storage adapters, integration clients — while each feature lives as a self-contained Django app under apps/. This keeps the dependency direction one-way: feature apps import from core, never from each other. The HTTP surface is Django REST Framework, with djangorestframework-simplejwt providing stateless access/refresh token authentication, django-filter giving list endpoints declarative filtering and query-parameter search, and django-cors-headers allowing the separately deployed front end to call the API cross-origin. Configuration is read from the environment via python-dotenv, so the same image runs against SQLite locally (db.sqlite3 is checked in for development) and a managed database in deployment.

Media handling is the backend's centre of gravity. django-storages is configured with a boto3 S3 backend so that Django's FileField/ImageField write straight to object storage instead of the local filesystem, and reads resolve to S3 URLs. Pillow handles image processing — validation, dimension inspection and derivation of the renditions the front end consumes — so the API returns processed, correctly sized assets rather than raw originals. AI assistance is a separate integration seam: the openai client, with httpx as the underlying transport, sits behind service functions in the backend rather than being called from views directly, which keeps model interaction swappable and testable and prevents an external timeout from becoming an unhandled request failure. The whole thing ships as a container — there is a Dockerfile, a .dockerignore and a deploy/ directory, driven by a .gitlab-ci.yml pipeline.

The front end is a Next.js 14 application on the app router, written in TypeScript with strict typing throughout. Rather than the conventional flat components/ tree, it separates concerns across modules/ (feature-scoped composites), components/ (presentational primitives), lib/ (API clients, formatting and shared helpers), types/ (the TypeScript contracts mirroring the DRF payloads) and data/ (static and structured content). Styling is Tailwind CSS with PostCSS and Autoprefixer; iconography comes from lucide-react. Because the app router renders on the server by default, list and detail pages fetch from the Django API server-side, keeping payloads and credentials off the client and letting individual segments opt into client interactivity only where they need it. Deployment targets Vercel via vercel.json, with .env.production supplying the API base URL — the two halves of the system are versioned, built and released independently.

How It Works

01/03
01

Media Upload & Processing

Authenticated uploads are validated, processed with Pillow, and streamed to S3 so no media ever lands on the application container's disk.

  1. 1An authenticated client posts a multipart upload to a DRF endpoint in the relevant app under apps/.
  2. 2The serialiser validates the payload and the file type before any write occurs.
  3. 3Pillow inspects and processes the image, verifying it decodes, reading its dimensions and producing the derived renditions the front end expects.
  4. 4django-storages streams the original and its renditions to S3 through the boto3 backend, keeping nothing on the application container's disk.
  5. 5Django persists the asset record with its S3 keys and metadata, and the endpoint returns the serialised object with resolved storage URLs.
02

AI Content Assistance

Editorial assistance requests are authenticated, routed through a backend service layer to OpenAI, then normalised and persisted against the record rather than returned raw.

  1. 1An editor triggers an assistance action from the front end against a stored asset or draft.
  2. 2The DRF view authenticates the request via a SimpleJWT bearer token and hands off to a service function in core/.
  3. 3That service builds the prompt from the persisted record and calls OpenAI through the openai client over httpx.
  4. 4The response is parsed and normalised into the app's own schema rather than being returned raw.
  5. 5The result is written back against the record and serialised to the caller, so the assistance output is durable and re-readable rather than transient.
03

Content Delivery

Next.js app router segments fetch from the Django API server-side through typed clients, so schema drift surfaces at build time and only interactive leaves ship JavaScript.

  1. 1A request hits a Next.js app router segment, which runs on the server.
  2. 2The segment calls a typed client in lib/, which issues the request to the Django API with the base URL from .env.production.
  3. 3DRF resolves the query, django-filter applies any filtering or search parameters, and the result set is serialised.
  4. 4The response is parsed against the interfaces in types/, so a schema drift on the backend surfaces as a TypeScript error at build time.
  5. 5Feature composites in modules/ render the data using primitives from components/, and only interactive leaves ship JavaScript to the browser.

Key Features

  • Media publishing & management
  • S3-backed storage
  • AI content assistance (OpenAI)
  • Modular, typed Next.js front end
  • Scalable Django core

Outcomes

  • Scalable media handling
  • AI-assisted workflows
  • Clean separation of concerns

More work