forked from 0x2620/pandora
output some metadata for search engines, cleanup padma metadata
This commit is contained in:
parent
d6b9929052
commit
06741903ba
8 changed files with 158 additions and 52 deletions
|
@ -556,6 +556,7 @@
|
||||||
],
|
],
|
||||||
"sendReferrer": false,
|
"sendReferrer": false,
|
||||||
"site": {
|
"site": {
|
||||||
|
"description": "0xDB is an experimental - and to some degree imaginary - movie database. It is intended to help re-imagine the future of cinema on the internet, to push the boundaries of web applications, and to serve as a point of reference for individuals and institutions who are dealing with large collections of films.",
|
||||||
// FIXME: "from" and "to" would be more intuitive as keys here
|
// FIXME: "from" and "to" would be more intuitive as keys here
|
||||||
"email": {
|
"email": {
|
||||||
// E-mail address in contact form (to)
|
// E-mail address in contact form (to)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from django.http import HttpResponse
|
||||||
from ox.django.shortcuts import json_response, render_to_json_response
|
from ox.django.shortcuts import json_response, render_to_json_response
|
||||||
from ox.django.decorators import login_required_json
|
from ox.django.decorators import login_required_json
|
||||||
|
|
||||||
|
import ox
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
|
||||||
import models
|
import models
|
||||||
|
@ -31,7 +32,7 @@ def intro(request):
|
||||||
def index(request):
|
def index(request):
|
||||||
context = RequestContext(request, {
|
context = RequestContext(request, {
|
||||||
'base_url': request.build_absolute_uri('/'),
|
'base_url': request.build_absolute_uri('/'),
|
||||||
'settings': settings
|
'settings': settings,
|
||||||
})
|
})
|
||||||
return render_to_response('index.html', context)
|
return render_to_response('index.html', context)
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,9 @@ import random
|
||||||
|
|
||||||
import Image
|
import Image
|
||||||
from django.db.models import Count, Sum, Max
|
from django.db.models import Count, Sum, Max
|
||||||
|
from django.template import RequestContext
|
||||||
from django.http import HttpResponse, HttpResponseForbidden, Http404
|
from django.http import HttpResponse, HttpResponseForbidden, Http404
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect, render_to_response
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
@ -807,3 +808,58 @@ def random_annotation(request):
|
||||||
clip = item.annotations.all()[pos]
|
clip = item.annotations.all()[pos]
|
||||||
return redirect('/%s'% clip.public_id)
|
return redirect('/%s'% clip.public_id)
|
||||||
|
|
||||||
|
def item(request, id):
|
||||||
|
id = id.split('/')[0]
|
||||||
|
template = 'index.html'
|
||||||
|
qs = models.Item.objects.filter(itemId=id)
|
||||||
|
if qs.count() == 0:
|
||||||
|
context = RequestContext(request, {
|
||||||
|
'base_url': request.build_absolute_uri('/'),
|
||||||
|
'settings': settings
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
item = qs[0]
|
||||||
|
template = 'item.html'
|
||||||
|
keys = [
|
||||||
|
'year',
|
||||||
|
'director',
|
||||||
|
'country',
|
||||||
|
'keywords',
|
||||||
|
'summary'
|
||||||
|
]
|
||||||
|
data = []
|
||||||
|
for key in keys:
|
||||||
|
value = item.get(key)
|
||||||
|
if value:
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = value = u', '.join([unicode(v) for v in value])
|
||||||
|
data.append({'key': key.capitalize(), 'value': value})
|
||||||
|
clips = []
|
||||||
|
for c in item.clips.all():
|
||||||
|
clip = {
|
||||||
|
'in': c.start,
|
||||||
|
'annotations': '<br />\n'.join([a.value for a in c.annotations.all()])
|
||||||
|
}
|
||||||
|
clips.append(clip)
|
||||||
|
ctx = {
|
||||||
|
'base_url': request.build_absolute_uri('/'),
|
||||||
|
'url': request.build_absolute_uri('/%s' % id),
|
||||||
|
'id': id,
|
||||||
|
'settings': settings,
|
||||||
|
'data': data,
|
||||||
|
'clips': clips,
|
||||||
|
'icon': 'poster',
|
||||||
|
|
||||||
|
}
|
||||||
|
for key in ('title', 'description', 'keywords'):
|
||||||
|
value = item.get({
|
||||||
|
'description': 'summary' in keys and 'summary' or 'description'
|
||||||
|
}.get(key, key))
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = value = ', '.join(value)
|
||||||
|
if value:
|
||||||
|
ctx[key] = ox.stripTags(value)
|
||||||
|
|
||||||
|
context = RequestContext(request, ctx)
|
||||||
|
return render_to_response(template, context)
|
||||||
|
|
||||||
|
|
|
@ -44,15 +44,16 @@
|
||||||
],
|
],
|
||||||
// fixme: either this, or filter: true in itemKeys, but not both
|
// fixme: either this, or filter: true in itemKeys, but not both
|
||||||
"filters": [
|
"filters": [
|
||||||
{"id": "collection", "title": "Collection", "type": "string"},
|
{"id": "source", "title": "Sources", "type": "string"},
|
||||||
{"id": "source", "title": "Source", "type": "string"},
|
{"id": "project", "title": "Projects", "type": "string"},
|
||||||
{"id": "director", "title": "Director", "type": "string"},
|
{"id": "topics", "title": "Topics", "type": "string"},
|
||||||
{"id": "cinematographer", "title": "Cinematographer", "type": "string"},
|
{"id": "name", "title": "People", "type": "string"},
|
||||||
|
{"id": "keywords", "title": "Keywords", "type": "string"},
|
||||||
|
{"id": "language", "title": "Languages", "type": "string"},
|
||||||
{"id": "places", "title": "Places", "type": "string"},
|
{"id": "places", "title": "Places", "type": "string"},
|
||||||
{"id": "year", "title": "Year", "type": "integer"},
|
{"id": "year", "title": "Years", "type": "integer"},
|
||||||
{"id": "language", "title": "Language", "type": "string"},
|
{"id": "director", "title": "Directors", "type": "string"},
|
||||||
{"id": "category", "title": "Category", "type": "string"},
|
{"id": "cinematographer", "title": "Cinematographers", "type": "string"}
|
||||||
{"id": "keywords", "title": "Keyword", "type": "string"}
|
|
||||||
],
|
],
|
||||||
/*
|
/*
|
||||||
An itemKey must have the following properties:
|
An itemKey must have the following properties:
|
||||||
|
@ -114,6 +115,17 @@
|
||||||
"find": true,
|
"find": true,
|
||||||
"sort": "person"
|
"sort": "person"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "features",
|
||||||
|
"title": "Features",
|
||||||
|
"type": ["string"],
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnRequired": true,
|
||||||
|
"columnWidth": 180,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": "person"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "name",
|
"id": "name",
|
||||||
"title": "Name",
|
"title": "Name",
|
||||||
|
@ -122,8 +134,8 @@
|
||||||
"find": true
|
"find": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "collection",
|
"id": "project",
|
||||||
"title": "Collection",
|
"title": "Project",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"autocomplete": true,
|
"autocomplete": true,
|
||||||
"columnWidth": 120,
|
"columnWidth": 120,
|
||||||
|
@ -165,8 +177,8 @@
|
||||||
"find": true
|
"find": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "category",
|
"id": "topic",
|
||||||
"title": "Category",
|
"title": "Topic",
|
||||||
"type": ["string"],
|
"type": ["string"],
|
||||||
"autocomplete": true,
|
"autocomplete": true,
|
||||||
"columnWidth": 180,
|
"columnWidth": 180,
|
||||||
|
@ -432,7 +444,7 @@
|
||||||
{"id": "clips", "title": "with Clips"},
|
{"id": "clips", "title": "with Clips"},
|
||||||
{"id": "timelines", "title": "with Timelines"},
|
{"id": "timelines", "title": "with Timelines"},
|
||||||
{"id": "maps", "title": "with Maps"},
|
{"id": "maps", "title": "with Maps"},
|
||||||
{"id": "calendars", "title": "with Calendars"},
|
//{"id": "calendars", "title": "with Calendars"},
|
||||||
{"id": "clip", "title": "as Clips"},
|
{"id": "clip", "title": "as Clips"},
|
||||||
//{"id": "video", "title": "as Video"},
|
//{"id": "video", "title": "as Video"},
|
||||||
{"id": "map", "title": "on Map"},
|
{"id": "map", "title": "on Map"},
|
||||||
|
@ -453,6 +465,7 @@
|
||||||
],
|
],
|
||||||
"sendReferrer": true,
|
"sendReferrer": true,
|
||||||
"site": {
|
"site": {
|
||||||
|
"description": "Pad.ma is an online archive of densely text-annotated video material, primarily footage and not finished films. The entire collection is searchable and viewable online, and is free to download for non-commercial use.",
|
||||||
"email": {
|
"email": {
|
||||||
// E-mail address in contact form (to)
|
// E-mail address in contact form (to)
|
||||||
"contact": "pad.ma@pad.ma",
|
"contact": "pad.ma@pad.ma",
|
||||||
|
@ -496,14 +509,15 @@
|
||||||
"clipsColumns": 2,
|
"clipsColumns": 2,
|
||||||
"columns": {
|
"columns": {
|
||||||
"Colors": {
|
"Colors": {
|
||||||
"columns": ["title", "director", "location", "collection", "hue", "saturation", "brightness"],
|
"columns": ["title", "director", "location", "source", "hue", "saturation", "brightness"],
|
||||||
"columnWidth": {}
|
"columnWidth": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filters": [
|
"filters": [
|
||||||
{"id": "collection", "sort": [{"key": "name", "operator": "+"}]},
|
|
||||||
{"id": "source", "sort": [{"key": "name", "operator": "+"}]},
|
{"id": "source", "sort": [{"key": "name", "operator": "+"}]},
|
||||||
{"id": "category", "sort": [{"key": "items", "operator": "-"}]},
|
{"id": "project", "sort": [{"key": "name", "operator": "+"}]},
|
||||||
|
{"id": "topic", "sort": [{"key": "items", "operator": "-"}]},
|
||||||
|
{"id": "people", "sort": [{"key": "items", "operator": "-"}]},
|
||||||
{"id": "keywords", "sort": [{"key": "items", "operator": "-"}]},
|
{"id": "keywords", "sort": [{"key": "items", "operator": "-"}]},
|
||||||
{"id": "places", "sort": [{"key": "items", "operator": "-"}]}
|
{"id": "places", "sort": [{"key": "items", "operator": "-"}]}
|
||||||
],
|
],
|
||||||
|
@ -515,7 +529,7 @@
|
||||||
"itemFind": {"conditions": [], "operator": "&"},
|
"itemFind": {"conditions": [], "operator": "&"},
|
||||||
"itemSort": [{"key": "position", "operator": "+"}],
|
"itemSort": [{"key": "position", "operator": "+"}],
|
||||||
"itemView": "info",
|
"itemView": "info",
|
||||||
"listColumns": ["title", "director", "location", "collection", "language", "duration", "source"],
|
"listColumns": ["title", "director", "location", "source", "language", "duration", "source"],
|
||||||
"listColumnWidth": {},
|
"listColumnWidth": {},
|
||||||
"listSelection": [],
|
"listSelection": [],
|
||||||
"listSort": [{"key": "title", "operator": "+"}],
|
"listSort": [{"key": "title", "operator": "+"}],
|
||||||
|
|
|
@ -15,6 +15,20 @@
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="/static/oxjs/build/Ox.js"></script>
|
<script type="text/javascript" src="/static/oxjs/build/Ox.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/pandora.js"></script>
|
<script type="text/javascript" src="/static/js/pandora.js"></script>
|
||||||
|
<meta name="title" content="{{title}}" />
|
||||||
|
<meta name="description" content="{{settings.CONFIG.site.description|safe}}"/>
|
||||||
|
<meta property="og:title" content="{{title}}"/>
|
||||||
|
<meta property="og:type" content="website"/>
|
||||||
|
<meta property="og:url" content="{{base_url}}"/>
|
||||||
|
<meta property="og:image" content="{{base_url}}static/png/icon64.png"/>
|
||||||
|
<meta property="og:site_name" content="{{settings.SITENAME}}"/>
|
||||||
|
<meta property="og:description" content="{{settings.CONFIG.site.description|safe}}"/>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<h1>{{settings.SITENAME}}</h1>
|
||||||
|
<p>{{settings.CONFIG.site.description|safe}}</p>
|
||||||
|
<p>{{settings.SITENAME}} requires JavaScript</p>
|
||||||
|
</noscript>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
47
pandora/templates/item.html
Normal file
47
pandora/templates/item.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>{{settings.SITENAME}} - {{title}}</title>
|
||||||
|
<link rel="shortcut icon" type="image/png" href="/static/png/icon16.png"/>
|
||||||
|
<link rel="icon" href="/static/png/icon64.png" sizes="32x32"/>
|
||||||
|
<link rel="icon" href="/static/png/icon64.png" sizes="48x48"/>
|
||||||
|
<meta name="application-name" content="{{settings.SITENAME}}"/>
|
||||||
|
<meta name="application-url" content="{{base_url}}"/>
|
||||||
|
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="{{settings.SITENAME}}" />
|
||||||
|
<script>
|
||||||
|
if (localStorage && !localStorage['Ox.theme'])
|
||||||
|
localStorage['Ox.theme'] = '"{{settings.CONFIG.user.ui.theme}}"';
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/static/oxjs/build/Ox.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/pandora.js"></script>
|
||||||
|
<meta name="title" content="{{title}}" />
|
||||||
|
<meta name="description" content="{{description|safe}}"/>
|
||||||
|
<meta name="keywords" content="{{keywords|safe}}"/>
|
||||||
|
<meta content="{{url}}/{{icon}}128.jpg" name="thumbnail"/>
|
||||||
|
<meta content="{{url}}/{{icon}}128.jpg" name="image_src"/>
|
||||||
|
<meta property="og:title" content="{{title}}"/>
|
||||||
|
<meta property="og:type" content="movie"/>
|
||||||
|
<meta property="og:url" content="{{url}}"/>
|
||||||
|
<meta property="og:image" content="{{url}}/{{icon}}128.jpg"/>
|
||||||
|
<meta property="og:site_name" content="{{settings.SITENAME}}"/>
|
||||||
|
<meta property="og:description" content="{{description|safe}}"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<img src="/{{id}}/poster256.jpg" style="float: left; margin-right: 8px">
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
{% for i in data %}
|
||||||
|
<div>
|
||||||
|
<b>{{i.key}}</b>: {{i.value|safe}}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% for c in clips %}
|
||||||
|
<div style="clear:both;padding-top: 4px">
|
||||||
|
<img src="/{{id}}/96p{{c.in}}.jpg" style="float: left; margin-right: 8px">
|
||||||
|
{{c.annotations|safe}}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</noscript>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -46,7 +46,7 @@ urlpatterns += patterns('',
|
||||||
)
|
)
|
||||||
urlpatterns += patterns('',
|
urlpatterns += patterns('',
|
||||||
(r'^(?P<id>[A-Z0-9].+)/embed', 'app.views.embed'),
|
(r'^(?P<id>[A-Z0-9].+)/embed', 'app.views.embed'),
|
||||||
(r'^[A-Z0-9].*$', 'app.views.index'),
|
(r'^(?P<id>[A-Z0-9].*)$', 'item.views.item'),
|
||||||
(r'^[a-z0-9].+$', 'app.views.index'),
|
(r'^[a-z0-9].+$', 'app.views.index'),
|
||||||
(r'^$', 'app.views.index'),
|
(r'^$', 'app.views.index'),
|
||||||
(r'^.*$', 'app.views.index'),
|
(r'^.*$', 'app.views.index'),
|
||||||
|
|
|
@ -146,9 +146,6 @@ pandora.ui.infoView = function(data) {
|
||||||
.append(
|
.append(
|
||||||
Ox.Editable({
|
Ox.Editable({
|
||||||
editable: isEditable,
|
editable: isEditable,
|
||||||
format: function(value) {
|
|
||||||
return formatTitle(value);
|
|
||||||
},
|
|
||||||
tooltip: isEditable ? 'Doubleclick to edit' : '',
|
tooltip: isEditable ? 'Doubleclick to edit' : '',
|
||||||
value: data.title
|
value: data.title
|
||||||
})
|
})
|
||||||
|
@ -267,16 +264,17 @@ pandora.ui.infoView = function(data) {
|
||||||
)
|
)
|
||||||
.appendTo($text);
|
.appendTo($text);
|
||||||
|
|
||||||
var list_keys = ['language', 'category', 'director', 'cinematographer'];
|
var list_keys = ['language', 'category', 'director', 'cinematographer', 'features'];
|
||||||
$('<div>').html('<br>').appendTo($text);
|
$('<div>').html('<br>').appendTo($text);
|
||||||
[
|
[
|
||||||
'date',
|
'date',
|
||||||
'location',
|
'location',
|
||||||
'director',
|
'director',
|
||||||
'cinematographer',
|
'cinematographer',
|
||||||
|
'features',
|
||||||
'language',
|
'language',
|
||||||
'source',
|
'source',
|
||||||
'collection',
|
'project',
|
||||||
'category',
|
'category',
|
||||||
'user',
|
'user',
|
||||||
].forEach(function(key) {
|
].forEach(function(key) {
|
||||||
|
@ -388,7 +386,7 @@ pandora.ui.infoView = function(data) {
|
||||||
if (value != data[key]) {
|
if (value != data[key]) {
|
||||||
var edit = {id: data.id};
|
var edit = {id: data.id};
|
||||||
if (key == 'title') {
|
if (key == 'title') {
|
||||||
Ox.extend(edit, parseTitle(value));
|
edit[key] = value;
|
||||||
} else if(['director', 'country', 'language', 'category'].indexOf(key) > -1) {
|
} else if(['director', 'country', 'language', 'category'].indexOf(key) > -1) {
|
||||||
edit[key] = value ? value.split(', ') : [];
|
edit[key] = value ? value.split(', ') : [];
|
||||||
} else {
|
} else {
|
||||||
|
@ -420,17 +418,6 @@ pandora.ui.infoView = function(data) {
|
||||||
return '<span style="color: rgb(128, 128, 128)">' + str + '</span>';
|
return '<span style="color: rgb(128, 128, 128)">' + str + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTitle(title) {
|
|
||||||
var match = /(\(S\d{2}E\d{2}\))/.exec(title);
|
|
||||||
if (match) {
|
|
||||||
title = title.replace(match[0], formatLight(match[0]));
|
|
||||||
}
|
|
||||||
return title + (
|
|
||||||
data.originalTitle && data.originalTitle != title
|
|
||||||
? ' ' + formatLight('(' + data.originalTitle + ')') : ''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatValue(value, key) {
|
function formatValue(value, key) {
|
||||||
return (Ox.isArray(value) ? value : [value]).map(function(value) {
|
return (Ox.isArray(value) ? value : [value]).map(function(value) {
|
||||||
return key ?
|
return key ?
|
||||||
|
@ -448,20 +435,6 @@ pandora.ui.infoView = function(data) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(title) {
|
|
||||||
var data = {title: title},
|
|
||||||
match = /(\(S(\d{2})E(\d{2})\))/.exec(title),
|
|
||||||
split;
|
|
||||||
if (match) {
|
|
||||||
data.season = parseInt(match[2], 10);
|
|
||||||
data.episode = parseInt(match[3], 10);
|
|
||||||
split = title.split(match[1]);
|
|
||||||
data.seriesTitle = split[0].trim();
|
|
||||||
data.episodeTitle = split[1].trim();
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadMetadata() {
|
function reloadMetadata() {
|
||||||
var item = ui.item;
|
var item = ui.item;
|
||||||
// fixme: maybe there's a better method name for this?
|
// fixme: maybe there's a better method name for this?
|
||||||
|
|
Loading…
Reference in a new issue