Git Product home page Git Product logo

django-video-encoding's Introduction

django-video-encoding

PyPI GitHub Workflow Status (master) Coveralls github branch PyPI - Python Version PyPI - License

django-video-encoding helps to convert your videos into different formats and resolutions.

Requirements

  • Python 3.6.1 or newer
  • ffmpeg and ffprobe

Installation

  1. Install django-video-encoding

    pip install django-video-encoding
  2. Add video_encoding to your INSTALLED_APPS.

Integration

Add a VideoField and a GenericRelation(Format) to your model. You can optionally store the width, height and duration of the video by supplying the corresponding field names to the VideoField.

from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from video_encoding.fields import VideoField
from video_encoding.models import Format


class Video(models.Model):
   width = models.PositiveIntegerField(editable=False, null=True)
   height = models.PositiveIntegerField(editable=False, null=True)
   duration = models.FloatField(editable=False, null=True)

   file = VideoField(width_field='width', height_field='height',
                     duration_field='duration')

   format_set = GenericRelation(Format)

To show all converted videos in the admin, you should add the FormatInline to your ModelAdmin

from django.contrib import admin
from video_encoding.admin import FormatInline

from .models import Video


@admin.register(Video)
class VideoAdmin(admin.ModelAdmin):
   inlines = (FormatInline,)

   list_dispaly = ('get_filename', 'width', 'height', 'duration')
   fields = ('file', 'width', 'height', 'duration')
   readonly_fields = fields

The conversion of the video should be done in a separate process. Typical options are django-rq or celery. We will use django-rq in the following example. The configuration for celery is similar. django-video-encoding already provides a task (convert_all_videos) for converting all videos on a model. This task should be triggered when a video was uploaded. Hence we listen to the post-save signal and enqueue the saved instance for processing.

# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_rq import enqueue

from video_encoding import tasks

from .models import Video


@receiver(post_save, sender=Video)
def convert_video(sender, instance, **kwargs):
    enqueue(tasks.convert_all_videos,
            instance._meta.app_label,
            instance._meta.model_name,
            instance.pk)

After a while You can access the converted videos using

video = Video.objects.get(...)
for format in video.format_set.complete().all():
   # do something

Generate a video thumbnail

The backend provides a get_thumbnail() method to extract a thumbnail from a video. Here is a basic example on how to generate the thumbnail and store it in the model.

# models.py
from django.db import models

class Video(models.Model):
   width = models.PositiveIntegerField(editable=False, null=True)
   height = models.PositiveIntegerField(editable=False, null=True)
   duration = models.FloatField(editable=False, null=True)

   thumbnail = ImageField(blank=True)
   file = VideoField(width_field='width', height_field='height',
                     duration_field='duration')

   format_set = GenericRelation(Format)


# tasks.py
from django.core.files import File
from video_encoding.backends import get_backend

from .models import Video


def create_thumbnail(video_pk):
   video = Video.objects.get(pk=video_pk)
   if not video.file:
      # no video file attached
      return

   if video.thumbnail:
      # thumbnail has already been generated
      return

   encoding_backend = get_backend()
   thumbnail_path = encoding_backend.get_thumbnail(video.file.path)
   filename = os.path.basename(self.url),

   try:
      with open(thumbnail_path, 'rb') as file_handler:
         django_file = File(file_handler)
         video.thumbnail.save(filename, django_file)
      video.save()
   finally:
      os.unlink(thumbnail_path)

You should run this method in a separate process by using django-rq, celery or similar) and enqueue execution from within a post_save signal.

# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_rq import enqueue

from . import tasks
from .models import Video


@receiver(post_save, sender=Video)
def create_thumbnail(sender, instance, **kwargs):
    enqueue(tasks.create_thumbnail, instance.pk)

Signals

During the encoding multiple signals are emitted to report the progress. You can register to the signals as described in the Django documentation.

This simple example demonstrates, on how to update the "video model" once the convertion is finished.

# apps.py
from django.apps import AppConfig


class MyAppConfig(AppConfig):
   # ...

    def ready(self) -> None:
      from . import signals  # register signals


# signals.py
from typing import Type

from django.dispatch import receiver
from video_encoding import signals

from myapp.models import Video


@receiver(signals.encoding_finished, sender=Video)
def mark_as_finished(sender: Type[Video], instance: Video) -> None:
   """
   Mark video as "convertion has been finished".
   """
   video.processed = True
   video.save(update_fields=['processed'])

signals.encoding_started

This is sent before the encoding starts.

Arguments
sender: Type[models.Model]: Model which contains the VideoField.
instance: models.Model): Instance of the model containing the VideoField.

signals.encoding_finished

Like encoding_started(), but sent after the file had been converted into all formats.

Arguments
sender: Type[models.Model]: Model which contains the VideoField.
instance: models.Model): Instance of the model containing the VideoField.

signals.format_started

This is sent before the video is converted to one of the configured formats.

Arguments
sender: Type[models.Model]: Model which contains the VideoField.
instance: models.Model): Instance of the model containing the VideoField.
format: Format: The format instance, which will reference the encoded video file.

signals.format_finished

Like format_finished, but sent after the video encoding process and includes whether the encoding was succesful or not.

Arguments
sender: Type[models.Model]: Model which contains the VideoField.
instance: models.Model): Instance of the model containing the VideoField.
format: Format: The format instance, which will reference the encoded video file.
result: ConversionResult: Instance of video_encoding.signals.ConversionResult and indicates whether the convertion FAILED, SUCCEEDED or was SKIPPED.

Configuration

VIDEO_ENCODING_THREADS (default: 1)
Defines how many threads should be used for encoding. This may not be supported by every backend.

VIDEO_ENCODING_BACKEND (default: 'video_encoding.backends.ffmpeg.FFmpegBackend')
Choose the backend for encoding. django-video-encoding only supports ffmpeg, but you can implement your own backend. Feel free to pulish your plugin and submit a pull request.

VIDEO_ENCODING_BACKEND_PARAMS (default: {})
If your backend requires some special configuration, you can specify them here as dict.

VIDEO_ENCODING_FORMATS (for defaults see video_encoding/config.py)
This dictionary defines all required encodings and has some resonable defaults. If you want to customize the formats, you have to specify name, extension and params for each format. For example

VIDEO_ENCODING_FORMATS = {
    'FFmpeg': [
        {
            'name': 'webm_sd',
            'extension': 'webm',
            'params': [
                '-b:v', '1000k', '-maxrate', '1000k', '-bufsize', '2000k',
                '-codec:v', 'libvpx', '-r', '30',
                '-vf', 'scale=-1:480', '-qmin', '10', '-qmax', '42',
                '-codec:a', 'libvorbis', '-b:a', '128k', '-f', 'webm',
           ],
        },
     ]

Encoding Backends

video_encoding.backends.ffmpeg.FFmpegBackend (default)

Backend for using ffmpeg and ffprobe to convert your videos.

Options

VIDEO_ENCODING_FFMPEG_PATH
Path to ffmpeg. If no path is provided, the backend uses which to locate it. VIDEO_ENCODING_FFPROBE_PATH
Path to ffprobe. If no path is provided, the backend uses which to locate it.

Custom Backend

You can implement a custom encoding backend. Create a new class which inherits from video_encoding.backends.base.BaseEncodingBackend. You must set the property name and implement the methods encode, get_media_info and get_thumbnail. For further details see the reference implementation: video_encoding.backends.ffmpeg.FFmpegBackend.

If you want to open source your backend, follow these steps.

  1. create a packages named django-video-encoding-BACKENDNAME

  2. publish your package to pypi

  3. Submit a pull requests with the following changes:

    • add the package to extra_requires
    • provide reasonable defaults for VIDEO_ENCODING_FORMATS

Development

This project uses poetry for packaging and managing all dependencies and pre-commit to run flake8, isort, mypy and black.

Additionally, pdbpp and better-exceptions are installed to provide a better debugging experience. To enable better-exceptions you have to run export BETTER_EXCEPTIONS=1 in your current session/terminal.

Clone this repository and run

poetry install
poetry run pre-commit install

to create a virtual enviroment containing all dependencies. Afterwards, You can run the test suite using

poetry run pytest

This repository follows the Conventional Commits style.

Cookiecutter template

This project was created using cruft and the cookiecutter-pyproject template. In order to update this repository to the latest template version run

cruft update

in the root of this repository.

django-video-encoding's People

Contributors

escaped avatar goranpavlovic avatar lifenautjoe avatar mabuelhagag avatar querschlag avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-video-encoding's Issues

Hi , I'm trying to use django-video-encoding, but it doesn't seem to work

I working on a project with Django GraphQL and Nextjs and trying to build a video course platform, and I want to use this library to change resolution of videos from 4k to 480p for example.

I've been following the example code, but now I don't have any fields in the Django Admin Dashboard to upload a video or something like that, is there any documentation about how to use this library in Django and send the data through API calls to the frontend. As I've mentioned I'm using Django GraphQL ( strawberry-graphql-django ) and Nextjs with Apollo Client.

I don't really understand how can I use the documentation in my case.

If you could give me some ideas or a place where I can find some documentation for this I'd highly appreciate it.

Thank you in advance!

Video thumbnail

Hi Alex,

Is there any way to have the library generate a video thumbnail or is this outside the scope of the package?

Cheers!

bandit insecure code analyzer issues

Hi!

Ran the security linter bandit ( https://pypi.org/project/bandit/ ) through this package and found 2 issues that might be worth looking into

Run started:2019-09-11 11:53:54.804266

Test results:
>> Issue: [B404:blacklist] Consider possible security implications associated with PIPE module.
   Severity: Low   Confidence: High
   Location: ./video_encoding/backends/ffmpeg.py:7
   More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
6	import tempfile
7	from subprocess import PIPE, Popen
8	
9	import six

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   Location: ./video_encoding/backends/ffmpeg.py:66
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b603_subprocess_without_shell_equals_true.html
65	            return Popen(
66	                cmds, shell=False,
67	                stdin=PIPE, stdout=PIPE, stderr=PIPE,
68	                close_fds=True,
69	            )
70	        except OSError as e:

--------------------------------------------------

Code scanned:
	Total lines of code: 22531
	Total lines skipped (#nosec): 1

Run metrics:
	Total issues (by severity):
		Undefined: 0.0
		Low: 2.0
		Medium: 0.0
		High: 0.0
	Total issues (by confidence):
		Undefined: 0.0
		Low: 0.0
		Medium: 0.0
		High: 2.0
Files skipped (0):
Exited with code 1

No default_validators

In admin video upload is not because you have taken ImageField instead of FieldFile

Primary key must now be explicit

In latest django versions a warning is emitted when models do not explicitly define a primary key.

(somus-api) ~/Documents/code/somus/somus-api(hotfix/video-encoding-update ✗) python manage.py runserver
2021-10-15 23:16:56,588 django.utils.autoreload INFO     Watching for file changes with StatReloader
Performing system checks...

System check identified some issues:

WARNINGS:
video_encoding.Format: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
	HINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.

I'll come back with a PR shortly.

Can't run dev environment

I'm trying to run tests for a feature I want to contribute to the project.

Followed the instructions

poetry develop

Followed by

poetry run test

Getting the following exception

~/Documents/code/open-source/django-video-encoding(master ✗) poetry run pytest
Traceback (most recent call last):
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/manager.py", line 267, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2290, in load
    self.require(*args, **kwargs)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2307, in require
    items = working_set.resolve(reqs, env, installer)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 858, in resolve
    raise VersionConflict(dist, req).with_context(dependent_req)
pkg_resources.ContextualVersionConflict: (setuptools 28.8.0 (/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages), Requirement.parse('setuptools>=30'), {'flake8'})

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/bin/pytest", line 11, in <module>
    sys.exit(main())
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/_pytest/config/__init__.py", line 58, in main
    config = _prepareconfig(args, plugins)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/_pytest/config/__init__.py", line 196, in _prepareconfig
    pluginmanager=pluginmanager, args=args
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/hooks.py", line 284, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/manager.py", line 67, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/manager.py", line 61, in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/_pytest/helpconfig.py", line 93, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/_pytest/config/__init__.py", line 675, in pytest_cmdline_parse
    self.parse(args)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/_pytest/config/__init__.py", line 845, in parse
    self._preparse(args, addopts=addopts)
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/_pytest/config/__init__.py", line 799, in _preparse
    self.pluginmanager.load_setuptools_entrypoints("pytest11")
  File "/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages/pluggy/manager.py", line 273, in load_setuptools_entrypoints
    message="Plugin %r could not be loaded: %s!" % (ep.name, e),
pluggy.manager.PluginValidationError: Plugin 'flake8' could not be loaded: (setuptools 28.8.0 (/Users/lifenautjoe/Library/Caches/pypoetry/virtualenvs/django-video-encoding-py3.6/lib/python3.6/site-packages), Requirement.parse('setuptools>=30'), {'flake8'})!

Video compression

Hi! Do you think it's possible to use this library for video compression purposes? I managed to run a celery script responsible for compressing videos using FFmpeg in my project. It works fine, but it looks like you guys covered many other aspects that I possibly didn't think of in my solution (like storages and S3). I wondered how I could accomplish a similar thing but using your library instead (maybe with some tweaks). Is it something that is already covered or needs to be implemented yet?

Do not upscale videos

Currently, all videos will be scaled to the requested resolution. This does not make sense when a video will be upscaled. We should change the source so that

  1. the video is not automatically upscaled and
  2. there is an option to explicitly turn upscaling on.

Support for multi-pass encoding

Many encoders provide multi-pass encoding for higher image quality and compression ration (eg. VP9). This is currently not supported and would be a nice addition for some use cases.

Signal to update field on Video model after conversion finished?

Hi, I was wondering if it's possible to know if a conversion already finished to update a model? Maybe with a post_signal or something like that.

I want to update a field on the Model where I put the VideoField, e.g, I add a boolean field on the model where I put the VideoField, and when the conversion finished, I want to change that boolean value.

New logo for django-video-encoding

Hello, I wanted to contribute to django-video-encoding. I designed a logo for it. If you like any of these, I'll send you files and pr.. I wanted to make a logo compatible with the content of the software. Especially if you have something you want, you can tell me.

django-video-encoding

Circular dependency with backends?

Currently the directions for creating a custom backend (which I am doing) say the following:

Create a new class which inherits from video_encoding.backends.base.BaseEncodingBackend. ...
If you want to open source your backend, follow these steps.

create a packages named django-video-encoding-BACKENDNAME

publish your package to pypi

Submit a pull requests with the following changes:

add the package to extra_requires
provide reasonable defaults for VIDEO_ENCODING_FORMATS

Won't this cause a circular dependency between this package and any additional backends? Wouldn't it be better to have the backend base (and probably subsequent backends) in a separate package?

Question regarding video encoding formats samples

Hi, this is very nice and useful project! I tried config.py's video formats and was curious what scenarios they are optimized for. On a 60MB mp4 file,the mp4 high version produced was more than 200MB and I found that strange

Cheers

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.