rework files

This commit is contained in:
j 2011-08-23 19:39:34 +02:00
parent a6e120322f
commit 50adcb66fc
9 changed files with 138 additions and 98 deletions

View file

@ -9,7 +9,7 @@ import models
class FileAdmin(admin.ModelAdmin): class FileAdmin(admin.ModelAdmin):
search_fields = ['name', 'folder','oshash', 'video_codec'] search_fields = ['name', 'folder','oshash', 'video_codec']
list_display = ['available', 'is_main', '__unicode__', 'itemId'] list_display = ['available', 'wanted', 'active', '__unicode__', 'itemId']
list_display_links = ('__unicode__', ) list_display_links = ('__unicode__', )
def itemId(self, obj): def itemId(self, obj):

View file

@ -30,7 +30,7 @@ class File(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
verified = models.BooleanField(default=False) active = models.BooleanField(default=False)
auto = models.BooleanField(default=True) auto = models.BooleanField(default=True)
oshash = models.CharField(max_length=16, unique=True) oshash = models.CharField(max_length=16, unique=True)
@ -69,34 +69,17 @@ class File(models.Model):
#This is true if derivative is available or subtitles where uploaded #This is true if derivative is available or subtitles where uploaded
available = models.BooleanField(default = False) available = models.BooleanField(default = False)
wanted = models.BooleanField(default = False)
uploading = models.BooleanField(default = False)
is_audio = models.BooleanField(default=False) is_audio = models.BooleanField(default=False)
is_video = models.BooleanField(default=False) is_video = models.BooleanField(default=False)
is_extra = models.BooleanField(default=False)
is_main = models.BooleanField(default=False)
is_subtitle = models.BooleanField(default=False) is_subtitle = models.BooleanField(default=False)
is_version = models.BooleanField(default=False)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
def set_state(self): def set_state(self):
instance = self.get_instance()
if instance:
if instance.name.lower().startswith('extras/'):
self.is_extra = True
self.is_main = False
elif instance.name.lower().startswith('versions/'):
self.is_version = True
self.is_main = False
else:
self.is_extra = False
self.is_main = True
else:
self.is_main = False
self.is_extra = False
self.is_version = False
self.name = self.get_name() self.name = self.get_name()
self.folder = self.get_folder() self.folder = self.get_folder()
self.sort_name = utils.sort_string(canonicalTitle(self.name)) self.sort_name = utils.sort_string(canonicalTitle(self.name))
@ -284,8 +267,8 @@ class File(models.Model):
'available': self.available, 'available': self.available,
'duration': duration, 'duration': duration,
'framerate': self.framerate, 'framerate': self.framerate,
'height': self.height, #'height': self.height,
'width': self.width, #'width': self.width,
'resolution': resolution, 'resolution': resolution,
'id': self.oshash, 'id': self.oshash,
'samplerate': self.samplerate, 'samplerate': self.samplerate,
@ -293,12 +276,12 @@ class File(models.Model):
'audio_codec': self.audio_codec, 'audio_codec': self.audio_codec,
'name': self.name, 'name': self.name,
'size': self.size, 'size': self.size,
'info': self.info, #'info': self.info,
'users': list(set([i.volume.user.username for i in self.instances.all()])), 'users': list(set([u.username
for u in User.objects.filter(volumes__files__in=self.instances.all())])),
'instances': [i.json() for i in self.instances.all()], 'instances': [i.json() for i in self.instances.all()],
'folder': self.get_folder(), 'folder': self.get_folder(),
'type': self.get_type(), 'type': self.get_type(),
'is_main': self.is_main,
'part': self.get_part() 'part': self.get_part()
} }
if keys: if keys:
@ -314,12 +297,12 @@ class File(models.Model):
if self.language: if self.language:
name = name[-(len(self.language)+1)] name = name[-(len(self.language)+1)]
qs = self.item.files.filter(Q(is_video=True)|Q(is_audio=True), qs = self.item.files.filter(Q(is_video=True)|Q(is_audio=True),
is_main=True, name__startswith=name) active=True, name__startswith=name)
if qs.count()>0: if qs.count()>0:
return qs[0].part return qs[0].part
if not self.is_extra: if self.active:
files = list(self.item.files.filter(type=self.type, language=self.language, files = list(self.item.files.filter(type=self.type, language=self.language,
is_main=self.is_main).order_by('sort_name')) active=self.active).order_by('sort_name'))
if self in files: if self in files:
return files.index(self) + 1 return files.index(self) + 1
return None return None
@ -412,6 +395,7 @@ class Instance(models.Model):
name = models.CharField(max_length=2048) name = models.CharField(max_length=2048)
folder = models.CharField(max_length=2048) folder = models.CharField(max_length=2048)
extra = models.BooleanField(default=False)
file = models.ForeignKey(File, related_name='instances') file = models.ForeignKey(File, related_name='instances')
volume = models.ForeignKey(Volume, related_name='files') volume = models.ForeignKey(Volume, related_name='files')

View file

@ -66,6 +66,9 @@ def update_or_create_instance(volume, f):
instance.file = get_or_create_file(volume, f, volume.user, item) instance.file = get_or_create_file(volume, f, volume.user, item)
for key in _INSTANCE_KEYS: for key in _INSTANCE_KEYS:
setattr(instance, key, f[key]) setattr(instance, key, f[key])
if instance.name.lower().startswith('extras/') or \
instance.name.lower().startswith('versions/'):
instance.extra = True
instance.save() instance.save()
instance.file.save() instance.file.save()
return instance return instance

View file

@ -98,11 +98,12 @@ def update(request):
if volume: if volume:
files = files.filter(volume=volume) files = files.filter(volume=volume)
response['data']['info'] = [f.file.oshash for f in files.filter(file__info='{}')] response['data']['info'] = [f.file.oshash for f in files.filter(file__info='{}')]
#needs some flag to find those that are actually used main is to generic
response['data']['data'] = [f.file.oshash for f in files.filter(file__is_video=True, response['data']['data'] = [f.file.oshash for f in files.filter(file__is_video=True,
file__is_main=True)] file__available=False,
file__wanted=True)]
response['data']['data'] += [f.file.oshash for f in files.filter(file__is_audio=True, response['data']['data'] += [f.file.oshash for f in files.filter(file__is_audio=True,
file__is_main=True)] file__available=False,
file__wanted=True)]
response['data']['file'] = [f.file.oshash for f in files.filter(file__is_subtitle=True, response['data']['file'] = [f.file.oshash for f in files.filter(file__is_subtitle=True,
name__endswith='.srt')] name__endswith='.srt')]
@ -188,6 +189,7 @@ def firefogg_upload(request):
response['result'] = -1 response['result'] = -1
elif form.cleaned_data['done']: elif form.cleaned_data['done']:
f.available = True f.available = True
f.uploading = False
f.save() f.save()
#FIXME: this fails badly if rabbitmq goes down #FIXME: this fails badly if rabbitmq goes down
try: try:
@ -205,6 +207,7 @@ def firefogg_upload(request):
if f.editable(request.user): if f.editable(request.user):
f.streams.all().delete() f.streams.all().delete()
f.available = False f.available = False
f.uploading = True
f.save() f.save()
response = { response = {
#is it possible to no hardcode url here? #is it possible to no hardcode url here?

View file

@ -34,6 +34,8 @@ from .timelines import join_timelines
from archive import extract from archive import extract
from annotation.models import Annotation, Layer from annotation.models import Annotation, Layer
import archive.models
from person.models import get_name_sort from person.models import get_name_sort
from app.models import site_config from app.models import site_config
@ -340,11 +342,11 @@ class Item(models.Model):
posters = [] posters = []
poster = self.path('poster.local.jpg') poster = self.path('siteposter.jpg')
poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster)) poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
if os.path.exists(poster): if os.path.exists(poster):
posters.append({ posters.append({
'url': '/%s/poster.jpg' % self.itemId, 'url': '/%s/siteposter.jpg' % self.itemId,
'width': 640, 'width': 640,
'height': 1024, 'height': 1024,
'source': settings.URL, 'source': settings.URL,
@ -614,18 +616,17 @@ class Item(models.Model):
s.wordsperminute = 0 s.wordsperminute = 0
s.clips = 0 #FIXME: get clips from all layers or something s.clips = 0 #FIXME: get clips from all layers or something
s.popularity = 0 #FIXME: get popularity from somewhere s.popularity = 0 #FIXME: get popularity from somewhere
videos = self.main_videos() videos = self.files.filter(active=True, is_video=True)
if len(videos) > 0: if videos.count() > 0:
s.duration = sum([v.duration for v in videos]) s.duration = sum([v.duration for v in videos])
s.resolution = videos[0].width * videos[0].height v = videos[0]
s.resolution = v.width * v.height
s.aspectratio = float(utils.parse_decimal(v.display_aspect_ratio)) s.aspectratio = float(utils.parse_decimal(v.display_aspect_ratio))
#FIXME: should be average over all files
if 'bitrate' in videos[0].info:
s.bitrate = videos[0].info['bitrate']
s.pixels = sum([v.pixels for v in videos]) s.pixels = sum([v.pixels for v in videos])
s.numberoffiles = self.files.all().count() s.numberoffiles = self.files.all().count()
s.parts = len(videos) s.parts = videos.count()
s.size = sum([v.size for v in videos]) #FIXME: only size of movies? s.size = sum([v.size for v in videos]) #FIXME: only size of movies?
s.bitrate = s.size * 8 / s.duration
s.volume = 0 s.volume = 0
else: else:
s.duration = None s.duration = None
@ -707,38 +708,62 @@ class Item(models.Model):
@property @property
def timeline_prefix(self): def timeline_prefix(self):
videos = self.main_videos() videos = self.streams()
if len(videos) == 1: if len(videos) == 1:
return os.path.join(settings.MEDIA_ROOT, videos[0].path('timeline')) return os.path.join(settings.MEDIA_ROOT, videos[0].path('timeline'))
return os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline') return os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline')
def main_videos(self): def get_files(self, user):
#FIXME: needs to check if more than one user has main files and only #FIXME: limit by user
# take from "higher" user return [f.json() for f in self.files.all()]
videos = self.files.filter(is_main=True, is_video=True, available=True, instances__gt=0).order_by('part')
if videos.count()>0: def users_with_files(self):
first = videos[0] return User.objects.filter(volumes__files__file__item=self).distinct()
user = first.instances.all()[0].volume.user
#only take videos from same user and with same width/height def update_wanted(self):
def check(v): users = self.users_with_files()
if v.instances.filter(volume__user=user).count()>0 and \ if users.filter(is_superuser=True).count()>0:
first.width == v.width and first.height == v.height: files = self.files.filter(instances__volume__user__is_superuser=True)
return True users = User.objects.filter(volumes__files__file__item__in=files,
return False is_superuser=True).distinct()
videos = filter(check, videos) elif users.filter(is_staff=True).count()>0:
files = self.files.filter(instances__volume__user__is_staff=True)
users = User.objects.filter(volumes__files__file__item__in=files,
is_staff=True).distinct()
else: else:
audio = self.files.filter(is_main=True, is_audio=True, available=True) files = self.files.all()
if audio.count()>0: files = files.filter(is_video=True, instances__extra=False, instances__gt=0).order_by('part')
first = audio[0] folders = list(set([f.folder for f in files]))
user = first.instances.all()[0].volume.user if len(folders) > 1:
#only take videos from same user and with same width/height files = files.filter(folder=folders[0])
def check(v): files.update(wanted=True)
if v.instances.filter(volume__user=user).count()>0:
return True def update_selected(self):
return False files = archive.models.File.objects.filter(item=self,
videos = filter(check, audio) streams__available=True,
streams__source=None)
return videos def get_level(users):
if users.filter(is_superuser=True).count() > 0: level = 0
elif users.filter(is_staff=True).count() > 0: level = 1
else: level = 2
return level
users = User.objects.filter(volumes__files__file__in=self.files.filter(active=True)).distinct()
current_level = get_level(users)
users = User.objects.filter(volumes__files__file__in=files).distinct()
possible_level = get_level(users)
if possible_level < current_level:
files = self.files.filter(instances__volume__user__in=users).order_by('part')
#FIXME: this should be instance folders
folders = list(set([f.folder
for f in files.filter(is_video=True, instances__extra=False)]))
files = files.filter(folder__startswith=folders[0])
files.update(active=True)
self.rendered = False
self.save()
self.update_timeline()
def make_torrent(self): def make_torrent(self):
base = self.path('torrent') base = self.path('torrent')
@ -751,26 +776,25 @@ class Item(models.Model):
base = os.path.abspath(os.path.join(settings.MEDIA_ROOT, base)) base = os.path.abspath(os.path.join(settings.MEDIA_ROOT, base))
size = 0 size = 0
duration = 0.0 duration = 0.0
if len(self.main_videos()) == 1: streams = self.streams()
if streams.count() == 1:
url = "%s/torrent/%s.webm" % (self.get_absolute_url(), url = "%s/torrent/%s.webm" % (self.get_absolute_url(),
quote(self.get('title').encode('utf-8'))) quote(self.get('title').encode('utf-8')))
video = "%s.webm" % base video = "%s.webm" % base
v = self.main_videos()[0] v = streams[0]
os.symlink(v.video.path, video) os.symlink(v.video.path, video)
info = ox.avinfo(video) size = v.video.size
size = info.get('size', 0) duration = v.duration
duration = info.get('duration', 0.0)
else: else:
url = "%s/torrent/" % self.get_absolute_url() url = "%s/torrent/" % self.get_absolute_url()
part = 1 part = 1
os.makedirs(base) os.makedirs(base)
for v in self.main_videos(): for v in streams:
video = "%s/%s.Part %d.webm" % (base, self.get('title'), part) video = "%s/%s.Part %d.webm" % (base, self.get('title'), part)
part += 1 part += 1
os.symlink(v.video.path, video) os.symlink(v.video.path, video)
info = ox.avinfo(video) size += v.video.size
size += info.get('size', 0) duration += v.duration
duration += info.get('duration', 0.0)
video = base video = base
torrent = '%s.torrent' % base torrent = '%s.torrent' % base
@ -794,7 +818,7 @@ class Item(models.Model):
def streams(self): def streams(self):
return [video.streams.filter(source=None, available=True)[0] return [video.streams.filter(source=None, available=True)[0]
for video in self.main_videos()] for video in self.files.filter(is_video=True, active=True)]
def update_timeline(self, force=False): def update_timeline(self, force=False):
config = site_config() config = site_config()
@ -846,8 +870,7 @@ class Item(models.Model):
else: else:
poster= self.path('poster.jpg') poster= self.path('poster.jpg')
path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster)) path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
posters = glob(path.replace('.jpg', '*.jpg')) for f in glob(path.replace('.jpg', '*.jpg')):
for f in filter(lambda p: not p.endswith('poster.local.jpg'), posters):
os.unlink(f) os.unlink(f)
def prefered_poster_url(self): def prefered_poster_url(self):
@ -883,7 +906,7 @@ class Item(models.Model):
self.poster.save('poster.jpg', ContentFile(f.read())) self.poster.save('poster.jpg', ContentFile(f.read()))
def make_local_poster(self): def make_local_poster(self):
poster = self.path('poster.local.jpg') poster = self.path('siteposter.jpg')
poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster)) poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
frame = self.get_poster_frame_path() frame = self.get_poster_frame_path()
@ -914,12 +937,14 @@ class Item(models.Model):
ox.makedirs(os.path.join(settings.MEDIA_ROOT,self.path())) ox.makedirs(os.path.join(settings.MEDIA_ROOT,self.path()))
p = subprocess.Popen(cmd) p = subprocess.Popen(cmd)
p.wait() p.wait()
for f in glob(poster.replace('.jpg', '*.jpg')):
os.unlink(f)
return poster return poster
def poster_frames(self): def poster_frames(self):
frames = [] frames = []
offset = 0 offset = 0
for f in self.main_videos(): for f in self.files(active=True, is_video=True):
for ff in f.frames.all(): for ff in f.frames.all():
frames.append({ frames.append({
'position': offset + ff.position, 'position': offset + ff.position,
@ -976,7 +1001,7 @@ class Item(models.Model):
Annotation.objects.filter(layer=layer,item=self).delete() Annotation.objects.filter(layer=layer,item=self).delete()
offset = 0 offset = 0
language = '' language = ''
languages = [f.language for f in self.files.filter(is_main=True, is_subtitle=True, languages = [f.language for f in self.files.filter(active=True, is_subtitle=True,
available=True)] available=True)]
if languages: if languages:
if 'en' in languages: if 'en' in languages:
@ -985,7 +1010,7 @@ class Item(models.Model):
language = '' language = ''
else: else:
language = languages[0] language = languages[0]
for f in self.files.filter(is_main=True, is_subtitle=True, for f in self.files.filter(active=True, is_subtitle=True,
available=True, language=language).order_by('part'): available=True, language=language).order_by('part'):
user = f.instances.all()[0].volume.user user = f.instances.all()[0].volume.user
for data in f.srt(offset): for data in f.srt(offset):
@ -999,7 +1024,7 @@ class Item(models.Model):
) )
annotation.save() annotation.save()
duration = self.files.filter(Q(is_audio=True)|Q(is_video=True)) \ duration = self.files.filter(Q(is_audio=True)|Q(is_video=True)) \
.filter(is_main=True, available=True, part=f.part) .filter(active=True, available=True, part=f.part)
if duration: if duration:
duration = duration[0].duration duration = duration[0].duration
else: else:

View file

@ -23,7 +23,8 @@ urlpatterns = patterns("item.views",
#poster #poster
(r'^(?P<id>[A-Z0-9].+)/poster(?P<size>\d+)\.jpg$', 'poster'), (r'^(?P<id>[A-Z0-9].+)/poster(?P<size>\d+)\.jpg$', 'poster'),
(r'^(?P<id>[A-Z0-9].+)/poster\.jpg$', 'poster_local'), (r'^(?P<id>[A-Z0-9].+)/siteposter(?P<size>\d+)\.jpg$', 'siteposter'),
(r'^(?P<id>[A-Z0-9].+)/poster\.jpg$', 'siteposter'),
(r'^(?P<id>[A-Z0-9].+)/frameposter(?P<position>\d+).jpg$', 'poster_frame'), (r'^(?P<id>[A-Z0-9].+)/frameposter(?P<position>\d+).jpg$', 'poster_frame'),

View file

@ -5,6 +5,7 @@ import os.path
from datetime import datetime, timedelta from datetime import datetime, timedelta
import mimetypes import mimetypes
import Image
from django.db.models import Count, Sum, Max from django.db.models import Count, Sum, Max
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
@ -343,6 +344,8 @@ def get(request):
info['stream'] = item.get_stream() info['stream'] = item.get_stream()
if not data['keys'] or 'layers' in data['keys']: if not data['keys'] or 'layers' in data['keys']:
info['layers'] = item.get_layers(request.user) info['layers'] = item.get_layers(request.user)
if data['keys'] and 'files' in data['keys']:
info['files'] = item.get_files(request.user)
response['data'] = info response['data'] = info
else: else:
response = json_response(status=403, text='permission denied') response = json_response(status=403, text='permission denied')
@ -582,7 +585,7 @@ def poster_frame(request, id, position):
raise Http404 raise Http404
def image_to_response(item, image, size=None): def image_to_response(image, size=None):
if size: if size:
size = int(size) size = int(size)
path = image.path.replace('.jpg', '.%d.jpg'%size) path = image.path.replace('.jpg', '.%d.jpg'%size)
@ -596,10 +599,17 @@ def image_to_response(item, image, size=None):
path = image.path path = image.path
return HttpFileResponse(path, content_type='image/jpeg') return HttpFileResponse(path, content_type='image/jpeg')
def poster_local(request, id): def siteposter(request, id, size=None):
item = get_object_or_404(models.Item, itemId=id) item = get_object_or_404(models.Item, itemId=id)
poster = item.path('poster.local.jpg') poster = item.path('siteposter.jpg')
poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster)) poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
if size:
image = Image.open(poster)
image_size = max(image.size)
if size < image_size:
path = poster.replace('.jpg', '.%d.jpg'%size)
extract.resize_image(poster, path, size=size)
poster = path
return HttpFileResponse(poster, content_type='image/jpeg') return HttpFileResponse(poster, content_type='image/jpeg')
def poster(request, id, size=None): def poster(request, id, size=None):

View file

@ -81,6 +81,13 @@ class UserProfile(models.Model):
del ui['lists'][i] del ui['lists'][i]
return ui return ui
def get_level(self):
if self.user.is_superuser:
return 'admin'
elif self.user.is_staff:
return 'staff'
return 'member'
def user_post_save(sender, instance, **kwargs): def user_post_save(sender, instance, **kwargs):
profile, new = UserProfile.objects.get_or_create(user=instance) profile, new = UserProfile.objects.get_or_create(user=instance)
@ -92,12 +99,7 @@ def get_user_json(user):
result = {} result = {}
for key in ('username', ): for key in ('username', ):
result[key] = getattr(user, key) result[key] = getattr(user, key)
if user.is_superuser: result['level'] = profile.get_level()
result['level'] = 'admin'
elif user.is_staff:
result['level'] = 'staff'
else:
result['level'] = 'member'
result['groups'] = [g.name for g in user.groups.all()] result['groups'] = [g.name for g in user.groups.all()]
result['preferences'] = profile.get_preferences() result['preferences'] = profile.get_preferences()
result['ui'] = profile.get_ui() result['ui'] = profile.get_ui()

View file

@ -309,22 +309,34 @@ def findUser(request):
param data { param data {
key: "username", key: "username",
value: "foo", operator: "=" value: "foo", operator: "="
keys: []
} }
return { return {
'status': {'code': int, 'text': string} 'status': {'code': int, 'text': string}
'data': { 'data': {
users = ['user1', 'user2'] users = [{username: 'user1', level: ...}, {username: 'user2', ..}]
} }
} }
''' '''
#FIXME: support other operators and keys #FIXME: support other operators and keys
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
response = json_response(status=200, text='ok') response = json_response(status=200, text='ok')
keys = data.get('keys')
if not keys:
keys = ['username', 'level']
def user_json(user, keys):
return {
'usernname': user.username,
'level': user.get_profile().get_level()
}
if data['key'] == 'email': if data['key'] == 'email':
response['data']['users'] = [u.username for u in User.objects.filter(email__iexact=data['value'])] response['data']['users'] = [user_json(u, keys)
for u in User.objects.filter(email__iexact=data['value'])]
else: else:
response['data']['users'] = [u.username for u in User.objects.filter(username__iexact=data['value'])] response['data']['users'] = [user_json(u, keys)
for u in User.objects.filter(username__iexact=data['value'])]
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findUser) actions.register(findUser)