clip timeline
This commit is contained in:
parent
2d5f924891
commit
a00b88acd9
6 changed files with 244 additions and 1 deletions
|
@ -129,6 +129,7 @@ INSTALLED_APPS = (
|
||||||
'place',
|
'place',
|
||||||
'text',
|
'text',
|
||||||
'torrent',
|
'torrent',
|
||||||
|
'timeline',
|
||||||
'user',
|
'user',
|
||||||
'api',
|
'api',
|
||||||
)
|
)
|
||||||
|
|
0
pandora/timeline/__init__.py
Normal file
0
pandora/timeline/__init__.py
Normal file
51
pandora/timeline/models.py
Normal file
51
pandora/timeline/models.py
Normal 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
23
pandora/timeline/tests.py
Normal 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
166
pandora/timeline/views.py
Normal 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)
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue