padma.local -- toward a presentation tool

This commit is contained in:
sanj 2010-07-13 00:39:02 +05:30
parent 32d6263392
commit 67dc10565f
7 changed files with 251 additions and 179 deletions

View File

@ -2,6 +2,7 @@ import urllib
from django.utils import simplejson from django.utils import simplejson
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from settings import PADMA_URL
def getHtmlFromUrl(url): def getHtmlFromUrl(url):
try: try:
@ -20,6 +21,16 @@ def fetchJson(request):
else: else:
return HttpResponse("{'error': True}", mimetype="application/javascript") return HttpResponse("{'error': True}", mimetype="application/javascript")
def fetchSrt(request):
if request.GET['id']:
padmaId = request.GET['id']
track = request.GET['track'] or 'transcript'
url = "%s%s/export/%s.srt" % (PADMA_URL, padmaId, track,)
srt = getHtmlFromUrl(url)
return HttpResponse(srt)
else:
return HttpResponse("{'error': True}", mimetype="application/javascript")
def index(request): def index(request):
return render_to_response("index.html") return render_to_response("index.html")
@ -28,4 +39,6 @@ def listDetail(request):
rDict = {'listId': request.GET['id'] } rDict = {'listId': request.GET['id'] }
return render_to_response("list.html", rDict) return render_to_response("list.html", rDict)
else: else:
return HttpResponse("Please pass me a list id:(") return HttpResponse("Please pass me a list id")

View File

@ -10,6 +10,7 @@ LOGGING_INTERCEPT_REDIRECTS = True
LOGGING_LOG_SQL = True LOGGING_LOG_SQL = True
LOGGING_SHOW_METRICS = True LOGGING_SHOW_METRICS = True
LOGGING_OUTPUT_ENABLED = True LOGGING_OUTPUT_ENABLED = True
PADMA_URL = "http://padma.local/"
PROJECT_PATH = os.path.dirname(__file__) PROJECT_PATH = os.path.dirname(__file__)

View File

@ -1,16 +1,78 @@
#wrapper { html, body {
width: 100%;
font-size: 12px;
}
#wrapper, #floatWrapper {
width: 100%; width: 100%;
} }
#videos {
width: 40%;
}
.layerResult {
margin-bottom: 2px;
font-size: 10px;
}
.layerResult.keyword {
background: orange;
}
.layerResult.description {
background: blue;
}
.layerResult.transcript {
background: green;
}
.layerResult.location {
background: purple;
}
.layerResult a {
color: white;
font-weight: bold;
text-decoration: none;
}
.layerResult a:hover {
color: lightgrey;
}
.layerInOut {
color: yellow;
font-weight: bold;
}
.layerTrack {
font-weight: bold;
}
.videoWrapper { .videoWrapper {
border: 1px solid #000; border: 1px solid #000;
width: 80%; width: 100%;
min-height: 180px; min-height: 180px;
/*
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
*/
margin-bottom: 15px; margin-bottom: 15px;
} }
#playBin {
position: fixed;
height: 96%;
overflow: auto;
left: 45%;
top: 2%;
bottom: 2%;
right: 5%;
border: 1px solid #000;
height: 500px;
}
.videoTitle { .videoTitle {
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
@ -21,7 +83,7 @@
} }
.videoMeta { .videoMeta {
clear: left; /* clear: left; */
} }
.videoPoster { .videoPoster {
@ -40,6 +102,32 @@
display: none; display: none;
} }
.padmaClip {
/* height: 128px; */
padding: 5px;
margin-bottom: 2px;
background: #ccc;
position: relative;
}
.layerImages {
float: left;
}
.layerControls {
float: left;
}
.clipImage {
float: left;
width: 128px;
text-align: center;
color: #363636;
font-weight: bold;
margin-right: 6px;
height: 100px;
}
#searchDiv { #searchDiv {
margin-right: 20px; margin-right: 20px;
text-align: right; text-align: right;
@ -62,3 +150,7 @@
color: #ccc; color: #ccc;
clear: both; clear: both;
} }
#results {
z-index: 500;
}

View File

@ -1,8 +1,93 @@
var local_json_url = "/jPadma?url=http://pad.ma"; //REMOVE tmpl FROM HERE!!!
/*
John Resig's templating utility - http://ejohn.org/blog/javascript-micro-templating/
Not updated code to resolve issue with single quotes from: http://www.west-wind.com/Weblog/posts/509108.aspx
define templates inside <script type="text/html" id="foo"> tags
get html of template with tmpl("foo", json)
*/
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {};
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();
// window.PADMA = new padmaApi();
function callPadma(api, callback, extra_params) { function callPadma(api, callback, extra_params) {
var local_json_url = "/jPadma?url=http://padma.local";
var url = local_json_url + api; var url = local_json_url + api;
$.getJSON(url, function(json) { $.getJSON(url, function(json) {
callback(json, extra_params); callback(json, extra_params);
}); });
} }
function getSRT(id, track, callback) {
var url = local_srt_url;
$.get(url, {
'id': id,
'track': track
}, function(srt) {
callback(srt);
});
}
(function($) {
jQuery.fn.padma = function(id, options) {
var that = this;
var o = $.extend({
'videoSize': 320,
'startTime': '0:00:00',
'endTime': '0:00:00',
'tracks': ['description', 'transcript'],
'tmpl_id': '',
'baseUrl': 'http://pad.ma/',
'globalVar': 'PADMA',
'localUrl': '/padmaApi/'
}, options);
window[o.globalVar] = new P(id, o, that);
}
var P = function(id, options, container) {
this.o = options;
this.container = container;
this.init();
}
P.prototype.init = function() {
}
})(jQuery);

View File

@ -1,182 +1,20 @@
var BASE_URL = "http://padma.local/";
$(document).ready(function() { $(document).ready(function() {
var apiUrl = "/list/videos?listId=" + listId; var apiUrl = "/list/videos?listId=" + listId;
callPadma(apiUrl, initList); callPadma(apiUrl, initList);
prevSearchTerms = []; prevSearchTerms = [];
$('#search').keyup(function() {
var val = $(this).val();
if (val == '') {
$('#results').html('');
}
var words = val.split(" ");
for (var i = 0; i < words.length; i++) {
var thisWord = words[i];
if (!prevSearchTerms.inArray(thisWord) && thisWord.length > 3) {
doSearch(thisWord);
prevSearchTerms.push(thisWord);
}
}
});
}); });
function doSearch(word) {
var matchingLayers = findMatchingLayers(word);
for (var l in matchingLayers) {
var layer = matchingLayers[l];
var imgPath = layer.firstFrame128;
var html = layer.getThumbElem();
// var imgHtml = "<img src='" + imgPath + "' />";
$('#results').append(html);
}
}
function findMatchingLayers(word) {
var matchedLayers = [];
for (var v in padmaVideos) {
var layers = padmaVideos[v].layers;
for (var l in layers) {
if (layers[l].value) {
var val = layers[l].value;
if (val.indexOf(word) != -1) {
var matchedLayer = new padmaLayer(layers[l], v)
matchedLayers.push(matchedLayer);
}
}
}
}
return matchedLayers;
}
Array.prototype.inArray = function(value) {
// Returns true if the passed value is found in the
// array. Returns false if it is not.
var i;
for (i=0; i < this.length; i++) {
if (this[i] == value) {
return true;
}
}
return false;
};
var padmaLayer = function(json, videoIndex) {
this.id = json.id;
this.video = videoIndex;
this.time_in = json.time_in;
this.time_out = json.time_out;
this.track = json.track;
this.value = json.value;
this.value_html = json.value_html;
this.creator = json.creator;
var that = this;
this.firstFrameStr = "http://pad.ma/" + padmaVideos[this.video].id + "/frame/" + ms2npt(that.time_in);
this.firstFrame128 = this.firstFrameStr + ".128.jpg";
this.firstFrame320 = this.firstFrameStr + ".320.jpg";
this.lastFrameStr = "http://pad.ma/" + padmaVideos[this.video].id + "/frame/" + ms2npt(that.time_out);
this.lastFrame128 = this.lastFrameStr + ".128.jpg";
this.lastFrame320 = this.lastFrameStr + ".320.jpg";
}
padmaLayer.prototype.getThumbElem = function() {
var that = this;
var e = $('<div />');
e.addClass('matchedLayer');
e.attr("id", that.id);
var img = $('<img />');
if (that.firstFrame128) {
img.attr("src", that.firstFrame128);
e.append(img);
}
e.attr("data-track", that.track);
e.attr("data-value", that.value_html);
e.mouseover(function() {
var html = '';
html += padmaVideos[that.video].title;
html += "<br />";
html += $(this).attr('data-track');
html += '<br />';
html += $(this).attr('data-value');
$('#resultDetails').html(html);
}).mouseout(function() {
$('#resultDetails').html('');
});
return e;
}
var padmaVideo = function(json, index) {
this.id = json.id;
this.index = index;
this.title = json.title;
this.poster = "http://pad.ma/" + this.id + "/poster.jpg";
this.timeline = "http://pad.ma/" + this.id + "/timeline.png";
this.wrapperHtml = this.getWrapperHtml()
this.init();
this.dataLoaded = false;
}
padmaVideo.prototype.getWrapperHtml = function() {
var that = this;
var videoElem = $('<div />');
videoElem.attr('id', that.id);
videoElem.addClass("videoWrapper");
var html = '';
html += "<div class='videoTitle'>";
html += this.title;
html += "</div>";
html += "<div class='videoPoster'>";
html += "<img src='" + this.poster + "' />";
html += "</div>";
html += "<div class='videoTimeline' data-index='" + this.index + "'>";
html += "<img src='" + this.timeline + "' class='timeline' />";
html += "</div>";
html += "<div class='videoMeta'>";
html += "Loading...";
html += "</div>";
videoElem.html(html);
return videoElem;
}
padmaVideo.prototype.init = function() {
var that = this;
$('#videos').append(that.wrapperHtml);
this.loadData();
}
padmaVideo.prototype.metaLoaded = function() {
var that = this;
$('#' + that.id + ' .videoMeta').html(that.meta.description_html);
}
padmaVideo.prototype.layersLoaded = function() {
this.dataLoaded = true;
$('#bottomBar').fadeIn();
/* $('.videoTimeline').mousemove(function(e) {
var offset = $(this).offset();
var x = e.pageX - offset.left;
var y = e.pageY - offset.top;
var index = $(this).attr('data-id');
var thisVideo = padmaVideos[index];
});
*/
}
padmaVideo.prototype.loadData = function() {
var that = this;
var metaUrl = "http://pad.ma/" + that.id + "/video.js";
$.getScript(metaUrl, function() {
that.meta = video;
that.metaLoaded();
var layersUrl = "http://pad.ma/" + that.id + "/layers.js";
$.getScript(layersUrl, function() {
that.layers = layers;
that.layersLoaded();
});
});
}
var padmaVideos = []; var padmaVideos = [];
function initList(json) { function initList(json) {
var videos = json.videos; var videos = json.videos;
for (v in videos) { for (var v=0; v < videos.length; v++) {
var thisVideo = new padmaVideo(videos[v], v); var thisVideo = new padmaVideo(videos[v], v);
padmaVideos.push(thisVideo); padmaVideos.push(thisVideo);
} }
@ -210,3 +48,4 @@ String.prototype.pad = function(pad, len, dir) {
return str; return str;
} }

View File

@ -1,3 +1,4 @@
var BASE_URL = "http://padma.local/"
var padmaLists = [] var padmaLists = []
$(document).ready(function() { $(document).ready(function() {
@ -8,7 +9,7 @@ var padmaList = function(json) {
this.listId = json.listId; this.listId = json.listId;
this.description = json.description; this.description = json.description;
this.iconId = json.iconId; this.iconId = json.iconId;
this.iconPath = "http://pad.ma/" + this.iconId + "/poster.jpg"; this.iconPath = BASE_URL + this.iconId + "/poster.jpg";
this.title = json.title; this.title = json.title;
this.html = this.getHtml(); this.html = this.getHtml();
this.init(); this.init();

View File

@ -1,8 +1,12 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block head %} {% block head %}
<title>Pad.ma: Play</title>
<link rel="stylesheet" type="text/css" href="/static/css/padmaList.css" /> <link rel="stylesheet" type="text/css" href="/static/css/padmaList.css" />
<script type="text/javascript" src="/static/js/padmaApi.js"></script> <script type="text/javascript" src="/static/js/padmaApi.js"></script>
<script type="text/javascript" src="/static/js/padmaList.js"></script> <script type="text/javascript" src="/static/js/padmaList.js"></script>
<script type="text/javascript" src="/static/js/padmaVideo.js"></script>
<script type="text/javascript" src="/static/js/padmaLayer.js"></script>
<script type="text/javascript" src="/static/js/padmaSearch.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var listId = "{{ listId|escapejs }}" var listId = "{{ listId|escapejs }}"
</script> </script>
@ -10,7 +14,7 @@ var listId = "{{ listId|escapejs }}"
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div id="wrapper"> <!--
<div id="resultWrapper"> <div id="resultWrapper">
<div id="results"> <div id="results">
@ -18,6 +22,41 @@ var listId = "{{ listId|escapejs }}"
<div id="resultDetails"> <div id="resultDetails">
</div> </div>
</div> </div>
-->
<script type="text/html" id="tmpl_layerResult">
<div class="layerTitle">
<span class="layerTrack"><%= layer.track %> </span>:
<span class="layerValue"><%= layer.shortValue %>... </span>
<span class="layerInOut"><%= layer.time_in_npt %> - <%= layer.time_out_npt %></span>
<span class="layerBtns">
<a href="#" class="addToClipBin" title="Add this clip to playlist.">&gt;&gt;</a>
</span>
</div>
</script>
<script type="text/html" id="tmpl_clip">
<div class="clipMain">
<div class="clipImages">
<div class="clipInImage clipImage">
<img src="<%= layer.firstFrame128 %>" title="In: <%= layer.value %>" /><br />
<%= layer.time_in_npt %>
</div>
<div class="clipOutImage clipImage">
<img src="<%= layer.lastFrame128 %>" title="Out: <%= layer.value %>" /><br />
<%= layer.time_out_npt %>
</div>
</div>
<div class="layerControls">
foo bar
</div>
</div>
</div>
</script>
<div id="wrapper">
<div id="playBin">
</div>
<div id="videos"> <div id="videos">
</div> </div>
@ -26,7 +65,9 @@ var listId = "{{ listId|escapejs }}"
</div> </div>
<div id="bottomBar"> <div id="bottomBar">
<div id="searchDiv"> <div id="searchDiv">
<form id="searchForm" action="" method="">
Search: <input type="text" id="search" /> Search: <input type="text" id="search" />
</form>
</div> </div>
</div> </div>