clip timeline

This commit is contained in:
j 2011-01-01 17:19:14 +05:30
parent 2d5f924891
commit a00b88acd9
6 changed files with 244 additions and 1 deletions

View file

@ -129,6 +129,7 @@ INSTALLED_APPS = (
'place', 'place',
'text', 'text',
'torrent', 'torrent',
'timeline',
'user', 'user',
'api', 'api',
) )

View file

View file

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
from django.db import models
from django.contrib.auth.models import User
class Timeline(models.Model):
class Meta:
unique_together = ("user", "name")
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User)
name = models.CharField(max_length=255)
public = models.BooleanField(default=False)
duration = models.FloatField(default=0)
#FIXME: how to deal with width/height?
def __unicode__(self):
return u'%s (%s)' % (self.title, self.user)
def editable(self, user):
#FIXME: make permissions work
if self.user == user or user.has_perm('Ox.admin'):
return True
return False
'''
#creating a new file from clips seams to work not to bad, needs testing for frame accuracy
ffmpeg -i 96p.webm -ss 123.33 -t 3 -vcodec copy -acodec copy 1.webm
ffmpeg -i 96p.webm -ss 323.33 -t 4 -vcodec copy -acodec copy 2.webm
ffmpeg -i 96p.webm -ss 423.33 -t 1 -vcodec copy -acodec copy 3.webm
mkvmerge 1.webm +2.webm +3.webm -o cutup.webm
'''
class Clip(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
timeline = models.ForeignKey(Timeline)
position = models.IntegerField(default=0) #clip position
timeline_position = models.FloatField(default=0) #time on timeline
item = models.ForeignKey("item.Item")
start = models.FloatField(default=0)
end = models.FloatField(default=-1)
def __unicode__(self):
return u'%s/%0.3f-%0.3f' % (self.item.itemId, self.start, self.end)

23
pandora/timeline/tests.py Normal file
View file

@ -0,0 +1,23 @@
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}

166
pandora/timeline/views.py Normal file
View file

@ -0,0 +1,166 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
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
from api.actions import actions
import models
@login_required_json
def addClip(request):
'''
param data
{timeline: timelineId,
item: itemId,
start: float,
end: float,
}
return {'status': {'code': int, 'text': string},
'data': {}}
'''
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if 'item' in data:
item = get_object_or_404_json(models.Item, pk=data['item'])
if list.editable(request.user):
list.add(item)
response = json_response(status=200, text='item added')
else:
response = json_response(status=403, text='not allowed')
elif 'query' in data:
response = json_response(status=501, text='not implemented')
else:
response = json_response(status=501, text='not implemented')
return render_to_json_response(response)
actions.register(addClip)
@login_required_json
def removeClip(request):
'''
param data
{timeline: timelineId,
clip: clipId,
}
return {'status': {'code': int, 'text': string},
'data': {}}
'''
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if 'item' in data:
item = get_object_or_404_json(models.Item, pk=data['item'])
if list.editable(request.user):
list.remove(item)
response = json_response(status=200, text='item removed')
else:
response = json_response(status=403, text='not allowed')
elif 'query' in data:
response = json_response(status=501, text='not implemented')
else:
response = json_response(status=501, text='not implemented')
return render_to_json_response(response)
actions.register(removeClip)
def getTimeline(request):
'''
param data
{name: value, user: user}
return {
'status': {'code': int, 'text': string},
'data': {
fixme
}
}
could be
timeline: {
0: {
itemId:, start, end
},
123: {
itemId:, start, end
}
}
or implicit timeline position
timeline: [
{
itemId:, start, end
},
{
itemId:, start, end
}
]
'''
response = json_response(status=501, text='not implemented')
return render_to_json_response(response)
actions.register(getTimeline)
@login_required_json
def addTimeline(request):
'''
param data
{name: value}
return {'status': {'code': int, 'text': string},
'data': {}}
'''
data = json.loads(request.POST['data'])
if models.Timeline.filter(name=data['name'], user=request.user).count() == 0:
list = models.Timeline(name=data['name'], user=request.user)
list.save()
response = json_response(status=200, text='created')
else:
response = json_response(status=200, text='list already exists')
response['data']['errors'] = {
'name': 'List already exists'
}
return render_to_json_response(response)
actions.register(addTimeline)
@login_required_json
def editTimeline(request):
'''
param data
{key: value}
keys: name, public
return {'status': {'code': int, 'text': string},
'data': {}
}
'''
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if list.editable(request.user):
for key in data:
if key in ('name', 'public'):
setattr(list, key, data['key'])
else:
response = json_response(status=403, text='not allowed')
return render_to_json_response(response)
actions.register(editTimeline)
@login_required_json
def removeTimeline(request):
'''
param data
{key: value}
return {'status': {'code': int, 'text': string},
'data': {}}
'''
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if list.editable(request.user):
list.delete()
else:
response = json_response(status=403, text='not allowed')
return render_to_json_response(response)
actions.register(removeTimeline)

View file

@ -738,6 +738,7 @@ var pandora = new Ox.App({
}), callback); }), callback);
} }
}); });
Ox.print('ZZZ')
$.each(app.ui.groups, function(i_, group_) { $.each(app.ui.groups, function(i_, group_) {
if (i_ != i) { if (i_ != i) {
Ox.print('setting groups request', i, i_) Ox.print('setting groups request', i, i_)
@ -838,7 +839,7 @@ var pandora = new Ox.App({
duration: video.duration, duration: video.duration,
find: '', find: '',
frameURL: function(position) { frameURL: function(position) {
return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg' return '/' + app.user.ui.item + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg'
}, },
height: app.$ui.contentPanel.size(1), height: app.$ui.contentPanel.size(1),
id: 'editor', id: 'editor',
@ -865,6 +866,7 @@ var pandora = new Ox.App({
{ {
collapsible: true, collapsible: true,
element: app.$ui.annotations = ui.annotations(), element: app.$ui.annotations = ui.annotations(),
//.bindEvent('resize', function(event, data) { Ox.print('resize annotations', data); }),
resizable: true, resizable: true,
resize: [256, 384], resize: [256, 384],
size: 256 size: 256