output some metadata for search engines, cleanup padma metadata

This commit is contained in:
j 2012-01-09 14:36:35 +05:30
parent d6b9929052
commit 06741903ba
8 changed files with 158 additions and 52 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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": "+"}],

View file

@ -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>

View 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>

View file

@ -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'),

View file

@ -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?