Build a Video Processing Pipeline with AssemblyAI on Koyeb
Introduction
In the digital era, video is the king, demanding innovative solutions for efficient processing and distribution.
This guide introduces a powerful approach using Koyeb's cloud services to build a scalable video processing pipeline. We will rely on secure protocols for video uploads, employ AI-driven tagging and classification via AssemblyAI, and leverage Koyeb's built-in CDN technology for global content distribution. Embrace the power of serverless architecture to meet the growing demand for video content, ensuring optimal performance and viewer satisfaction.
You can follow along with this guide by viewing the GitHub repositories for the video web app and the video worker service.
Requirements
Before diving into building your video processing pipeline with Koyeb, it's important to ensure that you have the necessary tools and knowledge. This section outlines the prerequisites needed to follow the upcoming guide successfully.
- A Koyeb account will be required for deploying. It will be helpful to have a foundational understanding of its service offerings (web service and database, in this case).
- An AssemblyAI API key to integrate AI-driven video tagging and classification capabilities. Note: You will need to add credit to your account to use the LLM features implemented in this guide.
- Knowledge of Python programming for scripting and automation within the serverless architecture.
- Experience with Django for developing robust, scalable web applications that interfaces effectively with back-end services.
Steps
- Set up the database: This step involves setting up a database service on Koyeb to store and manage video metadata. This database will be used by the web application to store and access data.
- Build the web application: This section guides you through developing a simple web application for video uploads along with features to view and search videos. The application is built with Django.
- Build the worker service: This step covers implementing a service API that processes the video uploads. The API will incorporate AI technologies from AssemblyAI for tagging, classifying, and potentially transcoding videos. Additionally, the API will utilize Koyeb's autoscaling features to efficiently manage varying video processing loads.
- Integrate with Koyeb's edge network: This section details how to integrate the web application with Koyeb's edge network to enhance the distribution of video content globally.
Set up the database
Setting up the database is an important part of this process. This database will be used to keep and organize video metadata, such as video titles, descriptions, lengths, file types, and other important information.
For this article you are going to setup a PostgreSQL database using Koyeb's recent fully-managed serverless PostgreSQL databases feature.
Here's a step-by-step guide on how to set up the database on Koyeb:
- In the Koyeb control panel, click Create Database Service.
- Choose alternatives to or confirm the provided defaults for the name and role fields.
- Choose the region closest to you or your users.
- Select the database size. If you are not already using it, you can deploy the Free tire as the Instance type.
- Click Create Database Service.
Once the database is created, access the database's detail page and there you can check the connection details.
Since you will be using Django later, select Django and copy the database connection details. Store this locally somewhere so that you can reference it later. It will look like something like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'koyebdb',
'USER': 'koyeb-adm',
'PASSWORD': <YOUR_DB_PASSWORD>,
'HOST': '<YOUR_DB_URL>.eu-central-1.pg.koyeb.app',
'OPTIONS': {'sslmode': 'require'},
}
}
Once you've completed these steps, your database will be ready to store and manage video metadata for your application.
Build the web application
This part of the guide will show you how to make a simple web application using Django and deploy it to Koyeb. This app will let users upload videos and also provide features to watch for these uploaded videos.
The focus of this article is the end-to-end process, so in this section we will only highlight the code relevant to this process. You can check the full source code in the project's GitHub repository.
Create a virtual environment and initialize a new project
To get started, create a project directory and then initialize a new virtual environment inside by typing:
mkdir example-video-app
cd example-video-app
python3 -m venv venv
Activate the new virtual environment by typing:
source venv/bin/activate
Install Django within the virtual environment. We'll also install some additional libraries and packages that we'll use in the application while we're here:
pip install Django requests python-decouple psycopg2-binary Pillow
Save the project's dependencies to a requirements.txt
file by typing:
pip freeze > requirements.txt
With Django installed, create a new Django project called VideoApp rooted in the existing project directory (be sure to include the trailing dot to avoid creating an extra directory hierarchy):
django-admin startproject VideoApp .
Next, create a new Django application called App that the project will incorporate:
python manage.py startapp App
This will create a new App
directory alongside the existing VideoApp
directory.
Set up the app models
In the App/models.py
file, you can now define the Django models for the video metadata. Replace the current contents with the following:
# File: App/models.py
from django.db import models
# Model to store video metadata
class Video(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
video_file = models.FileField(upload_to='videos/')
uploaded_at = models.DateTimeField(auto_now_add=True)
duration = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
size = models.CharField(max_length=10, null=True, blank=True)
def __str__(self):
return self.title
# Model to store video related tags
class Tag(models.Model):
name = models.CharField(max_length=50)
video = models.ManyToManyField(Video, related_name='tags')
def __str__(self):
return self.name
# Model to store video classification
class Category(models.Model):
name = models.CharField(max_length=50)
video = models.ManyToManyField(Video, related_name='categories')
def __str__(self):
return self.name
Here you are defining three database models: Video
, Tag
, and Category
.
The Video
model is used to store video metadata, including the title, description, video file, upload date and time, duration, and size.
The Tag
model is used to store video-related tags. It has a many-to-many relationship with the Video
model, meaning that a video can have multiple tags and a tag can be associated with multiple videos. The related_name
attribute specifies the name of the reverse relation from the Video
model back to the Tag
model.
The Category
model is used to store video classifications. It also has a many-to-many relationship with the Video
model, meaning that a video can belong to multiple categories and a category can contain multiple videos. The related_name
attribute specifies the name of the reverse relation from the Video
model back to the Category
model.
Run your services on the best serverless platform and enjoy the built-in CI/CD pipeline, global load balancing, real-time metrics and monitoring, autoscaling, and more.
Create a video upload form
In order to be able to upload any videos, you need to have a form defined in Django, let's now create that in App/forms.py
:
# File: App/forms.py
from django import forms
from .models import Video, Tag, Category
# Form for video upload
class VideoForm(forms.ModelForm):
class Meta:
model = Video
fields = ['title', 'description', 'video_file']
The VideoForm
is a ModelForm
, a special form created from a Django model. In this case, the Video
model is used.
The Meta
class inside VideoForm
is used to specify additional metadata for the form. The model
attribute indicates which Django model this form is associated with, and the fields
attribute is a list of model fields that should be included in the form. In this case, the form includes fields for title
, description
, video_file
.
The tags
and categories
, as well as duration
and size
will be filled in later on with the results from the worker service API.
This form will allow users to input data for these fields, which will then be saved as a new Video
object in the database when the form is submitted.
Configure the App views
With the form in place, next you can create the view that will handle the form submission. Replace the contents of App/views.py
with the following:
# File: App/views.py
import requests
from decouple import config
from django.shortcuts import render
from App.forms import VideoForm
from App.models import Tag, Category, Video
# View to handle video upload from the video upload form
def upload_video(request):
form = VideoForm()
if request.method == 'POST':
form = VideoForm(request.POST, request.FILES)
if form.is_valid():
# Save the video
form.save()
# Process the video file with the worker
video_url = config("DOMAIN") + form.instance.video_file.url
worker_url = config("WORKER_URL") + "/process_video?video_url=" + video_url
# Send a GET request to the worker URL
response = requests.get(worker_url)
if response.status_code == 200:
# Get the response data
response_data = response.json()
# Save the video tags
tags = response_data.get('tags')
for tag in tags:
tag, created = Tag.objects.get_or_create(name=tag)
form.instance.tags.add(tag)
# Save the video categories
categories = response_data.get('categories')
for category in categories:
category, created = Category.objects.get_or_create(name=category)
form.instance.categories.add(category)
# Save the video duration and resolution
form.instance.duration = response_data.get('duration')
form.instance.size = response_data.get('resolution')
form.instance.save()
# Return a success message
return render(request, 'upload_video.html',
{'form': form, 'message': 'Video uploaded successfully!'})
else:
# Return the form with errors
return render(request, 'upload_video.html', {'form': form,
'message': 'Error uploading video!'})
return render(request, 'upload_video.html', {'form': form})
def list_videos(request):
videos = Video.objects.all()
return render(request, 'list_videos.html', {'videos': videos})
The upload_video
view function handles the video upload process. When a user submits the video upload form, this function is called to process the form data. It starts by creating an instance of the VideoForm
with the form data and files. If the form is valid, it saves the video, processes the video file with a worker service, and saves the video metadata (tags, categories, duration, and resolution) returned by the worker service API. If the form is not valid, it returns the form with errors. If the request method is not POST, it simply renders the video upload form.
The list_videos
view function retrieves all videos from the database and renders them in a template.
Create the application templates
In order for this view to work, you will need to create the HTML template. Create a new directory for HTML templates:
mkdir App/templates
Inside, create a new file at App/templates/upload_video.html
with the following content:
<!-- File: App/templates/upload_video.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Video App</title>
</head>
<body>
<h1>Video App</h1>
<p><a href="{% url 'list_videos' %}">Back to Video list</a></p>
<h3>Upload your video</h3>
<form method="post" enctype="multipart/form-data">
{% csrf_token %} {{ form.as_p }}
<button type="submit">Upload</button>
</form>
{{ message }}
</body>
</html>
This HTML markup creates a simple web page for uploading videos.
The video upload form is created using the HTML <form>
tag. The method
attribute is set to post
, which means the form data will be sent to the server using the HTTP POST
method. The enctype
attribute is set to "multipart/form-data", which is necessary for forms that allow file uploads. The {% csrf_token %}
template tag is used to protect against cross-site request forgery attacks.
When the form is submitted, the data is sent to the server and handled by the upload_video
view function in the views.py
file. As mentioned earlier, if the form is valid, the video is saved and processed by the worker service, and a success message is displayed. If the form is not valid, an error message is displayed.
You will also need an HTML template to list the videos. Add the following to a App/templates/list_videos.html
file:
<!-- File: App/templates/list_videos.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Video App</title>
</head>
<body>
<h1>Video App</h1>
<p><a href="{% url 'upload_video' %}">Upload New Video</a></p>
{% for video in videos %}
<video controls width='50%' height='50%'>
<source src="{{ video.video_file.url }}" type="video/mp4"></source>
</video>
<h2>{{ video.title }}</h2>
<p>Description: {{ video.description }}</p>
<p>Tags:
{% for tag in video.tags.all %}
{{ tag.name }},
{% endfor %}
</p>
<p>Categories:
{% for category in video.categories.all %}
{{ category.name }},
{% endfor %}
</p>
<p>Duration: {{ video.duration }} seconds</p>
<p>Resolution: {{ video.size }}</p>
<p>-----</p>
{% endfor %}
</body>
</html>
The {% for video in videos %}
template tag is used to loop through the list of videos passed from the list_videos
view function in the views.py
file. For each video, it displays the video player, title, description, tags, categories, duration, and resolution.
The <video>
element embeds a video player in the web page. The controls
attribute is used to display the video controls, such as play, pause, and volume. The width
and height
attributes are used to set the size of the video player. The <source>
element is used to specify the video file and its MIME type.
The {{ video.title }}
, {{ video.description }}
, {{ video.duration }}
, and {{ video.size }}
template tags are used to display the video metadata.
The {% for tag in video.tags.all %}
and {% for category in video.categories.all %}
template tags are used to loop through the list of tags and categories associated with the video. The {{ tag.name }}
and {{ category.name }}
template tags are used to display the name of each tag and category.
When the web page is loaded, the list_videos
view function in the views.py
file is called to retrieve the list of videos from the database and pass it to the template. The template then loops through the list of videos and displays them on the web page.
Define the URL routing
To access the views and HTML templates, you need to define the URLs, which you can add to App/urls.py
:
# File: App/urls.py
from django.urls import path
from App import views
urlpatterns = [
path('', views.list_videos, name='list_videos'),
path('upload', views.upload_video, name='upload_video'),
]
The urlpatterns
list contains two path
objects that map URLs to view functions. The first path
object maps the root URL ('') to the list_videos
view function and assigns it the name 'list_videos'. This means that when a user navigates to the /
URL of the application, the list_videos
view function will be called to handle the request and render the appropriate response.
The second path
object maps the 'upload' URL to the upload_video
view function and assigns it the name 'upload_video'. This means that when a user navigates to the /upload
URL, the upload_video
view function will be called to handle the request and render the appropriate response.
Hook the new App/urls.py
file in to the project URL processing by editing the VideoApp/urls.py
file as follows:
# File: VideoApp/urls.py
from django.contrib import admin
from django.urls import path # [!code --]
from django.urls import include, path # [!code ++]
from django.conf import settings # [!code ++]
from django.conf.urls.static import static # [!code ++]
urlpatterns = [
path('', include("App.urls")), # [!code ++]
path('admin/', admin.site.urls),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # [!code ++]
Adjust the project configuration
Hook the application up with the project by editing the VideoApp/settings.py
file and adding the AppConfig
instance declared in the App/apps.py
file to the list of INSTALLED_APPS
:
# File: VideoApp/settings.py
. . .
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'App.apps.AppConfig', # [!code ++]
]
. . .
You also need to make sure that we connect the Django project to the PostgreSQL database defined earlier. Also in the VideoApp/settings.py
file, edit the DATABASES
dictionary to take parameterized values from environment variables:
# File: VideoApp/settings.py
. . .
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': config("DJANGO_DB_NAME"),
'USER': config("DJANGO_DB_USER"),
'PASSWORD': config("DJANGO_DB_PASSWORD"),
'HOST': config("DJANGO_DB_HOST"),
'OPTIONS': {'sslmode': 'require'},
}
}
. . .
We'll finish the configuration by setting some required variables and configuring static files. First, add some new imports to the top of the file:
# File: VideoApp/settigns.py
import os # [!code ++]
from pathlib import Path
from decouple import config # [!code ++]
. . .
Next, create or set the following variables:
# File: VideoApp/settings.py
. . .
SECRET_KEY = config("DJANGO_SECRET_KEY")
DEBUG = True
ALLOWED_HOSTS = config("ALLOWED_HOSTS", cast=lambda v: [s.strip() for s in v.split(',')])
DOMAIN = config("DOMAIN")
CSRF_TRUSTED_ORIGINS = config("CSRF_TRUSTED_ORIGINS", cast=lambda v: [s.strip() for s in v.split(',')])
. . .
The ALLOWED_HOSTS
variable is used to set a list of allowed host names for the application. This is a security feature that prevents host header attacks.
The DOMAIN
variable is used to set the domain name of the application. This is used in the upload_video
view function to construct the URL for the worker service.
The CSRF_TRUSTED_ORIGINS
variable is used to set a list of trusted origins for the Cross-Site Request Forgery (CSRF) protection in Django. This is a security feature that prevents malicious websites from making unauthorized requests to the application.
Finally, configure the static file configuration by adding the following:
# File: VideoApp/settings.py
. . .
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
. . .
Now, you can create and run the migrations, with:
python manage.py makemigrations
python manage.py migrate
The main page and the video upload form are now complete and should render if you run the test server with the expected environment variables configured. To actually upload videos, however, you need to create the associated worker service.
To finish up with the web app, create a new repository on GitHub. Afterwards, initialize a git repository in the project root, download a basic Python .gitignore
file, and push the changes:
git init
curl -L https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore -o .gitignore
echo "videos" >> .gitingore
git add :/
git commit -m "Initial commit"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_GITHUB_REPOSITORY>.git
git branch -M main
git push -u origin main
Build the worker service
You will build a second application with FastAPI that will be responsible for processing uploads.
Outside of the Django project directory, create a new project directory for the worker API service. Deactivate any existing virtual environments and create a new virtual environment:
deactivate # If you're not currently in a virtual environment, this command will fail. This is expected.
mkdir example-video-worker
cd example-video-worker
python3 -m venv venv
Activate the new virtual environment by typing:
source venv/bin/activate
Create a requirements.txt
file with the service's dependencies:
# File: requirements.txt
fastapi
assemblyai
python-decouple
requests
moviepy
uvicorn
Install the dependencies by typing:
pip install -r requirements.txt
The important packages installed here are assemblyai
, which is a speech recognition and natural language processing API, and moviepy
, which is a library for video editing and processing.
Create a .env
file to define your AssemblyAI API key as an environment variable:
# File: .env
ASSEMBLYAI_API_KEY=<YOUR_ASSEMBLYAI_API_KEY>
You will need an API key from AssemblyAI, which you can get [here](AssemblyAI | Dashboard). You will need to sign up for an account if you don't have one. To use the AI features (like LEMUR) you will need to add credits to the account.
Then you can create your main.py
file inside the FastAPI project:
# File: main.py
import requests
from decouple import config
from fastapi import FastAPI
import assemblyai as aai
from moviepy.video.io.VideoFileClip import VideoFileClip
import os
# Set up the AssemblyAI client
aai.settings.api_key = config("ASSEMBLYAI_API_KEY")
transcriber = aai.Transcriber()
# Define a function to download a video file from a URL
def download_video(url, filename):
# Send a GET request to the URL
response = requests.get(url, stream=True)
# Check if the request was successful
if response.status_code == 200:
# Open a local file in binary write mode
with open(filename, 'wb') as file:
# Write the content of the response to the file in chunks
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
# Define a function to extract audio from a video file
def extract_audio_from_video(video_file_path, output_audio_path):
# Load the video file
video = VideoFileClip(video_file_path)
# Extract the audio from the video
audio = video.audio
# Write the audio to a file
audio.write_audiofile(output_audio_path)
# Close the video file to free up resources
video.close()
# Define a function to get the resolution and duration of a video file
def get_resolution_and_duration_from_video(video_file_path):
# Load the video file
video = VideoFileClip(video_file_path)
# Get the resolution of the video
resolution = video.size
# Get the duration of the video
duration = video.duration
# Close the video file to free up resources
video.close()
return resolution, duration
# Create a FastAPI instance
app = FastAPI()
# Define a route handler for the default route, for health checks
@app.get("/")
async def version():
return {"version": "v0.1"}
# Define a route handler for the /process_video route
@app.get("/process_video")
async def process_video(video_url: str):
# Download the video file from the URL and save it locally
print("Downloading video...")
video_filename = "video.mp4"
download_video(video_url, video_filename)
# Get audio from video file with MoviePy
print("Extracting audio from video...")
audio_filename = "audio.mp3"
extract_audio_from_video(video_filename, audio_filename)
# Get resolution and duration of the video
print("Getting resolution and duration...")
resolution, duration = get_resolution_and_duration_from_video(video_filename)
# Format the resolution as a string
resolution = f"{resolution[0]}x{resolution[1]}"
# Transcribe the audio with AssemblyAI
print("Transcribing audio...")
transcript = transcriber.transcribe(audio_filename)
# Generate tags for the video
print("Generating tags...")
prompt_tags = ("Generate a list of tags (max 5) for this video."
"Return only the tags, separated by commas and nothing else.")
result = transcript.lemur.task(prompt_tags)
tags = result.response.replace("\n", " ").split(",")
# Trim the tags
tags = [tag.strip() for tag in tags]
# Limit the number of tags to 5
tags = tags[:5]
# Generate the categories for the video
print("Generating categories...")
prompt_categories = ("Generate a list of categories (max 3) for this video."
"Return only the categories, separated by commas and nothing else.")
result = transcript.lemur.task(prompt_categories)
categories = result.response.replace("\n", " ").split(",")
# Trim the categories
categories = [category.strip() for category in categories]
# Limit the number of categories to 3
categories = categories[:3]
# Delete the video and audio files
print("Cleaning up...")
os.remove(video_filename)
os.remove(audio_filename)
# Return the tags and categories
print("Processing complete!")
record = {"tags": tags, "categories": categories, "resolution": resolution, "duration": duration}
print(record)
return record
This script first sets up an API key for the AssemblyAI service, which is used for transcribing audio. It then defines several functions for downloading a video file from a URL, extracting audio from a video file, and getting the resolution and duration of a video file.
Afterwards, it creates a FastAPI instance and defines two route handlers. The first route handler is for the default route (/
) and simply returns the version number of the service.
The second route handler is for the /process_video
route and performs the following steps:
- Downloads the video file from the provided URL and saves it locally.
- Extracts audio from the video file using MoviePy.
- Gets the resolution and duration of the video file.
- Transcribes the audio using AssemblyAI.
- Generates tags and categories for the video using AssemblyAI's LEMUR model.
- Deletes the video and audio files from local storage.
- Returns the generated tags, categories, resolution, and duration as a JSON object.
The assemblyai
library is used for transcribing audio and generating tags and categories and the moviepy
library is used for extracting audio from a video file and getting the resolution and duration of a video file.
To finish up with the worker service, create a new repository on GitHub. Afterwards, initialize a git repository in the project root, download a basic Python .gitignore
file, and push the changes:
git init
curl -L https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore -o .gitignore
printf "%s\n" "*.mp4" "*.mp3" >> .gitignore
git add :/
git commit -m "Initial commit"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_GITHUB_REPOSITORY>.git
git branch -M main
git push -u origin main
Integrate with Koyeb's edge network
Integrating with Koyeb's edge network requires nothing more than deploying the applications to Koyeb. All CDN features are enabled by default and all services are part of the service mesh network and edge network.
Let's now see how you can deploy the applications to Koyeb to build the pipeline.
Deploy the web application
You can start by first deploying the web application. For that go the Koyeb control panel and click Create Web Service:
-
Select GitHub as your deployment method and select your GitHub project for the web application.
-
In the Builder section, override the Run command with
python manage.py runserver 0.0.0.0:8000
. -
In the App and Service names section, configure the App name. This will impact the environment variable values you define next.
-
In the Environment variables section, click Bulk edit and configure the following variables:
DJANGO_DB_HOST= DJANGO_DB_USER= DJANGO_DB_PASSWORD= DJANGO_DB_NAME= ALLOWED_HOSTS= CSRF_TRUSTED_ORIGINS= DJANGO_SECRET_KEY= DOMAIN= WORKER_URL=
Fill in the variables as follows:
DJANGO_DB_HOST
: The hostname of the PostgreSQL database.DJANGO_DB_USER
: The PostgreSQL username to authenticate with.DJANGO_DB_PASSWORD
: The PostgreSQL password to authenticate with.DJANGO_DB_NAME
: The name of the PostgreSQL database to connect to.ALLOWED_HOSTS
: The bare hostname where this application will be deployed. It will begin with your App name followed by your Koyeb org name, a hash, and end with.koyeb.app
.CSRF_TRUSTED_ORIGINS
: The domain where this application will be deployed. It will begin withhttps://
and include your App name, Koyeb org name, a hash, and end with.koyeb.app
.DOMAIN
: The domain where this application will be deployed. It will begin withhttps://
and include your App name, Koyeb org name, a hash, and end with.koyeb.app
.DJANGO_SECRET_KEY
: A secret key used for encryption by Django. You can follow the procedure in generate a secure Django secret key locally to generate a secure Django key.WORKER_URL
: The internal URL where your service worker will be deployed. This should take the following format:http://<WORKER_SERVICE_NAME>.<YOUR_KOYEB_ORG>.koyeb:8080
. Use the name you plan to deploy your service worker under.
-
Click Deploy.
After a couple of minutes the application should be deployed and accessible at the application's URL.
Deploy the worker service API
Next, deploy the worker API service. Navigate to the previous created application in the Koyeb control panel and click Create Service:
- Select GitHub as your deployment method and select your GitHub project for the worker service API.
- In the Builder section, override the Run command with
uvicorn main:app --port 8080 --host 0.0.0.0
. - In the Environment variables section, configure the following environment variable:
ASSEMBLYAI_API_KEY=<YOUR_ASSEMBLYAI_API_KEY>
. - In the Scaling section, select Autoscaling from 1 to 3 Instances. Set the number of requests per second to your desired threshold.
- In the Exposed ports section, deselect the Public toggle to make it only accessible from the service mesh and set the port to 8080.
- In the App and Service names section, set the Service name to the value you chose in the
WORKER_URL
variable when you deployed the Django application. - Click Deploy.
After a couple of minutes the Worker Web API should be deployed.
Enjoy automatic continuous deployment, global load balancing, real-time metrics and monitoring, autoscaling, and more when your services run on Koyeb.
Test the application
You can now test the web application and the worker API pipeline by accessing the web application URL and uploading a video file.
In this example, first we upload a file and fill in the title and description:
We can observe the different steps of the video worker API in the Koyeb logs:
And finally, returning to the web application, we can see the categories and tags as well as the additional information filled in:
You now have a full functional working video pipeline which can automatically scale when the number of requests for the video worker API crosses the threshold.
Conclusion
This article described how to build a video processing app using FastAPI, AssemblyAI, and Django on Koyeb. We've covered everything from setting up the app and creating the FastAPI service to implementing video processing features using AssemblyAI and MoviePy.
Throughout the article, we've seen how to build a reliable and scalable app that can process videos and extract valuable metadata, such as transcriptions, tags, and categories. You can customize and extend the app to meet your specific needs and use cases.
With the skills and knowledge you've gained from this article, you can confidently create your own video processing apps and use Koyeb to deploy innovative solutions for video processing and analysis. The demand for video content and video processing is growing rapidly, so the abilities you've learned here will be extremely valuable if you want to develop advanced video processing apps.