Minio and NATS based video converter (PoC) for Nextcloud

- nextcloud minio nats kubernetes video-conversion

Due to the current circumstances around COVID-19, my wife started to create more and more videos for her choirs for learning and greeting purposes.

For distribution, we use Nextcloud for file sharing. But some videos are really large. Streaming in different qualities is not possible and we don´t use youtube for data privacy reasons. So we had to convert the videos to a smaller, web optimized size to be (better) sharable. But her laptop is a bit older and even small videos take quite a long time to encode. And even with newer hardware a tool like Handbrake is nothing for my wife.

Before I started this proof of concept, we shared a directory between our two users. She placed a video there, I encoded it in 720p with my PC and placed the result again in this folder. We did this several time, every few days.

But this isn´t really a nice solution for the future. So wouldn´t it be great if she can make use of a folder where she can place videos for encoding? Then after some time the encoded result appears in this folder automatically.

nextcloud-integration

Idea

Video (mp4) files uploaded into a folder should be automatically converted into a smaller 720p video as a copy besides the original file.

Some requirements:

Rough design:

graph TD; nextcloud -- 1. mount external storage - put file --> minio-bucket subgraph "Minnat-converter" minio-bucket -- 2. notification --> nats; minnat-converter -- 3. listening --> nats; minio-bucket -- 4. download file --> minnat-converter; minnat-converter -- 5. ffmpeg encoding --> minnat-converter; minnat-converter -- 6. upload new video --> minio-bucket; end

Setup

Because this project starts as a proof of concept I use my small kubernetes cluster at home to run this solution. So the setup described here needs the following components to be available.

Install NATS

For nats on kubernetes it is recommended to use the nats-operator. I downloaded the operator yaml´s and modified them to point to the namespace minnat-converter.

# setup nats-operator https://github.com/nats-io/nats-operator
kubectl apply -f nats-operator/00-prereqs.yaml
kubectl apply -f nats-operator/10-deployment.yaml
kubectl apply -f nats-operator/default-rbac.yaml
kubectl apply -f nats-operator/deployment.yaml

# setup nats server
kubectl apply -f nats.yaml

For local tests against the nats cluster within kubernetes one could use the command line tools from nats. This would look like the following.

go get github.com/nats-io/go-nats-examples/tools/nats-sub
go get github.com/nats-io/go-nats-examples/tools/nats-pub
export PATH=$PATH:$HOME/go/bin/

kubectl port-forward nats-1 4222

nats-sub -t "minio.files.put"

Install minio

For minio I use the official helm chart for a proper installation. See minio.yaml for configuration options including ingress and nats configuration.

# create minio secret with access-key and secret-key pair
kubectl create secret generic minio-keys --from-literal=accesskey=foobarbaz --from-literal=secretkey=foobarbazqux

helm install minio stable/minio -f k8s-setup/minio.yaml

# add bucket
mc config host add minnat https://<domain> <access-key> <secret-key>
mc mb minnat/videos

# enable bucketnotifcations for mp4 videos
mc event add minnat/videos arn:minio:sqs:home:_:nats --suffix .mp4 --event put
mc event list

Minnat-converter application

The application is based on micronaut. It uses https://github.com/bramp/ffmpeg-cli-wrapper and ffmpeg itself for conversion. Of course I use the official minio and nats/nats-streaming java sdk´s.

Finally the application is packaged as docker image and deployed to kubernetes.

Deployment minnat-converter

See k8s folder for k8s deployment descriptors. For development and deployment I use skaffold. You can see skaffold.yaml for configuration. Basically it builds the docker image and deploys every yaml file in folder k8s.

./gradlew clean build
skaffold run

configuration options

Environment variable default value description
MINNAT_NATS_HOST ‘nats://nats:4222’ Url to nats cluster
MINNAT_NATS_CLIENTID ‘minnat-converter’ Nats clientId prefix
MINNAT_NATS_CLUSTERID ‘stan’ Nats streaming clusterId
MINNAT_NATS_SUBJECT ‘minio.files.put’ Nats subject to listen on events
MINNAT_NATS_QUEUE_NAME ‘minnat-converter’ Nats queue name
MINNAT_NATS_DURABLE_NAME ‘minnat-converter’ Nats durable name
MINNAT_MINIO_HOST http://minio:9000' Internal minio url
MINNAT_MINIO_ACCESS_KEY fetched from minio-keys secret, see [](./k8s/deployment.yaml)
MINNAT_MINIO_SECRET_KEY fetched from minio-keys secret, see [](./k8s/deployment.yaml)
MINNAT_FILTER_PATTERN ’.mp4’ Only files ending with pattern will be converted
MINNAT_CONVERTED_SUFFIX ’-720p’ Suffix added to converted files
MINNAT_WORKING_DIRECTORY ’/tmp’ Temp directory for video files

Conclusion and outlook

All code and configurations used above are available in the gitlab repo https://gitlab.com/cin/minnat-converter.

For a easter weekend site-project I have now a solution for video conversion and it works pretty good. Ok, there are no configuration options for the end user, error handling has to be improved and some more testing has to be done. But that is something for the next weekend :-)