update video editor

This commit is contained in:
rolux 2012-01-15 20:35:37 +05:30
parent ed37cd6924
commit 84cdd2ebf5
13 changed files with 155 additions and 44 deletions

View file

@ -118,7 +118,6 @@ class Annotation(models.Model):
} }
for key in ('id', 'in', 'out', 'value', 'created', 'modified'): for key in ('id', 'in', 'out', 'value', 'created', 'modified'):
j[key] = getattr(self, { j[key] = getattr(self, {
'duration': 'clip__duration',
'hue': 'clip__hue', 'hue': 'clip__hue',
'id': 'public_id', 'id': 'public_id',
'in': 'start', 'in': 'start',
@ -127,6 +126,7 @@ class Annotation(models.Model):
'saturation': 'clip__saturation', 'saturation': 'clip__saturation',
'volume': 'clip__volume', 'volume': 'clip__volume',
}.get(key, key)) }.get(key, key))
j['duration'] = j['out'] - j['in']
l = self.get_layer() l = self.get_layer()
if l['type'] == 'place': if l['type'] == 'place':

View file

@ -142,6 +142,7 @@ def addAnnotation(request):
value=data['value']) value=data['value'])
annotation.save() annotation.save()
response = json_response(annotation.json()) response = json_response(annotation.json())
response['data']['editable'] = True
else: else:
response = json_response(status=403, text='permission denied') response = json_response(status=403, text='permission denied')
return render_to_json_response(response) return render_to_json_response(response)
@ -198,6 +199,7 @@ def editAnnotation(request):
}.get(key,key), data[key]) }.get(key,key), data[key])
a.save() a.save()
response['data'] = a.json() response['data'] = a.json()
response['data']['editable'] = True
else: else:
response = json_response(status=403, text='permission denied') response = json_response(status=403, text='permission denied')
return render_to_json_response(response) return render_to_json_response(response)

View file

@ -364,6 +364,17 @@ def editFile(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(editFile, cache=False) actions.register(editFile, cache=False)
@login_required_json
def removeFiles(request):
data = json.loads(request.POST['data'])
response = json_response()
if request.user.get_profile().get_level() == 'admin':
models.File.objects.filter(oshash__in=data['ids'], instances__id=None).delete()
else:
response = json_response(status=403, text='permissino denied')
return render_to_json_response(response)
actions.register(removeFiles, cache=False)
def lookup_file(request, oshash): def lookup_file(request, oshash):
f = get_object_or_404(models.File, oshash=oshash) f = get_object_or_404(models.File, oshash=oshash)

View file

@ -216,6 +216,15 @@ class Item(models.Model):
for g in filter(lambda g: g not in current_groups, groups): for g in filter(lambda g: g not in current_groups, groups):
group, created = Group.objects.get_or_create(name=g) group, created = Group.objects.get_or_create(name=g)
self.groups.add(group) self.groups.add(group)
keys = [k['id'] for k in
filter(lambda i: i.get('description'), settings.CONFIG['itemKeys'])]
for k in keys:
key = '%sdescription' % k
if key in data:
value = data.get(k, self.get(k, ''))
d, created = Description.objects.get_or_create(key=k, value=value)
d.description = data.pop(key)
d.save()
for key in data: for key in data:
self.data[key] = data[key] self.data[key] = data[key]
return self.save() return self.save()
@ -496,6 +505,17 @@ class Item(models.Model):
elif self.poster_frame != -1.0: elif self.poster_frame != -1.0:
i['posterFrame'] = self.poster_frame i['posterFrame'] = self.poster_frame
dkeys = [k['id'] for k in
filter(lambda i: i.get('description'), settings.CONFIG['itemKeys'])]
if keys:
dkeys = filter(lambda k: k in keys, dkeys)
for key in dkeys:
qs = Description.objects.filter(key=key, value=self.get(key, ''))
if qs.count() == 0:
i['%sdescription'%key] = ''
else:
i['%sdescription'%key] = qs[0].description
if keys: if keys:
info = {} info = {}
for key in keys: for key in keys:
@ -1299,3 +1319,13 @@ class Facet(models.Model):
self.sortvalue = utils.sort_string(self.value) self.sortvalue = utils.sort_string(self.value)
super(Facet, self).save(*args, **kwargs) super(Facet, self).save(*args, **kwargs)
class Description(models.Model):
'''
shared itemkey descriptions
'''
class Meta:
unique_together = ("key", "value")
key = models.CharField(max_length=200, db_index=True)
value = models.CharField(max_length=1000, db_index=True)
description = models.TextField()

View file

@ -404,7 +404,11 @@ def get(request):
if not data['keys'] or 'notes' in data['keys'] \ if not data['keys'] or 'notes' in data['keys'] \
and request.user.get_profile().capability('canEditMetadata'): and request.user.get_profile().capability('canEditMetadata'):
info['notes'] = item.notes info['notes'] = item.notes
if not data['keys'] or 'groups' in data['keys'] \
and request.user.get_profile().capability('canEditMetadata'):
info['groups'] = [g.name for g in item.groups.all()] info['groups'] = [g.name for g in item.groups.all()]
info['editable'] = item.editable(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')

View file

@ -56,7 +56,8 @@
{"id": "keywords", "title": "Keywords", "type": "string"}, {"id": "keywords", "title": "Keywords", "type": "string"},
{"id": "language", "title": "Languages", "type": "string"}, {"id": "language", "title": "Languages", "type": "string"},
{"id": "places", "title": "Places", "type": "string"}, {"id": "places", "title": "Places", "type": "string"},
{"id": "year", "title": "Years", "type": "integer"}, //{"id": "year", "title": "Years", "type": "integer"},
{"id": "features", "title": "Features", "type": "string"},
{"id": "director", "title": "Directors", "type": "string"}, {"id": "director", "title": "Directors", "type": "string"},
{"id": "cinematographer", "title": "Cinematographers", "type": "string"} {"id": "cinematographer", "title": "Cinematographers", "type": "string"}
], ],
@ -104,6 +105,7 @@
"title": "Source", "title": "Source",
"type": "string", "type": "string",
"autocomplete": true, "autocomplete": true,
"description": true,
"columnWidth": 180, "columnWidth": 180,
"filter": true, "filter": true,
"find": true "find": true
@ -113,6 +115,7 @@
"title": "Project", "title": "Project",
"type": "string", "type": "string",
"autocomplete": true, "autocomplete": true,
"description": true,
"columnWidth": 120, "columnWidth": 120,
"filter": true, "filter": true,
"find": true "find": true
@ -562,7 +565,7 @@
"showBrowser": true, "showBrowser": true,
"showCalendarControls": true, // fixme: should be false "showCalendarControls": true, // fixme: should be false
"showFilters": true, "showFilters": true,
"showFlags": true, "showFlags": false,
"showHome": true, "showHome": true,
"showIconBrowser": false, "showIconBrowser": false,
"showInfo": true, "showInfo": true,

View file

@ -7,9 +7,7 @@ pandora.ui.clipList = function(videoRatio) {
var ui = pandora.user.ui, var ui = pandora.user.ui,
fixedRatio = !ui.item ? 16/9 : videoRatio, fixedRatio = !ui.item ? 16/9 : videoRatio,
isClipView = !ui.item ? ui.listView == 'clip' : ui.itemView == 'clips', isClipView = !ui.item ? ui.listView == 'clip' : ui.itemView == 'clips',
textKey = Ox.getObjectById(pandora.site.layers, 'subtitles') textKey = pandora.getClipTextKey(),
? 'subtitles'
: 'annotations',
that = Ox.IconList({ that = Ox.IconList({
fixedRatio: fixedRatio, fixedRatio: fixedRatio,
item: function(data, sort, size) { item: function(data, sort, size) {

View file

@ -4,9 +4,7 @@
pandora.ui.clipsView = function(videoRatio) { pandora.ui.clipsView = function(videoRatio) {
var textKey = Ox.getObjectById(pandora.site.layers, 'subtitles') var textKey = pandora.getClipTextKey(),
? 'subtitles'
: 'annotations',
that = Ox.SplitPanel({ that = Ox.SplitPanel({
elements: [ elements: [
{ {

View file

@ -203,6 +203,20 @@ pandora.ui.filesView = function(options, self) {
}); });
} }
}, },
'delete': function(data) {
var ids = data.ids.filter(function(id) {
return self.$filesList.value(id, 'instances').length == 0;
});
if(ids.length>0 && pandora.user.level == 'admin') {
Ox.print('delete', ids);
pandora.api.removeFiles({
ids: ids
}, function(result) {
Ox.Request.clearCache();
self.$filesList.reloadList();
});
}
},
init: function(data) { init: function(data) {
self.numberOfItems = data.items; self.numberOfItems = data.items;
}, },

View file

@ -6,7 +6,8 @@ pandora.ui.infoView = function(data) {
// when collapsing the movies browser, the info view should become a split panel // when collapsing the movies browser, the info view should become a split panel
var ui = pandora.user.ui, var ui = pandora.user.ui,
canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level], descriptions = [],
canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level] || data.editable,
css = { css = {
marginTop: '4px', marginTop: '4px',
textAlign: 'justify', textAlign: 'justify',
@ -108,7 +109,6 @@ pandora.ui.infoView = function(data) {
.appendTo($reflection), .appendTo($reflection),
$text = Ox.Element({ $text = Ox.Element({
tooltip: canEdit && !isEditable ? 'Doubleclick to reload metadata' : ''
}) })
.css({ .css({
position: 'absolute', position: 'absolute',
@ -117,9 +117,6 @@ pandora.ui.infoView = function(data) {
right: margin + statisticsWidth + margin + 'px', right: margin + statisticsWidth + margin + 'px',
//background: 'green' //background: 'green'
}) })
.bindEvent(canEdit && !isEditable ? {
doubleclick: reloadMetadata
} : {})
.appendTo($data.$element), .appendTo($data.$element),
$statistics = $('<div>') $statistics = $('<div>')
@ -252,7 +249,8 @@ pandora.ui.infoView = function(data) {
Ox.Editable({ Ox.Editable({
clickLink: pandora.clickLink, clickLink: pandora.clickLink,
placeholder: formatLight('unknown'), placeholder: formatLight('unknown'),
tooltip: 'Doubleclick to edit', editable: isEditable,
tooltip: isEditable ? 'Doubleclick to edit' : '',
type: 'textarea', type: 'textarea',
value: data.description || '' value: data.description || ''
}) })
@ -267,14 +265,14 @@ pandora.ui.infoView = function(data) {
var list_keys = ['language', 'topic', 'director', 'cinematographer', 'features', 'groups']; var list_keys = ['language', 'topic', 'director', 'cinematographer', 'features', 'groups'];
$('<div>').html('<br>').appendTo($text); $('<div>').html('<br>').appendTo($text);
[ [
'source',
'project',
'date', 'date',
'location', 'location',
'director', 'director',
'cinematographer', 'cinematographer',
'features', 'features',
'language', 'language',
'source',
'project',
'topic', 'topic',
'user', 'user',
].forEach(function(key) { ].forEach(function(key) {
@ -291,11 +289,16 @@ pandora.ui.infoView = function(data) {
clickLink: pandora.clickLink, clickLink: pandora.clickLink,
format: function(value) { format: function(value) {
return list_keys.indexOf(key) >= 0 return list_keys.indexOf(key) >= 0
? formatValue(value.split(', '), key) ? formatValue(value.split(', '), {
'director': 'name',
'cinematographer': 'name',
'features': 'name',
}[key] || key)
: value; : value;
}, },
placeholder: formatLight('unknown'), placeholder: formatLight('unknown'),
tooltip: 'Doubleclick to edit', editable: isEditable,
tooltip: isEditable ? 'Doubleclick to edit' : '',
value: list_keys.indexOf(key) >= 0 value: list_keys.indexOf(key) >= 0
? (data[key] || []).join(', ') ? (data[key] || []).join(', ')
: data[key] || '' : data[key] || ''
@ -306,6 +309,31 @@ pandora.ui.infoView = function(data) {
} }
}) })
.appendTo($div); .appendTo($div);
if(pandora.site.itemKeys.filter(function(item) {
if (item.id == key)
return item.description
}).length > 0) {
$('<div>')
.append(
descriptions[key] = Ox.Editable({
clickLink: pandora.clickLink,
placeholder: formatLight(Ox.toTitleCase(key) + ' Description'),
editable: isEditable,
tooltip: isEditable ? 'Doubleclick to edit' : '',
type: 'textarea',
value: data[key + 'description']|| ''
})
.bindEvent({
submit: function(event) {
editMetadata(key + 'description', event.value);
}
})
).css({
'padding-top': '4px',
'padding-bottom': '4px'
})
.appendTo($div);
}
}); });
$('<div>').css({height: '16px'}).appendTo($text); $('<div>').css({height: '16px'}).appendTo($text);
@ -348,9 +376,10 @@ pandora.ui.infoView = function(data) {
.appendTo($statistics); .appendTo($statistics);
renderRightsLevel(); renderRightsLevel();
// Notes ------------------------------------------------------------------- // Groups, Notes ---------------------------------------------------------
if (canEdit) { if (canEdit) {
$('<div>') $('<div>')
.css({marginBottom: '4px'}) .css({marginBottom: '4px'})
.append(formatKey('Groups', true)) .append(formatKey('Groups', true))
@ -403,6 +432,11 @@ pandora.ui.infoView = function(data) {
edit[key] = value; edit[key] = value;
} }
pandora.api.edit(edit, function(result) { pandora.api.edit(edit, function(result) {
data[key] = value;
descriptions[key] && descriptions[key].options({
value: result.data[key+'description']
});
Ox.Request.clearCache(); // fixme: too much? can change filter/list etc Ox.Request.clearCache(); // fixme: too much? can change filter/list etc
if (result.data.id != data.id) { if (result.data.id != data.id) {
pandora.UI.set({item: result.data.id}); pandora.UI.set({item: result.data.id});
@ -445,21 +479,6 @@ pandora.ui.infoView = function(data) {
); );
} }
function reloadMetadata() {
var item = ui.item;
// fixme: maybe there's a better method name for this?
pandora.api.updateExternalData({
id: ui.item
}, function(result) {
Ox.Request.clearCache(item);
if (ui.item == item && ui.itemView == 'info') {
pandora.$ui.contentPanel.replaceElement(
1, pandora.$ui.item = pandora.ui.item()
);
}
});
}
function renderCapabilities(rightsLevel) { function renderCapabilities(rightsLevel) {
var capabilities = Ox.merge( var capabilities = Ox.merge(
canEdit ? [{name: 'canSeeItem', symbol: 'Find'}] : [], canEdit ? [{name: 'canSeeItem', symbol: 'Find'}] : [],

View file

@ -278,8 +278,9 @@ pandora.ui.item = function() {
out: data.out, out: data.out,
value: data.value, value: data.value,
}, function(result) { }, function(result) {
result.data.editable = true; // fixme: both should come from backend
result.data.duration = result.data.out - result.data['in']; result.data.duration = result.data.out - result.data['in'];
result.data.editable = true;
pandora.$ui.editor.addAnnotation(data.layer, result.data); pandora.$ui.editor.addAnnotation(data.layer, result.data);
}); });
}, },
@ -295,6 +296,10 @@ pandora.ui.item = function() {
annotationssort: function(data) { annotationssort: function(data) {
pandora.UI.set({annotationsSort: data.sort}); pandora.UI.set({annotationsSort: data.sort});
}, },
define: function(data) {
pandora.$ui.placesDialog && pandora.$ui.placesDialog.remove();
pandora.$ui.placesDialog = pandora.ui.placesDialog(data).open();
},
editannotation: function(data) { editannotation: function(data) {
Ox.Log('', 'editAnnotation', data); Ox.Log('', 'editAnnotation', data);
//fixme: check that edit was successfull //fixme: check that edit was successfull
@ -304,9 +309,11 @@ pandora.ui.item = function() {
out: data.out, out: data.out,
value: data.value, value: data.value,
}, function(result) { }, function(result) {
Ox.Log('', 'editAnnotation result', result); Ox.print('', 'editAnnotation result', result);
// fixme: both should come from backend
result.data.duration = result.data.out - result.data['in']; result.data.duration = result.data.out - result.data['in'];
pandora.$ui.editor.updateAnnotation(data.layer, result.data); result.data.editable = true;
pandora.$ui.editor.updateAnnotation(result.data);
}); });
}, },
find: function(data) { find: function(data) {

View file

@ -2,16 +2,12 @@
'use strict'; 'use strict';
pandora.ui.placesDialog = function() { pandora.ui.placesDialog = function(options) {
// options can be {id: '...'} or {name: '...'}
var height = Math.round((window.innerHeight - 48) * 0.9), var height = Math.round((window.innerHeight - 48) * 0.9),
width = Math.round(window.innerWidth * 0.9), width = Math.round(window.innerWidth * 0.9),
$content = Ox.ListMap({ $content = Ox.ListMap({
height: height - 48, height: height - 48,
places: function(data, callback) {
return pandora.api.findPlaces(Ox.extend({
query: {conditions: [], operator: ''}
}, data), callback);
},
addPlace: function(place, callback) { addPlace: function(place, callback) {
pandora.api.addPlace(place, function(result) { pandora.api.addPlace(place, function(result) {
Ox.Request.clearCache(); // fixme: remove Ox.Request.clearCache(); // fixme: remove
@ -45,12 +41,23 @@ pandora.ui.placesDialog = function() {
callback(result.data.items); callback(result.data.items);
}); });
}, },
names: pandora.hasPlacesLayer ? function(callback) {
pandora.api.getPlaceNames(function(result) {
callback(result.data.items);
});
} : null,
places: function(data, callback) {
return pandora.api.findPlaces(Ox.extend({
query: {conditions: [], operator: ''}
}, data), callback);
},
removePlace: function(place, callback) { removePlace: function(place, callback) {
pandora.api.removePlace(place, function(result) { pandora.api.removePlace(place, function(result) {
Ox.Request.clearCache(); // fixme: remove Ox.Request.clearCache(); // fixme: remove
callback(result); callback(result);
}); });
}, },
selected: options ? options.id : void 0,
showControls: pandora.user.ui.showMapControls, showControls: pandora.user.ui.showMapControls,
showLabels: pandora.user.ui.showMapLabels, showLabels: pandora.user.ui.showMapLabels,
showTypes: true, showTypes: true,

View file

@ -495,6 +495,12 @@ pandora.getClipsQuery = function() {
return clipsQuery; return clipsQuery;
}; };
pandora.getClipTextKey = function() {
return Ox.getObjectById(pandora.site.layers, 'subtitles')
? 'subtitles'
: 'annotations';
};
(function() { (function() {
var itemTitles = {}; var itemTitles = {};
pandora.getDocumentTitle = function(itemTitle) { pandora.getDocumentTitle = function(itemTitle) {
@ -820,11 +826,23 @@ pandora.hasDialogOrScreen = function() {
|| $('.OxScreen').length; || $('.OxScreen').length;
}; };
pandora.hasEventsLayer = function() {
return pandora.site.layers.some(function(layer) {
return layer.type == 'event';
});
};
pandora.hasFocusedInput = function() { pandora.hasFocusedInput = function() {
var focused = Ox.Focus.focused(); var focused = Ox.Focus.focused();
return focused && Ox.UI.elements[focused].is('.OxInput'); return focused && Ox.UI.elements[focused].is('.OxInput');
}; };
pandora.hasPlacesLayer = function() {
return pandora.site.layers.some(function(layer) {
return layer.type == 'place';
});
};
pandora.isClipView = function(view, item) { pandora.isClipView = function(view, item) {
if (arguments.length == 0) { if (arguments.length == 0) {
item = pandora.user.ui.item; item = pandora.user.ui.item;