forked from 0x2620/pandora
- rework file/upload handling, no more extras
- cleanup imdb data, get from external data service - rename filesview
This commit is contained in:
parent
3bff9d1fb9
commit
45798810a9
18 changed files with 295 additions and 631 deletions
|
|
@ -8,8 +8,8 @@ import models
|
|||
|
||||
|
||||
class FileAdmin(admin.ModelAdmin):
|
||||
search_fields = ['name', 'folder','oshash', 'video_codec']
|
||||
list_display = ['available', 'wanted', 'active', '__unicode__', 'itemId']
|
||||
search_fields = ['path','oshash', 'video_codec']
|
||||
list_display = ['available', 'wanted', 'selected', '__unicode__', 'itemId']
|
||||
list_display_links = ('__unicode__', )
|
||||
|
||||
def itemId(self, obj):
|
||||
|
|
@ -21,7 +21,7 @@ admin.site.register(models.File, FileAdmin)
|
|||
|
||||
|
||||
class InstanceAdmin(admin.ModelAdmin):
|
||||
search_fields = ['name', 'folder', 'volume__name', 'file__oshash']
|
||||
search_fields = ['path', 'volume__name', 'file__oshash']
|
||||
form = InstanceAdminForm
|
||||
|
||||
admin.site.register(models.Instance, InstanceAdmin)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class FileAdminForm(forms.ModelForm):
|
|||
|
||||
|
||||
class InstanceAdminForm(forms.ModelForm):
|
||||
file = ForeignKeyByLetter(models.File, field_name='name')
|
||||
file = ForeignKeyByLetter(models.File, field_name='path')
|
||||
|
||||
class Meta:
|
||||
model = models.Instance
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import os.path
|
|||
import re
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from django.db.models.signals import pre_delete
|
||||
|
||||
from ox.django import fields
|
||||
|
|
@ -17,7 +17,6 @@ import ox
|
|||
import chardet
|
||||
|
||||
from item import utils
|
||||
from person.models import get_name_sort
|
||||
|
||||
import extract
|
||||
|
||||
|
|
@ -26,19 +25,17 @@ class File(models.Model):
|
|||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
|
||||
active = models.BooleanField(default=False)
|
||||
auto = models.BooleanField(default=True)
|
||||
|
||||
oshash = models.CharField(max_length=16, unique=True)
|
||||
item = models.ForeignKey("item.Item", related_name='files')
|
||||
|
||||
name = models.CharField(max_length=2048, default="") # canoncial path/file
|
||||
folder = models.CharField(max_length=2048, default="") # canoncial path/file
|
||||
sort_name = models.CharField(max_length=2048, default="") # sort name
|
||||
path = models.CharField(max_length=2048, default="") # canoncial path/file
|
||||
sort_path = models.CharField(max_length=2048, default="") # sort name
|
||||
|
||||
type = models.CharField(default="", max_length=255)
|
||||
part = models.IntegerField(null=True)
|
||||
version = models.CharField(default="", max_length=255) # sort path/file name
|
||||
version = models.CharField(default="", max_length=255)
|
||||
language = models.CharField(default="", max_length=8)
|
||||
|
||||
season = models.IntegerField(default=-1)
|
||||
|
|
@ -65,22 +62,22 @@ class File(models.Model):
|
|||
|
||||
#This is true if derivative is available or subtitles where uploaded
|
||||
available = models.BooleanField(default = False)
|
||||
wanted = models.BooleanField(default = False)
|
||||
selected = models.BooleanField(default = False)
|
||||
uploading = models.BooleanField(default = False)
|
||||
wanted = models.BooleanField(default = False)
|
||||
|
||||
is_audio = models.BooleanField(default=False)
|
||||
is_video = models.BooleanField(default=False)
|
||||
is_subtitle = models.BooleanField(default=False)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
return self.path
|
||||
|
||||
def set_state(self):
|
||||
self.name = self.get_name()
|
||||
self.folder = self.get_folder()
|
||||
self.sort_name = utils.sort_string(ox.get_sort_title(self.name))
|
||||
self.path = self.create_path()
|
||||
self.sort_path= utils.sort_string(self.path)
|
||||
|
||||
if not os.path.splitext(self.name)[-1] in (
|
||||
if not os.path.splitext(self.path)[-1] in (
|
||||
'.srt', '.rar', '.sub', '.idx', '.txt', '.jpg', '.png', '.nfo') \
|
||||
and self.info:
|
||||
for key in ('duration', 'size'):
|
||||
|
|
@ -99,8 +96,8 @@ class File(models.Model):
|
|||
self.display_aspect_ratio = "%s:%s" % (self.width, self.height)
|
||||
self.is_video = True
|
||||
self.is_audio = False
|
||||
if self.name.endswith('.jpg') or \
|
||||
self.name.endswith('.png') or \
|
||||
if self.path.endswith('.jpg') or \
|
||||
self.path.endswith('.png') or \
|
||||
self.duration == 0.04:
|
||||
self.is_video = False
|
||||
else:
|
||||
|
|
@ -126,11 +123,11 @@ class File(models.Model):
|
|||
self.pixels = int(self.width * self.height * float(utils.parse_decimal(self.framerate)) * self.duration)
|
||||
|
||||
else:
|
||||
self.is_video = os.path.splitext(self.name)[-1] in ('.avi', '.mkv', '.dv', '.ogv', '.mpeg', '.mov', '.webm')
|
||||
self.is_audio = os.path.splitext(self.name)[-1] in ('.mp3', '.wav', '.ogg', '.flac', '.oga')
|
||||
self.is_subtitle = os.path.splitext(self.name)[-1] in ('.srt', )
|
||||
self.is_video = os.path.splitext(self.path)[-1] in ('.avi', '.mkv', '.dv', '.ogv', '.mpeg', '.mov', '.webm')
|
||||
self.is_audio = os.path.splitext(self.path)[-1] in ('.mp3', '.wav', '.ogg', '.flac', '.oga')
|
||||
self.is_subtitle = os.path.splitext(self.path)[-1] in ('.srt', )
|
||||
|
||||
if self.name.endswith('.srt'):
|
||||
if self.path.endswith('.srt'):
|
||||
self.is_subtitle = True
|
||||
self.is_audio = False
|
||||
self.is_video = False
|
||||
|
|
@ -138,7 +135,8 @@ class File(models.Model):
|
|||
self.is_subtitle = False
|
||||
|
||||
self.type = self.get_type()
|
||||
self.language = self.get_language()
|
||||
info = ox.parse_movie_path(self.path)
|
||||
self.language = info['language']
|
||||
self.part = self.get_part()
|
||||
|
||||
if self.type not in ('audio', 'video'):
|
||||
|
|
@ -156,9 +154,9 @@ class File(models.Model):
|
|||
|
||||
#upload and data handling
|
||||
data = models.FileField(null=True, blank=True,
|
||||
upload_to=lambda f, x: f.path('data.bin'))
|
||||
upload_to=lambda f, x: f.get_path('data.bin'))
|
||||
|
||||
def path(self, name):
|
||||
def get_path(self, name):
|
||||
h = self.oshash
|
||||
return os.path.join('files', h[:2], h[2:4], h[4:6], h[6:], name)
|
||||
|
||||
|
|
@ -277,13 +275,12 @@ class File(models.Model):
|
|||
'samplerate': self.samplerate,
|
||||
'video_codec': self.video_codec,
|
||||
'audio_codec': self.audio_codec,
|
||||
'name': self.name,
|
||||
'path': self.path,
|
||||
'size': self.size,
|
||||
#'info': self.info,
|
||||
'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()],
|
||||
'folder': self.get_folder(),
|
||||
'type': self.get_type(),
|
||||
'part': self.get_part()
|
||||
}
|
||||
|
|
@ -295,17 +292,17 @@ class File(models.Model):
|
|||
|
||||
def get_part(self):
|
||||
#FIXME: this breaks for sub/idx/srt
|
||||
if os.path.splitext(self.name)[-1] in ('.sub', '.idx', '.srt'):
|
||||
name = os.path.splitext(self.name)[0]
|
||||
if os.path.splitext(self.path)[-1] in ('.sub', '.idx', '.srt'):
|
||||
name = os.path.splitext(self.path)[0]
|
||||
if self.language:
|
||||
name = name[-(len(self.language)+1)]
|
||||
qs = self.item.files.filter(Q(is_video=True)|Q(is_audio=True),
|
||||
active=True, name__startswith=name)
|
||||
selected=True, path__startswith=name)
|
||||
if qs.count()>0:
|
||||
return qs[0].part
|
||||
if self.active:
|
||||
if self.selected:
|
||||
files = list(self.item.files.filter(type=self.type, language=self.language,
|
||||
active=self.active).order_by('sort_name'))
|
||||
selected=self.selected).order_by('sort_path'))
|
||||
if self in files:
|
||||
return files.index(self) + 1
|
||||
return None
|
||||
|
|
@ -315,7 +312,7 @@ class File(models.Model):
|
|||
return 'video'
|
||||
if self.is_audio:
|
||||
return 'audio'
|
||||
if self.is_subtitle or os.path.splitext(self.name)[-1] in ('.sub', '.idx'):
|
||||
if self.is_subtitle or os.path.splitext(self.path)[-1] in ('.sub', '.idx'):
|
||||
return 'subtitle'
|
||||
return 'unknown'
|
||||
|
||||
|
|
@ -325,30 +322,10 @@ class File(models.Model):
|
|||
return self.instances.all()[0]
|
||||
return None
|
||||
|
||||
def get_folder(self):
|
||||
def create_path(self):
|
||||
instance = self.get_instance()
|
||||
if instance:
|
||||
return instance.folder
|
||||
name = os.path.splitext(self.get_name())[0]
|
||||
name = name.replace('. ', '||').split('.')[0].replace('||', '. ')
|
||||
if self.item:
|
||||
if settings.USE_IMDB:
|
||||
director = self.item.get('director', ['Unknown Director'])
|
||||
director = map(get_name_sort, director)
|
||||
director = u'; '.join(director)
|
||||
director = re.sub(r'[:\\/]', '_', director)
|
||||
name = os.path.join(director, name)
|
||||
year = self.item.get('year')
|
||||
if year:
|
||||
name += u' (%s)' % year
|
||||
name = os.path.join(name[0].upper(), name)
|
||||
return name
|
||||
return u''
|
||||
|
||||
def get_name(self):
|
||||
instance = self.get_instance()
|
||||
if instance:
|
||||
return instance.name
|
||||
return instance.path
|
||||
if self.item:
|
||||
name = self.item.get('title', 'Untitled')
|
||||
name = re.sub(r'[:\\/]', '_', name)
|
||||
|
|
@ -357,12 +334,6 @@ class File(models.Model):
|
|||
ext = '.unknown'
|
||||
return name + ext
|
||||
|
||||
def get_language(self):
|
||||
language = self.name.split('.')
|
||||
if len(language) >= 3 and len(language[-2]) == 2:
|
||||
return language[-2]
|
||||
return ''
|
||||
|
||||
def delete_file(sender, **kwargs):
|
||||
f = kwargs['instance']
|
||||
#FIXME: delete streams here
|
||||
|
|
@ -394,7 +365,7 @@ class Volume(models.Model):
|
|||
class Instance(models.Model):
|
||||
|
||||
class Meta:
|
||||
unique_together = ("name", "folder", "volume")
|
||||
unique_together = ("path", "volume")
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
|
|
@ -403,15 +374,14 @@ class Instance(models.Model):
|
|||
ctime = models.IntegerField(default=lambda: int(time.time()), editable=False)
|
||||
mtime = models.IntegerField(default=lambda: int(time.time()), editable=False)
|
||||
|
||||
name = models.CharField(max_length=2048)
|
||||
folder = models.CharField(max_length=2048)
|
||||
extra = models.BooleanField(default=False)
|
||||
path = models.CharField(max_length=2048)
|
||||
ignore = models.BooleanField(default=False)
|
||||
|
||||
file = models.ForeignKey(File, related_name='instances')
|
||||
volume = models.ForeignKey(Volume, related_name='files')
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s's %s <%s>"% (self.volume.user, self.name, self.file.oshash)
|
||||
return u"%s's %s <%s>"% (self.volume.user, self.path, self.file.oshash)
|
||||
|
||||
@property
|
||||
def itemId(self):
|
||||
|
|
@ -421,14 +391,13 @@ class Instance(models.Model):
|
|||
return {
|
||||
'user': self.volume.user.username,
|
||||
'volume': self.volume.name,
|
||||
'folder': self.folder,
|
||||
'name': self.name
|
||||
'path': self.path
|
||||
}
|
||||
|
||||
def frame_path(frame, name):
|
||||
ext = os.path.splitext(name)[-1]
|
||||
name = "%s%s" % (frame.position, ext)
|
||||
return frame.file.path(name)
|
||||
return frame.file.get_path(name)
|
||||
|
||||
|
||||
class Frame(models.Model):
|
||||
|
|
@ -486,7 +455,7 @@ class Stream(models.Model):
|
|||
return u"%s/%s" % (self.file, self.name())
|
||||
|
||||
def path(self, name=''):
|
||||
return self.file.path(name)
|
||||
return self.file.get_path(name)
|
||||
|
||||
def extract_derivatives(self):
|
||||
config = settings.CONFIG['video']
|
||||
|
|
|
|||
|
|
@ -1,29 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
import os
|
||||
|
||||
from celery.decorators import task
|
||||
import ox
|
||||
|
||||
from item.utils import parse_path
|
||||
from item.models import get_item
|
||||
from django.conf import settings
|
||||
|
||||
import models
|
||||
|
||||
_INSTANCE_KEYS = ('mtime', 'name', 'folder')
|
||||
|
||||
_INSTANCE_KEYS = ('mtime', 'path')
|
||||
|
||||
def get_or_create_item(volume, f, user):
|
||||
in_same_folder = models.Instance.objects.filter(folder=f['folder'], volume=volume)
|
||||
if in_same_folder.count() > 0:
|
||||
i = in_same_folder[0].file.item
|
||||
else:
|
||||
if settings.USE_FOLDER:
|
||||
item_info = parse_path(f['folder'])
|
||||
else:
|
||||
item_info = parse_path(f['path'])
|
||||
i = get_item(item_info, user)
|
||||
return i
|
||||
def get_or_create_item(volume, info, user):
|
||||
item_info = ox.parse_movie_info(info['path'])
|
||||
return get_item(item_info, user)
|
||||
|
||||
def get_or_create_file(volume, f, user, item=None):
|
||||
try:
|
||||
|
|
@ -31,7 +21,7 @@ def get_or_create_file(volume, f, user, item=None):
|
|||
except models.File.DoesNotExist:
|
||||
file = models.File()
|
||||
file.oshash = f['oshash']
|
||||
file.name = f['name']
|
||||
file.path = f['path']
|
||||
if item:
|
||||
file.item = item
|
||||
else:
|
||||
|
|
@ -50,13 +40,11 @@ def update_or_create_instance(volume, f):
|
|||
setattr(instance, key, f[key])
|
||||
updated=True
|
||||
if updated:
|
||||
if instance.name.lower().startswith('extras/') or \
|
||||
instance.name.lower().startswith('versions/'):
|
||||
instance.extra = True
|
||||
instance.ignore = False
|
||||
instance.save()
|
||||
instance.file.save()
|
||||
else:
|
||||
instance = models.Instance.objects.filter(name=f['name'], folder=f['folder'], volume=volume)
|
||||
instance = models.Instance.objects.filter(path=f['path'], volume=volume)
|
||||
if instance.count()>0:
|
||||
#same path, other oshash, keep path/item mapping, remove instance
|
||||
item = instance[0].file.item
|
||||
|
|
@ -69,9 +57,6 @@ def update_or_create_instance(volume, f):
|
|||
instance.file = get_or_create_file(volume, f, volume.user, item)
|
||||
for key in _INSTANCE_KEYS:
|
||||
setattr(instance, key, f[key])
|
||||
if instance.name.lower().startswith('extras/') or \
|
||||
instance.name.lower().startswith('versions/'):
|
||||
instance.extra = True
|
||||
instance.save()
|
||||
instance.file.save()
|
||||
instance.file.item.update_wanted()
|
||||
|
|
@ -83,17 +68,15 @@ def update_files(user, volume, files):
|
|||
volume, created = models.Volume.objects.get_or_create(user=user, name=volume)
|
||||
all_files = []
|
||||
for f in files:
|
||||
folder = f['path'].split('/')
|
||||
name = folder.pop()
|
||||
if folder and folder[-1].lower() in ('extras', 'versions', 'dvds'):
|
||||
name = '/'.join([folder.pop(), name])
|
||||
f['folder'] = '/'.join(folder)
|
||||
f['name'] = name
|
||||
all_files.append(f['oshash'])
|
||||
update_or_create_instance(volume, f)
|
||||
|
||||
#ignore extras etc,
|
||||
#imdb stlye is L/Last, First/Title (Year)/Title.. 4
|
||||
#otherwise T/Title (Year)/Title... 3
|
||||
folder_depth = settings.USE_IMDB and 4 or 3
|
||||
if len(f['path'].split('/')) == folder_depth:
|
||||
all_files.append(f['oshash'])
|
||||
update_or_create_instance(volume, f)
|
||||
|
||||
#remove deleted files
|
||||
#FIXME: can this have any bad consequences? i.e. on the selction of used item files.
|
||||
models.Instance.objects.filter(volume=volume).exclude(file__oshash__in=all_files).delete()
|
||||
|
||||
@task(queue="encoding")
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from django.shortcuts import get_object_or_404, redirect
|
|||
from django.conf import settings
|
||||
from django.db.models import Count, Sum
|
||||
|
||||
import ox
|
||||
from ox.utils import json
|
||||
from ox.django.decorators import login_required_json
|
||||
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
|
||||
|
|
@ -105,7 +106,7 @@ def update(request):
|
|||
file__wanted=True)]
|
||||
response['data']['file'] = [f.file.oshash for f in files.filter(file__is_subtitle=True,
|
||||
file__available=False,
|
||||
name__endswith='.srt')]
|
||||
path__endswith='.srt')]
|
||||
|
||||
return render_to_json_response(response)
|
||||
actions.register(update, cache=False)
|
||||
|
|
@ -319,24 +320,8 @@ def lookup_file(request, oshash):
|
|||
def _order_query(qs, sort, prefix=''):
|
||||
order_by = []
|
||||
if len(sort) == 1:
|
||||
sort.append({'operator': '+', 'key': 'sort_name'})
|
||||
sort.append({'operator': '+', 'key': 'path'})
|
||||
sort.append({'operator': '-', 'key': 'created'})
|
||||
'''
|
||||
if sort[0]['key'] == 'title':
|
||||
sort.append({'operator': '-', 'key': 'year'})
|
||||
sort.append({'operator': '+', 'key': 'director'})
|
||||
elif sort[0]['key'] == 'director':
|
||||
sort.append({'operator': '-', 'key': 'year'})
|
||||
sort.append({'operator': '+', 'key': 'title'})
|
||||
elif sort[0]['key'] == 'year':
|
||||
sort.append({'operator': '+', 'key': 'director'})
|
||||
sort.append({'operator': '+', 'key': 'title'})
|
||||
elif not sort[0]['key'] in ('value', 'value_sort'):
|
||||
sort.append({'operator': '+', 'key': 'director'})
|
||||
sort.append({'operator': '-', 'key': 'year'})
|
||||
sort.append({'operator': '+', 'key': 'title'})
|
||||
|
||||
'''
|
||||
|
||||
for e in sort:
|
||||
operator = e['operator']
|
||||
|
|
@ -346,7 +331,7 @@ def _order_query(qs, sort, prefix=''):
|
|||
'id': 'item__itemId',
|
||||
'users': 'instances__volume__user__username',
|
||||
'resolution': 'width',
|
||||
'name': 'sort_name'
|
||||
'path': 'sort_path'
|
||||
}.get(e['key'], e['key'])
|
||||
#if operator=='-' and '%s_desc'%key in models.ItemSort.descending_fields:
|
||||
# key = '%s_desc' % key
|
||||
|
|
@ -400,10 +385,10 @@ Groups
|
|||
keys: array of keys to return
|
||||
group: group elements by, country, genre, director...
|
||||
|
||||
possible values for keys: name, items
|
||||
possible values for keys: path, items
|
||||
|
||||
with keys
|
||||
items contains list of {'name': string, 'items': int}:
|
||||
items contains list of {'path': string, 'items': int}:
|
||||
return {'status': {'code': int, 'text': string},
|
||||
'data': {items: array}}
|
||||
|
||||
|
|
@ -465,7 +450,7 @@ Positions
|
|||
|
||||
elif 'range' in data:
|
||||
qs = qs[query['range'][0]:query['range'][1]]
|
||||
response['data']['items'] = [{'name': i['value'], 'items': i[items]} for i in qs]
|
||||
response['data']['items'] = [{'path': i['value'], 'items': i[items]} for i in qs]
|
||||
else:
|
||||
response['data']['items'] = qs.count()
|
||||
elif 'positions' in query:
|
||||
|
|
@ -481,6 +466,7 @@ Positions
|
|||
response['data']['items'] = []
|
||||
qs = models.File.objects.filter(item__in=query['qs'])
|
||||
qs = _order_query(qs, query['sort'])
|
||||
qs = qs.select_related()
|
||||
keys = query['keys']
|
||||
qs = qs[query['range'][0]:query['range'][1]]
|
||||
response['data']['items'] = [f.json(keys) for f in qs]
|
||||
|
|
@ -492,3 +478,19 @@ Positions
|
|||
|
||||
actions.register(findFiles)
|
||||
|
||||
def parsePath(request): #parse path and return info
|
||||
'''
|
||||
param data {
|
||||
path: string
|
||||
}
|
||||
return {
|
||||
status: {'code': int, 'text': string},
|
||||
data: {
|
||||
imdb: string
|
||||
}
|
||||
}
|
||||
'''
|
||||
path = json.loads(request.POST['data'])['path']
|
||||
response = json_response(ox.parse_movie_path(path))
|
||||
return render_to_json_response(response)
|
||||
actions.register(parsePath)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue