# MG_GPL_HEADER_BEGIN
#
# This file is part of Media Gallery, GNU GPLv2.
#
# Media Gallery is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# Media Gallery is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Media Gallery; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# MG_GPL_HEADER_END
__author__ = "Media Gallery contributors <mg@lists.ssji.net>"
__copyright__ = "(c) 2010"
__credits__ = ["Dimitri Refauvelet", "Nicolas Pouillon"]
from django.db import models
from mg.core.element.models import Element
__all__ = ['Video', 'VideoFormat', 'ResizedVideo']
BOTTOM_SIDE = (
('D', 'Default'),
('B', 'Bottom'),
('L', 'Left'),
('T', 'Top'),
('R', 'Right'),
)
class Video(Element):
[docs] width = models.IntegerField()
height = models.IntegerField()
ratio = models.FloatField(db_index = True)
dar = models.FloatField()
largest = models.IntegerField(db_index = True)
file = models.FileField(upload_to = "spool")
afmt = models.CharField(max_length = 16, blank = True)
vfmt = models.CharField(max_length = 16, blank = True)
cfmt = models.CharField(max_length = 16, blank = True)
bottom_side = models.CharField(db_index = True,
max_length = 1, choices = BOTTOM_SIDE,
default = "D")
key_time = models.IntegerField(null = True, blank = True)
def save(self, *args, **kwargs):
if self.pk is None:
self.width = self.height = self.ratio = self.dar = self.largest = 0
super(Video, self).save(*args, **kwargs)
self._rehash_size()
super(Video, self).save(*args, **kwargs)
def _rehash_size(self):
vh = self.get_handler()
try:
vs = vh.get_streams('video')[0]
except Exception, e:
return
self.width, self.height = vs.size
self.vfmt = vs.codec
self.cfmt = vh.container
self.dar = vs.dar
try:
as_ = vh.get_streams('audio')[0]
self.afmt = as_.codec
except:
pass
self.largest = max((self.width, self.height))
self.ratio = float(self.width) / self.height
def get_handler(self):
from mg.utils.video import VideoHandler
from mg.utils.file import local_file_from_storage
filename, fd = local_file_from_storage(self.file, "."+self.cfmt)
vh = VideoHandler(filename)
# Take a ref on the tmp file, make it live as long as the
# handler lives
vh.__filename = filename
vh.__fd = fd
return vh
@classmethod
def __get_resizer(cls):
from mg.utils.resizer import resizer
return resizer
def resized_get(self, **filt):
return self.resizedvideo_set.filter(**filt).order_by('size__width')[0]
def thumbnail_new(self, size):
from mg.core.element.models import Thumbnail
import tempfile
from django.core.files.base import File
r = self.__get_resizer()
r.set_output_size(size = size.wh, mode = r.PAN_SCAN)
# Get a temporary image size, taking DAR in account
if self.dar > 1:
w = size.height * self.dar
h = size.height
else:
w = size.width
h = size.width / self.dar
# First do video -> image (of native size)
vh = self.get_handler()
tmp = tempfile.NamedTemporaryFile(suffix = ".jpg", mode = "w+b")
vh.snapshot(tmp.name,
rot_count = self._get_rot(),
time_offset = self.key_time,
image_size = (w, h))
# Now use the image resizer with bbox fitting to thumbnail
out = tempfile.NamedTemporaryFile(suffix = ".jpg", mode = "w+b")
r.resize(out.name, tmp.name, rot_count = 0)
# Create the thumbnail with this
dst = Thumbnail(size = size, element = self)
dst.save()
# And of course save the payload
dst.file.save(dst.filename_gen(), File(out))
dst.save()
return dst
def __unicode__(self):
return u"%s"%(self.name)
def _get_rot(self):
"""
Returns the number of rotations of 90 degrees, clockwise, to
apply to image
"""
# If bottom side of scene is on right side (R) of picture, we
# have to rotate the image once clockwise, so R=1, others are
# trivial.
return "BRTLD".find(self.bottom_side) % 4
def rehash_all_resized(self):
from mg.core.element.models import ThumbnailSize
self.thumbnail_set.all().delete()
self.resizedvideo_set.all().delete()
for s in VideoFormat.objects.all():
ResizedVideo.forge(self, s)
for s in ThumbnailSize.objects.all():
self.thumbnail_new(s)
def info(self):
h = self.duration / 3600
m = (self.duration / 60) % 60
s = self.duration % 60
r = Element.info(self)
if r:
r += ", "
return r + '%02d:%02d:%02d' % (h, m, s)
Element.register_type("video", Video)
class VideoFormat(models.Model):
[docs] video = models.ForeignKey(Video)
size = models.ForeignKey(VideoFormat)
file = models.ImageField(upload_to = "resized")
bottom_side = models.CharField(db_index = True, max_length = 1, choices = BOTTOM_SIDE, default = "D")
def filename_gen(self):
[docs] base = u"%s_%s"%(self.video.name, self.size)
return self.file.storage.get_available_name(
self.file.storage.get_valid_name(base+"."+self.size.cfmt))
class Meta:
unique_together = (
('video', 'size'),
)
def __unicode__(self):
return u"<%s @%s>"%(self.video, self.size)
@classmethod
def forge(cls, source, format):
import tempfile
from django.core.files.base import File
# dar redizing is done before rotation. then we dont care
# about choosing between (w, w*dar) and (w, w/dar)
h = int((source.width / source.dar)+.5)
preset = format.preset((source.width, h))
vh = source.get_handler()
# Do video conversion
tmp = tempfile.NamedTemporaryFile(suffix = "."+preset['container'],
mode = "w+b")
vh.transcode(tmp.name,
preset['container'],
preset['vs_info'],
preset['as_info'],
source._get_rot(),
preset['add_flags'],
)
# Create the object with this
dst = cls(size = format, video = source, bottom_side = source.bottom_side)
dst.save()
# And of course save the payload
dst.file.save(dst.filename_gen(), File(tmp))
dst.save()
return dst