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,
|
||||
"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
|
||||
"email": {
|
||||
// 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.decorators import login_required_json
|
||||
|
||||
import ox
|
||||
from ox.utils import json
|
||||
|
||||
import models
|
||||
|
@ -31,7 +32,7 @@ def intro(request):
|
|||
def index(request):
|
||||
context = RequestContext(request, {
|
||||
'base_url': request.build_absolute_uri('/'),
|
||||
'settings': settings
|
||||
'settings': settings,
|
||||
})
|
||||
return render_to_response('index.html', context)
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@ import random
|
|||
|
||||
import Image
|
||||
from django.db.models import Count, Sum, Max
|
||||
from django.template import RequestContext
|
||||
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 ox.utils import json
|
||||
|
@ -807,3 +808,58 @@ def random_annotation(request):
|
|||
clip = item.annotations.all()[pos]
|
||||
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
|
||||
"filters": [
|
||||
{"id": "collection", "title": "Collection", "type": "string"},
|
||||
{"id": "source", "title": "Source", "type": "string"},
|
||||
{"id": "director", "title": "Director", "type": "string"},
|
||||
{"id": "cinematographer", "title": "Cinematographer", "type": "string"},
|
||||
{"id": "source", "title": "Sources", "type": "string"},
|
||||
{"id": "project", "title": "Projects", "type": "string"},
|
||||
{"id": "topics", "title": "Topics", "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": "year", "title": "Year", "type": "integer"},
|
||||
{"id": "language", "title": "Language", "type": "string"},
|
||||
{"id": "category", "title": "Category", "type": "string"},
|
||||
{"id": "keywords", "title": "Keyword", "type": "string"}
|
||||
{"id": "year", "title": "Years", "type": "integer"},
|
||||
{"id": "director", "title": "Directors", "type": "string"},
|
||||
{"id": "cinematographer", "title": "Cinematographers", "type": "string"}
|
||||
],
|
||||
/*
|
||||
An itemKey must have the following properties:
|
||||
|
@ -114,6 +115,17 @@
|
|||
"find": true,
|
||||
"sort": "person"
|
||||
},
|
||||
{
|
||||
"id": "features",
|
||||
"title": "Features",
|
||||
"type": ["string"],
|
||||
"autocomplete": true,
|
||||
"columnRequired": true,
|
||||
"columnWidth": 180,
|
||||
"filter": true,
|
||||
"find": true,
|
||||
"sort": "person"
|
||||
},
|
||||
{
|
||||
"id": "name",
|
||||
"title": "Name",
|
||||
|
@ -122,8 +134,8 @@
|
|||
"find": true
|
||||
},
|
||||
{
|
||||
"id": "collection",
|
||||
"title": "Collection",
|
||||
"id": "project",
|
||||
"title": "Project",
|
||||
"type": "string",
|
||||
"autocomplete": true,
|
||||
"columnWidth": 120,
|
||||
|
@ -165,8 +177,8 @@
|
|||
"find": true
|
||||
},
|
||||
{
|
||||
"id": "category",
|
||||
"title": "Category",
|
||||
"id": "topic",
|
||||
"title": "Topic",
|
||||
"type": ["string"],
|
||||
"autocomplete": true,
|
||||
"columnWidth": 180,
|
||||
|
@ -432,7 +444,7 @@
|
|||
{"id": "clips", "title": "with Clips"},
|
||||
{"id": "timelines", "title": "with Timelines"},
|
||||
{"id": "maps", "title": "with Maps"},
|
||||
{"id": "calendars", "title": "with Calendars"},
|
||||
//{"id": "calendars", "title": "with Calendars"},
|
||||
{"id": "clip", "title": "as Clips"},
|
||||
//{"id": "video", "title": "as Video"},
|
||||
{"id": "map", "title": "on Map"},
|
||||
|
@ -453,6 +465,7 @@
|
|||
],
|
||||
"sendReferrer": true,
|
||||
"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": {
|
||||
// E-mail address in contact form (to)
|
||||
"contact": "pad.ma@pad.ma",
|
||||
|
@ -496,14 +509,15 @@
|
|||
"clipsColumns": 2,
|
||||
"columns": {
|
||||
"Colors": {
|
||||
"columns": ["title", "director", "location", "collection", "hue", "saturation", "brightness"],
|
||||
"columns": ["title", "director", "location", "source", "hue", "saturation", "brightness"],
|
||||
"columnWidth": {}
|
||||
}
|
||||
},
|
||||
"filters": [
|
||||
{"id": "collection", "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": "places", "sort": [{"key": "items", "operator": "-"}]}
|
||||
],
|
||||
|
@ -515,7 +529,7 @@
|
|||
"itemFind": {"conditions": [], "operator": "&"},
|
||||
"itemSort": [{"key": "position", "operator": "+"}],
|
||||
"itemView": "info",
|
||||
"listColumns": ["title", "director", "location", "collection", "language", "duration", "source"],
|
||||
"listColumns": ["title", "director", "location", "source", "language", "duration", "source"],
|
||||
"listColumnWidth": {},
|
||||
"listSelection": [],
|
||||
"listSort": [{"key": "title", "operator": "+"}],
|
||||
|
|
|
@ -15,6 +15,20 @@
|
|||
</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="{{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>
|
||||
<body></body>
|
||||
<body>
|
||||
<noscript>
|
||||
<h1>{{settings.SITENAME}}</h1>
|
||||
<p>{{settings.CONFIG.site.description|safe}}</p>
|
||||
<p>{{settings.SITENAME}} requires JavaScript</p>
|
||||
</noscript>
|
||||
</body>
|
||||
</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('',
|
||||
(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'^$', 'app.views.index'),
|
||||
(r'^.*$', 'app.views.index'),
|
||||
|
|
|
@ -146,9 +146,6 @@ pandora.ui.infoView = function(data) {
|
|||
.append(
|
||||
Ox.Editable({
|
||||
editable: isEditable,
|
||||
format: function(value) {
|
||||
return formatTitle(value);
|
||||
},
|
||||
tooltip: isEditable ? 'Doubleclick to edit' : '',
|
||||
value: data.title
|
||||
})
|
||||
|
@ -267,16 +264,17 @@ pandora.ui.infoView = function(data) {
|
|||
)
|
||||
.appendTo($text);
|
||||
|
||||
var list_keys = ['language', 'category', 'director', 'cinematographer'];
|
||||
var list_keys = ['language', 'category', 'director', 'cinematographer', 'features'];
|
||||
$('<div>').html('<br>').appendTo($text);
|
||||
[
|
||||
'date',
|
||||
'location',
|
||||
'director',
|
||||
'cinematographer',
|
||||
'features',
|
||||
'language',
|
||||
'source',
|
||||
'collection',
|
||||
'project',
|
||||
'category',
|
||||
'user',
|
||||
].forEach(function(key) {
|
||||
|
@ -388,7 +386,7 @@ pandora.ui.infoView = function(data) {
|
|||
if (value != data[key]) {
|
||||
var edit = {id: data.id};
|
||||
if (key == 'title') {
|
||||
Ox.extend(edit, parseTitle(value));
|
||||
edit[key] = value;
|
||||
} else if(['director', 'country', 'language', 'category'].indexOf(key) > -1) {
|
||||
edit[key] = value ? value.split(', ') : [];
|
||||
} else {
|
||||
|
@ -420,17 +418,6 @@ pandora.ui.infoView = function(data) {
|
|||
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) {
|
||||
return (Ox.isArray(value) ? value : [value]).map(function(value) {
|
||||
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() {
|
||||
var item = ui.item;
|
||||
// fixme: maybe there's a better method name for this?
|
||||
|
|
Loading…
Reference in a new issue