first commit
This commit is contained in:
commit
510feb01d3
4
chrome.manifest
Normal file
4
chrome.manifest
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
content speedtrans chrome/content/
|
||||||
|
overlay chrome://browser/content/browser.xul chrome://speedtrans/content/menu.xul
|
||||||
|
|
||||||
|
|
307
chrome/content/classes.js
Executable file
307
chrome/content/classes.js
Executable file
|
@ -0,0 +1,307 @@
|
||||||
|
//elem = string (elementId to make TextArea)
|
||||||
|
var TextArea = function(elem) {
|
||||||
|
this.elem = $('#' + elem);
|
||||||
|
this.hasFocus = false;
|
||||||
|
this.width = this.elem.width();
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
TextArea.prototype.init = function() {
|
||||||
|
var that = this;
|
||||||
|
var e = this.elem;
|
||||||
|
var tc;
|
||||||
|
e.focus(function() {
|
||||||
|
that.hasFocus = true;
|
||||||
|
}).blur(function() {
|
||||||
|
that.hasFocus = false;
|
||||||
|
});
|
||||||
|
e.dblclick(function() {
|
||||||
|
if (tc = that.isTc()) {
|
||||||
|
Video.set(tc);
|
||||||
|
} else {
|
||||||
|
that.insertTc();
|
||||||
|
}
|
||||||
|
Video.togglePause();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//returns tc in ms if cursor is at a time-code, else returns false
|
||||||
|
TextArea.prototype.isTc = function() {
|
||||||
|
var that = this;
|
||||||
|
var e = this.elem;
|
||||||
|
var eDom = e.get(0);
|
||||||
|
var val = e.val();
|
||||||
|
var pos = eDom.selectionStart;
|
||||||
|
var word = this.getWord(pos);
|
||||||
|
if (isValidTimecode(word)) {
|
||||||
|
return npt2ms(word);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//inserts current timecode at cursor position
|
||||||
|
TextArea.prototype.insertTc = function() {
|
||||||
|
var that = this;
|
||||||
|
var e = that.elem;
|
||||||
|
var eDom = e.get(0);
|
||||||
|
var scrollTop = eDom.scrollTop;
|
||||||
|
var val = this.elem.val();
|
||||||
|
var pos = eDom.selectionStart;
|
||||||
|
var tcNpt = ms2npt(Video.get());
|
||||||
|
var newVal = val.substring(0,pos) + "\n" + tcNpt + "\n" + val.substring(pos, val.length);
|
||||||
|
e.val(newVal);
|
||||||
|
e.focus();
|
||||||
|
eDom.selectionStart = pos + tcNpt.length + 1;
|
||||||
|
eDom.selectionEnd = pos + tcNpt.length + 1;
|
||||||
|
eDom.scrollTop = scrollTop + 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
//gets the word at character pos (int) [in val (str)]
|
||||||
|
TextArea.prototype.getWord = function(pos, val) {
|
||||||
|
if (!val) {
|
||||||
|
val = this.elem.val();
|
||||||
|
}
|
||||||
|
var c;
|
||||||
|
var i = pos;
|
||||||
|
var j = pos;
|
||||||
|
while (c != " " && c != "\n") {
|
||||||
|
if (i==0) {
|
||||||
|
i = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
c = val.substring(i,i+1);
|
||||||
|
}
|
||||||
|
var firstLetter = i+1;
|
||||||
|
var d;
|
||||||
|
while (d != " " && d != "\n") {
|
||||||
|
if (j >= val.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
d = val.substring(j,j+1);
|
||||||
|
}
|
||||||
|
var lastLetter = j;
|
||||||
|
var word = val.substring(firstLetter, lastLetter);
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
//takes an srt as param, loads into txtarea
|
||||||
|
TextArea.prototype.fromSrt = function(srt) {
|
||||||
|
var spans = [];
|
||||||
|
srt = srt.replace('\r\n|\r|\n', '\n')
|
||||||
|
srt = strip(srt);
|
||||||
|
var srt_ = srt.split('\n\n');
|
||||||
|
for(s in srt_) {
|
||||||
|
st = srt_[s].split('\n');
|
||||||
|
if(st.length >=2) {
|
||||||
|
var n = st[0];
|
||||||
|
var i = strip(st[1].split(' --> ')[0]);
|
||||||
|
var o = strip(st[1].split(' --> ')[1]);
|
||||||
|
var t = st[2];
|
||||||
|
if(st.length > 2) {
|
||||||
|
for(j=3; j<st.length;j++) {
|
||||||
|
t += '\n'+st[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var is = toSeconds(i);
|
||||||
|
var os = toSeconds(o);
|
||||||
|
spans[spans.length] = new Span(is, os, t, spans.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var out = '';
|
||||||
|
for (span in spans) {
|
||||||
|
if (spans.hasOwnProperty(span)) {
|
||||||
|
var sp = spans[span];
|
||||||
|
out += ms2npt(sp.tcInMs) + "\n";
|
||||||
|
out += sp.text;
|
||||||
|
out += "\n";
|
||||||
|
//If the outpoint of current span is equal to inpoint of next span, dont print out timecode, and just add the extra \n to go to next span.
|
||||||
|
if (span < spans.length - 1) {
|
||||||
|
var p = parseInt(span) + 1;
|
||||||
|
if (spans[p].tcInMs != sp.tcOutMs) {
|
||||||
|
out += ms2npt(sp.tcOutMs) + "\n\n";
|
||||||
|
} else {
|
||||||
|
out += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.elem.val(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function strip(s) {
|
||||||
|
return s.replace(/^\s+|\s+$/g,"");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSeconds(t) {
|
||||||
|
var s = 0.0;
|
||||||
|
if(t) {
|
||||||
|
var p = t.split(':');
|
||||||
|
for(i=0;i<p.length;i++) {
|
||||||
|
s = s * 60 + parseFloat(p[i].replace(',', '.'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function spansToSrt(arr, fmt, start_no) {
|
||||||
|
if (typeof start_no == 'undefined') {
|
||||||
|
start_no = 1;
|
||||||
|
}
|
||||||
|
var srt = '';
|
||||||
|
var srtNo = start_no;
|
||||||
|
for (var k=0; k < arr.length; k++) {
|
||||||
|
var s = arr[k];
|
||||||
|
if (s.text.trim() == '') {
|
||||||
|
} else {
|
||||||
|
var text = s.text.trim();
|
||||||
|
linebreaksRegex = new RegExp('\n+', "g")
|
||||||
|
text = text.replace(linebreaksRegex, "\n");
|
||||||
|
if (!s.tcOutMs) {
|
||||||
|
s.tcOutMs = parseInt(Video.player.duration * 1000);
|
||||||
|
}
|
||||||
|
if (fmt == 'srt') {
|
||||||
|
srt += srtNo + " ";
|
||||||
|
srt += "\n";
|
||||||
|
srt += "0" + ms2npt(s.tcInMs).replace(".", ",") + " --> " + "0" + ms2npt(s.tcOutMs).replace(".", ",");
|
||||||
|
srt += "\n";
|
||||||
|
srt += text;
|
||||||
|
srt += "\n\n";
|
||||||
|
}
|
||||||
|
else if (fmt == 'enc') {
|
||||||
|
srt += srtNo + ms2frames(s.tcInMs) + " " + ms2frames(s.tcOutMs) + " " + text;
|
||||||
|
srt += "\n\n";
|
||||||
|
}
|
||||||
|
srtNo++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return srt;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns textarea formatted to .srt
|
||||||
|
TextArea.prototype.toSrt = function(fmt) {
|
||||||
|
if (!fmt) var fmt = 'srt';
|
||||||
|
var text = this.elem.val();
|
||||||
|
var lines = [];
|
||||||
|
lines = text.split("\n");
|
||||||
|
var i=0;
|
||||||
|
var j=0;
|
||||||
|
spans = [];
|
||||||
|
while (i < lines.length) {
|
||||||
|
var l = lines[i];
|
||||||
|
if (isValidTimecode(l.trim())) {
|
||||||
|
var tcIn = l.trim();
|
||||||
|
var t = "";
|
||||||
|
var thisLine = '';
|
||||||
|
while (!isValidTimecode(thisLine.trim())) {
|
||||||
|
i++;
|
||||||
|
if (i >= lines.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thisLine = lines[i];
|
||||||
|
if (!isValidTimecode(thisLine.trim())) {
|
||||||
|
t += thisLine + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spans[j] = new Span(tcIn, thisLine, t, j);
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var srt = spansToSrt(spans, fmt);
|
||||||
|
// console.log(srt);
|
||||||
|
return srt;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Using spans as GLOBAL is ridiculous - please fix.
|
||||||
|
TextArea.prototype.addTime = function(ms, start_no) {
|
||||||
|
// console.log(ms);
|
||||||
|
if (typeof spans == 'undefined') {
|
||||||
|
this.toSrt();
|
||||||
|
}
|
||||||
|
var s = [];
|
||||||
|
for (var i=0; i<spans.length;i++) {
|
||||||
|
s[i] = {
|
||||||
|
index: i,
|
||||||
|
tcOutMs: spans[i].tcOutMs + ms,
|
||||||
|
text: spans[i].text,
|
||||||
|
tcInMs: spans[i].tcInMs + ms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spansToSrt(s, 'srt', start_no)
|
||||||
|
}
|
||||||
|
|
||||||
|
//creates new Span (tcIn and tcOut in npt format)
|
||||||
|
var Span = function(tcIn, tcOut, text, index) {
|
||||||
|
this.index = index;
|
||||||
|
this.tcOutMs = npt2ms(tcOut);
|
||||||
|
this.text = text;
|
||||||
|
this.tcInMs = npt2ms(tcIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
var SeekBar = function(elem) {
|
||||||
|
this.elem = $('#' + elem);
|
||||||
|
this.pointerId = "seekPointer";
|
||||||
|
this.width = this.elem.width();
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekBar.prototype.init = function() {
|
||||||
|
var that = this;
|
||||||
|
var e = $('<div />');
|
||||||
|
e.attr("id", that.pointerId);
|
||||||
|
e.draggable({
|
||||||
|
containment: 'parent',
|
||||||
|
axis: 'horizontally',
|
||||||
|
drag: function() {
|
||||||
|
clearInterval(videoListener);
|
||||||
|
var pos = that.get();
|
||||||
|
$('#timeCode').html(ms2npt(pos));
|
||||||
|
},
|
||||||
|
stop: function() {
|
||||||
|
var pos = that.get();
|
||||||
|
Video.set(pos);
|
||||||
|
videoListener = setInterval(Video.listener, 250);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
that.elem.append(e);
|
||||||
|
this.pointerElem = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
//gets current time-code (int) of seekbar position
|
||||||
|
SeekBar.prototype.get = function() {
|
||||||
|
var cssPos = parseInt(this.pointerElem.css("left"));
|
||||||
|
var pos = parseInt((cssPos / this.width) * (Video.player.duration * 1000));
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sets seek bar css pos according to current time-code
|
||||||
|
SeekBar.prototype.set = function(ms) {
|
||||||
|
var cssPos = parseInt(((ms / 1000) / Video.player.duration) * this.width);
|
||||||
|
this.elem.css("left", cssPos + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
var KeyboardController = function() {
|
||||||
|
this.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function loadSrt(filename) {
|
||||||
|
// alert("hmm.. you have a .srt file but not a txt file - this is a bug. sorry.");
|
||||||
|
var txt = mozillaLoadFile(filename);
|
||||||
|
textArea.fromSrt(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadMeta(filename) {
|
||||||
|
var m = mozillaLoadFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSrtTxt(filename) {
|
||||||
|
var txt = mozillaLoadFile(filename);
|
||||||
|
$('#txt').val(txt);
|
||||||
|
}
|
556
chrome/content/jquery-ui.js
vendored
Executable file
556
chrome/content/jquery-ui.js
vendored
Executable file
File diff suppressed because one or more lines are too long
3549
chrome/content/jquery.js
vendored
Executable file
3549
chrome/content/jquery.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
21
chrome/content/menu.js
Normal file
21
chrome/content/menu.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
var padma={
|
||||||
|
openAnnot:function()
|
||||||
|
{
|
||||||
|
window.open("chrome://speedtrans/content/transcribe.html");
|
||||||
|
/*
|
||||||
|
window.open("chrome://speedtrans/content/transcribe.html",
|
||||||
|
"annot", "chrome,centerscreen,resizable=yes,toolbar=no,menubar=no");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMainWindow() {
|
||||||
|
return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Components.interfaces.nsIWebNavigation)
|
||||||
|
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
|
||||||
|
.rootTreeItem
|
||||||
|
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
15
chrome/content/menu.xul
Normal file
15
chrome/content/menu.xul
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<overlay id="annot"
|
||||||
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||||
|
|
||||||
|
<script type="application/x-javascript"
|
||||||
|
src="chrome://speedtrans/content/menu.js" />
|
||||||
|
|
||||||
|
<menupopup id="menu_ToolsPopup">
|
||||||
|
<menuitem id="annot_menu" image="chrome://speedtrans/content/icon.png"
|
||||||
|
class="menuitem-iconic"
|
||||||
|
insertafter="javascriptConsole,devToolsSeparator"
|
||||||
|
label="Pad.ma Transcription Client"
|
||||||
|
oncommand="padma.openAnnot();"/>
|
||||||
|
</menupopup>
|
||||||
|
</overlay>
|
116
chrome/content/player.js
Executable file
116
chrome/content/player.js
Executable file
|
@ -0,0 +1,116 @@
|
||||||
|
function Player() {
|
||||||
|
this.supportsOverlay = false;
|
||||||
|
this.muted = false;
|
||||||
|
}
|
||||||
|
Player.prototype.init = function(elemID) {
|
||||||
|
//make overlay settings happy..., add a dummy element
|
||||||
|
this.player = document.getElementById(elemID);
|
||||||
|
}
|
||||||
|
Player.prototype.play = function() { }
|
||||||
|
Player.prototype.pause = function() { }
|
||||||
|
Player.prototype.get = function() { }
|
||||||
|
Player.prototype.listener = function() { }
|
||||||
|
Player.prototype.set = function(pos) { }
|
||||||
|
Player.prototype.mute = function(pos) { }
|
||||||
|
|
||||||
|
Player.prototype.seekFwd = function(ms) {
|
||||||
|
var currentMs = this.get();
|
||||||
|
var newMs = currentMs + ms;
|
||||||
|
this.set(newMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.prototype.seekBack = function(ms) {
|
||||||
|
var currentMs = this.get();
|
||||||
|
var newMs = currentMs - ms;
|
||||||
|
this.set(newMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.prototype.unmute = function(pos) { }
|
||||||
|
/* Player.prototype.url = function(pos) {
|
||||||
|
var timecode = pos2npt(pos);
|
||||||
|
var link = video.url;
|
||||||
|
if(pos > 0)
|
||||||
|
link += "?t=npt:" + timecode;
|
||||||
|
return link;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
function VideoPlayer() {
|
||||||
|
this.supportsOverlay = true;
|
||||||
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype = new Player();
|
||||||
|
VideoPlayer.prototype.init = function(elemID) {
|
||||||
|
this.player = document.getElementById(elemID);
|
||||||
|
this.width = $(this.player).attr('width');
|
||||||
|
this.height = $(this.player).attr('height');
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.set = function(pos) {
|
||||||
|
/* var url = this.url(pos);
|
||||||
|
var autoplay = 'true';
|
||||||
|
if(this.isPlaying)
|
||||||
|
varautoplay = 'true';
|
||||||
|
if(this.player) {
|
||||||
|
var element = $(this.player);
|
||||||
|
this.player.pause();
|
||||||
|
} else {
|
||||||
|
var element = $('#' + playerID);
|
||||||
|
}
|
||||||
|
this.player = document.createElement('video');
|
||||||
|
this.player.id = playerID;
|
||||||
|
this.player.width = this.width;
|
||||||
|
this.player.height = this.height;
|
||||||
|
this.player.setAttribute('src', url);
|
||||||
|
//this.player.setAttribute('autoplay', autoplay);
|
||||||
|
element.replaceWith(this.player); */
|
||||||
|
this.player.currentTime = pos / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VideoPlayer.prototype.get = function() {
|
||||||
|
try {
|
||||||
|
return parseInt(this.player.currentTime * 1000);
|
||||||
|
} catch(err) { }
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.play = function() {
|
||||||
|
this.isPlaying = true;
|
||||||
|
this.player.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.pause = function() {
|
||||||
|
this.isPlaying = false;
|
||||||
|
this.player.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.mute = function(pos) {
|
||||||
|
this.player.muted = true;
|
||||||
|
this.muted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.unmute = function(pos) {
|
||||||
|
this.player.muted = false;
|
||||||
|
this.muted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.togglePause = function() {
|
||||||
|
if (Video.isPlaying == true) {
|
||||||
|
Video.pause();
|
||||||
|
} else {
|
||||||
|
Video.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VideoPlayer.prototype.listener = function() {
|
||||||
|
var ms = Video.get();
|
||||||
|
var npt = ms2npt(ms);
|
||||||
|
$('#timeCode').html(npt);
|
||||||
|
var seekBarPos = parseInt((ms / (Video.duration * 1000)) * 320);
|
||||||
|
$('#seekPointer').css("left", seekBarPos + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer.prototype.setDuration = function(duration) {
|
||||||
|
this.duration = duration;
|
||||||
|
}
|
184
chrome/content/staticfuncs.js
Executable file
184
chrome/content/staticfuncs.js
Executable file
|
@ -0,0 +1,184 @@
|
||||||
|
function npt2ms(npt) {
|
||||||
|
var ms = 0.0
|
||||||
|
npt = String(npt);
|
||||||
|
var p = npt.split(':')
|
||||||
|
for(i=0;i<p.length;i++)
|
||||||
|
ms = ms * 60 + parseFloat(p[i])
|
||||||
|
return ms * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ms2npt(ms) {
|
||||||
|
var it, ss, mm, hh, npt;
|
||||||
|
var it = parseInt(ms / 1000)
|
||||||
|
ms = ms - it * 1000;
|
||||||
|
if (ms.toString().length > 3) {
|
||||||
|
ms = ms.toString().substring(0,3);
|
||||||
|
}
|
||||||
|
ss = it % 60;
|
||||||
|
mm = ((it - ss) / 60) % 60;
|
||||||
|
hh = ((it - (mm * 60) - ss) / 3600) % 60;
|
||||||
|
npt = hh+':'+strpad(mm.toString(), '0', 2, 'left')
|
||||||
|
npt += ':'+strpad(ss.toString(), '0', 2, 'left')
|
||||||
|
npt += '.'+strpad(ms.toString(), '0', 3, 'left')
|
||||||
|
return npt;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ms2frames(ms, fmt) {
|
||||||
|
if (!fmt) var fmt = "PAL";
|
||||||
|
var npt = ms2npt(ms);
|
||||||
|
var dotpos = npt.lastIndexOf(".");
|
||||||
|
var mmStr = npt.substring(dotpos + 1, npt.length);
|
||||||
|
var mmInt = parseInt(mmStr);
|
||||||
|
if (fmt == 'PAL') {
|
||||||
|
var frames = parseInt((mmInt / 1000) * 24);
|
||||||
|
} else if (fmt == "NTSC") {
|
||||||
|
var frames = parseInt((mmInt / 1000) * 29.97);
|
||||||
|
}
|
||||||
|
var framesTc = '';
|
||||||
|
var joinToken = ":";
|
||||||
|
var framesTc = npt.substring(0, dotpos ) + joinToken + frames;
|
||||||
|
return framesTc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ms2time(ms) {
|
||||||
|
var npt = ms2npt(ms)
|
||||||
|
return npt.substr(npt.length-9, npt.length-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
function framesToNpt(timeCode) {
|
||||||
|
var frames = timeCode.substring(9, 11);
|
||||||
|
var ms = parseInt(frames) / 25 * 1000;
|
||||||
|
var ms = String(ms);
|
||||||
|
var ms = strpad(ms, '0', 3, 'right');
|
||||||
|
var timeCodeNpt = timeCode.substring(0,8) + "." + ms;
|
||||||
|
return timeCodeNpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function strpad(str, pad, len, dir) {
|
||||||
|
while (str.length < len) {
|
||||||
|
if (dir == 'left')
|
||||||
|
str = pad + str;
|
||||||
|
else if (dir == 'right')
|
||||||
|
str = str + pad;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidTimecode(tc) {
|
||||||
|
var tc = $.trim(tc);
|
||||||
|
var nptRegex = new RegExp("^[0-9][0-9]?\:[0-9][0-9]\:[0-9][0-9][\.|\,|\:][0-9]?[0-9]?[0-9]?$");
|
||||||
|
return nptRegex.test(tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFile() {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||||
|
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
||||||
|
fp.init(window, "Choose a File", nsIFilePicker.modeOpen);
|
||||||
|
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);
|
||||||
|
fp.appendFilters("Video Files", "*.ogg;*.ogv;*.ogx;*.dv;*.avi;*.mov");
|
||||||
|
var rv = fp.show();
|
||||||
|
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
|
||||||
|
var file = fp.file;
|
||||||
|
var path = fp.file.path;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mozillaSaveFile(filePath,content)
|
||||||
|
{
|
||||||
|
if(window.Components) {
|
||||||
|
try {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
|
||||||
|
file.initWithPath(filePath);
|
||||||
|
if(!file.exists())
|
||||||
|
file.create(0,0664);
|
||||||
|
var os = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
|
||||||
|
var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
|
||||||
|
out.init(file,0x20|0x02,00004,null);
|
||||||
|
os.init(out, "UTF-8", 0, 0x0000);
|
||||||
|
os.writeString(content);
|
||||||
|
//out.write(content,content.length);
|
||||||
|
os.close();
|
||||||
|
//out.flush();
|
||||||
|
out.close();
|
||||||
|
return true;
|
||||||
|
} catch(ex) {
|
||||||
|
alert(ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns null if it can't do it, false if there's an error, or a string of the content if successful
|
||||||
|
function mozillaLoadFile(filePath)
|
||||||
|
{
|
||||||
|
if(window.Components) {
|
||||||
|
try {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
|
||||||
|
file.initWithPath(filePath);
|
||||||
|
if(!file.exists())
|
||||||
|
return null;
|
||||||
|
var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
|
||||||
|
inputStream.init(file,0x01,00004,null);
|
||||||
|
//var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||||
|
//sInputStream.init(inputStream);
|
||||||
|
var charset = "UTF-8";
|
||||||
|
const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
|
||||||
|
var cInputStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||||
|
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||||
|
cInputStream.init(inputStream, charset, 1024, replacementChar);
|
||||||
|
var str = {};
|
||||||
|
var contents = '';
|
||||||
|
while (cInputStream.readString(4096, str) != 0) {
|
||||||
|
contents += str.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var contents = sInputStream.read(sInputStream.available());
|
||||||
|
cInputStream.close();
|
||||||
|
//sInputStream.close();
|
||||||
|
inputStream.close();
|
||||||
|
return contents;
|
||||||
|
} catch(ex) {
|
||||||
|
alert(ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkFileExists(filePath) {
|
||||||
|
if(window.Components) {
|
||||||
|
try {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
|
||||||
|
file.initWithPath(filePath);
|
||||||
|
if (file.exists()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
//alert("Error");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileNameSansExt(filename) {
|
||||||
|
var dotPos = filename.lastIndexOf(".");
|
||||||
|
if (dotPos != '-1') {
|
||||||
|
var filenameSansExt = filename.substring(0,dotPos);
|
||||||
|
} else {
|
||||||
|
var filenameSansExt = filename;
|
||||||
|
}
|
||||||
|
return filenameSansExt;
|
||||||
|
}
|
||||||
|
|
126
chrome/content/styles.css
Executable file
126
chrome/content/styles.css
Executable file
|
@ -0,0 +1,126 @@
|
||||||
|
body {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#additionalFeatures {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#videoWrapper {
|
||||||
|
float: left;
|
||||||
|
width: 400px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#selectFileDiv {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpWrapper {
|
||||||
|
-moz-border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000;
|
||||||
|
border: 2px solid #999;
|
||||||
|
background: #ccc;
|
||||||
|
padding: 4px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#video {
|
||||||
|
height: 300px;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#txtWrapper {
|
||||||
|
width: 50%;
|
||||||
|
margin-left: 20px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#buttonsWrapper {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventMetadata {
|
||||||
|
display: none;
|
||||||
|
margin-left: 20px;
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.eventMeta, textarea.eventMeta {
|
||||||
|
padding: 5px;
|
||||||
|
margin: 3px;
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#seekTime {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dateYear {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dateMonth {
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dateDay {
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#description {
|
||||||
|
width: 80%;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 8px;
|
||||||
|
margin-left: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 15px;
|
||||||
|
background: #ccc;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border: 2px solid #000;
|
||||||
|
color: #f33;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#txt {
|
||||||
|
height: 500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#seekBar {
|
||||||
|
height: 16px;
|
||||||
|
width: 320px;
|
||||||
|
margin-left: 40px;
|
||||||
|
background: #666;
|
||||||
|
opacity: 0.7;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#seekPointer {
|
||||||
|
height: 12px;
|
||||||
|
width: 10px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
#timeCode {
|
||||||
|
color: #f00;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
216
chrome/content/transcribe.html
Executable file
216
chrome/content/transcribe.html
Executable file
|
@ -0,0 +1,216 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>
|
||||||
|
Pad.ma: Subtitle n more...
|
||||||
|
</title>
|
||||||
|
<script type="text/javascript" src="jquery.js"></script>
|
||||||
|
<script type="text/javascript" src="player.js"></script>
|
||||||
|
<script type="text/javascript" src="jquery-ui.js"></script>
|
||||||
|
<script type="text/javascript" src="staticfuncs.js"></script>
|
||||||
|
<script type="text/javascript" src="classes.js"></script>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<script type="text/javascript">
|
||||||
|
//init globals
|
||||||
|
var seekBar;
|
||||||
|
var textArea;
|
||||||
|
var Video;
|
||||||
|
var filePath = false;
|
||||||
|
var spans;
|
||||||
|
var videoListener;
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
textArea = new TextArea("txt");
|
||||||
|
$('#saveFile').click(saveFile);
|
||||||
|
$('#saveSrt').click(saveSrt);
|
||||||
|
$('#addTime').click(function() {
|
||||||
|
var timeToAdd = npt2ms($.trim($('#timeToAdd').val()));
|
||||||
|
var startNo = parseInt($('#startNo').val());
|
||||||
|
var r = textArea.addTime(timeToAdd, startNo);
|
||||||
|
$('#addTimeResult').val(r);
|
||||||
|
});
|
||||||
|
$('#showMore').toggle(function() {
|
||||||
|
$(this).text("Show Less Features");
|
||||||
|
$('#additionalFeatures').show("fast");
|
||||||
|
}, function() {
|
||||||
|
$(this).text("Show More Features");
|
||||||
|
$('#additionalFeatures').hide("fast");
|
||||||
|
});
|
||||||
|
$('#saveEncore').click(saveEncore);
|
||||||
|
$('#selectFile').click(function() {
|
||||||
|
var videoFile = selectFile();
|
||||||
|
alert(videoFile);
|
||||||
|
filePath = getFileNameSansExt(videoFile);
|
||||||
|
var srtTxtFilename = filePath + ".srt.txt";
|
||||||
|
var srtFilename = filePath + ".srt";
|
||||||
|
var metaFilename = filePath + ".txt";
|
||||||
|
if (checkFileExists(srtTxtFilename)) {
|
||||||
|
loadSrtTxt(srtTxtFilename);
|
||||||
|
} else if (checkFileExists(srtFilename)) {
|
||||||
|
loadSrt(srtFilename);
|
||||||
|
}
|
||||||
|
if (checkFileExists(metaFilename)) {
|
||||||
|
loadMeta(metaFilename);
|
||||||
|
}
|
||||||
|
$('#video').attr("src", "file://" + videoFile);
|
||||||
|
document.getElementById("video").load();
|
||||||
|
$('#video').one("loadedmetadata", function() {
|
||||||
|
$('#selectFileDiv').fadeOut();
|
||||||
|
Video = new VideoPlayer();
|
||||||
|
Video.init("video");
|
||||||
|
//
|
||||||
|
// seekBar = new SeekBar("seekBar");
|
||||||
|
Video.setDuration(Video.player.duration);
|
||||||
|
$('#insertTc').click(textArea.insertTc);
|
||||||
|
/* $('#video').click(function() {
|
||||||
|
Video.togglePause();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
$('#video').dblclick(function() {
|
||||||
|
textArea.insertTc();
|
||||||
|
});
|
||||||
|
videoListener = setInterval(Video.listener, 250);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).keyup(function(e) {
|
||||||
|
//Esc
|
||||||
|
if (e.keyCode == 27 && textArea.hasFocus) {
|
||||||
|
Video.togglePause();
|
||||||
|
}
|
||||||
|
//Ins
|
||||||
|
if (e.keyCode == 45) {
|
||||||
|
if (!textArea.isTc()) {
|
||||||
|
textArea.insertTc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Ctrl - Seek Back
|
||||||
|
if (e.keyCode == 17) {
|
||||||
|
var seekTime = parseInt(parseFloat($('#seekTime').val()) * 1000);
|
||||||
|
var currTime = Video.get();
|
||||||
|
var newTime = currTime - seekTime;
|
||||||
|
Video.set(newTime);
|
||||||
|
}
|
||||||
|
//Alt - Seek Fwd.
|
||||||
|
if (e.keyCode == 18) {
|
||||||
|
var seekTime = parseInt(parseFloat($('#seekTime').val()) * 1000);
|
||||||
|
var currTime = Video.get();
|
||||||
|
var newTime = currTime + seekTime;
|
||||||
|
Video.set(newTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Space - togglePause if no focus on TA
|
||||||
|
if (e.keyCode == 32 && textArea.hasFocus == false) {
|
||||||
|
Video.togglePause();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#fillMeta').click(function() {
|
||||||
|
$('#txtWrapper').hide();
|
||||||
|
$('#eventMetadata').show();
|
||||||
|
});
|
||||||
|
$('#doneMetadata').click(function() {
|
||||||
|
$('#eventMetadata').hide();
|
||||||
|
$('#txtWrapper').show();
|
||||||
|
});
|
||||||
|
$('.eventMeta').each(function() {
|
||||||
|
var defVal = $(this).attr('data-default');
|
||||||
|
$(this).val(defVal);
|
||||||
|
$(this).focus(function() {
|
||||||
|
if ($(this).val() == $(this).attr("data-default")) {
|
||||||
|
$(this).val('');
|
||||||
|
}
|
||||||
|
}).blur(function() {
|
||||||
|
if ($(this).val() == '') {
|
||||||
|
$(this).val($(this).attr('data-default'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function saveFile() {
|
||||||
|
var savePath = filePath + ".srt.txt";
|
||||||
|
var content = $('#txt').val();
|
||||||
|
if (mozillaSaveFile(savePath, content)) {
|
||||||
|
alert("saved file at " + savePath);
|
||||||
|
} else {
|
||||||
|
alert("error saving file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSrt() {
|
||||||
|
var srtPath = filePath + ".srt";
|
||||||
|
var content = textArea.toSrt();
|
||||||
|
if (mozillaSaveFile(srtPath, content)) {
|
||||||
|
alert("export .srt file to " + srtPath);
|
||||||
|
} else {
|
||||||
|
alert("error exporting srt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveEncore() {
|
||||||
|
var encPath = filePath + ".enc.txt";
|
||||||
|
var content = textArea.toSrt("enc");
|
||||||
|
if (mozillaSaveFile(encPath, content)) {
|
||||||
|
alert("saved encore compatible subtitle file at " + encPath);
|
||||||
|
} else {
|
||||||
|
alert("error creating encore compatible subtitle file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="videoWrapper">
|
||||||
|
<div id="selectFileDiv">
|
||||||
|
<button id="selectFile">Select File</button>
|
||||||
|
</div>
|
||||||
|
<video id="video" src="" controls="true">
|
||||||
|
Sorry you need <a href="http://www.mozilla.com/en-US/firefox/all-beta.html">Firefox 3.5 Beta</a> for this to work.
|
||||||
|
</video>
|
||||||
|
<div id="seekBar">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="timeCode">0:00:00.000</div>
|
||||||
|
<div id="helpWrapper">
|
||||||
|
Shortcuts: <br /><br />
|
||||||
|
Esc: Pause / Unpause <br />
|
||||||
|
Insert: Insert time-code<br />
|
||||||
|
Double-click in Textarea: insert time-code <br />
|
||||||
|
Double-click on video: insert time-code<br />
|
||||||
|
Double-click on time-code in textarea: Seek video to time-code. <br />
|
||||||
|
Ctrl / Alt: Seek back / forward <input id="seekTime" value="1" /> seconds.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="txtWrapper">
|
||||||
|
<textarea id="txt"></textarea><br /> <br />
|
||||||
|
<div id="buttonsWrapper">
|
||||||
|
<!-- <button id="fillMeta" class="button">Enter Metadata</button> -->
|
||||||
|
<button id="saveFile" class="button">Save File</button>
|
||||||
|
<button id="saveSrt" class="button">Export SRT</button>
|
||||||
|
<button id="saveEncore" class="button">Export to Encore</button>
|
||||||
|
<button id="showMore" class="button">Show More Features</button>
|
||||||
|
</div>
|
||||||
|
<div id="additionalFeatures">
|
||||||
|
<div id="addTimeWrap">
|
||||||
|
Time to add: <input id="timeToAdd" /><br />
|
||||||
|
Start no: <input id="startNo" /> <button id="addTime">Add</button><br />
|
||||||
|
<textarea id="addTimeResult"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="eventMetadata">
|
||||||
|
<input id="title" data-default="Title" class="eventMeta" /><br />
|
||||||
|
<input id="director" data-default="Director" class="eventMeta" /><br />
|
||||||
|
<input id="collection" data-default="Collection" class="eventMeta" /><br />
|
||||||
|
<input id="source" data-default="Source" class="eventMeta" /><br />
|
||||||
|
<textarea id="description" data-default="Event Description" class="eventMeta"></textarea><br />
|
||||||
|
Date: <input id="dateYear" data-default="YYYY" class="eventMeta" /> <input id="dateMonth" data-default="MM" class="eventMeta" /> <input id="dateDay" data-default="DD" class="eventMeta" /><br />
|
||||||
|
Logged by: <input id="loggedBy" data-default="Your name" class="eventMeta" /><br /><br />
|
||||||
|
<button id="doneMetadata" class="button">Done Adding Metadata</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
29
install.rdf
Normal file
29
install.rdf
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||||
|
|
||||||
|
<Description about="urn:mozilla:install-manifest">
|
||||||
|
<em:id>b@pad.ma</em:id>
|
||||||
|
<em:version>0.2</em:version>
|
||||||
|
<em:type>2</em:type>
|
||||||
|
|
||||||
|
<!-- Target Application this extension can install into,
|
||||||
|
with minimum and maximum supported versions. -->
|
||||||
|
<em:targetApplication>
|
||||||
|
<Description>
|
||||||
|
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||||
|
<em:minVersion>1.5</em:minVersion>
|
||||||
|
<em:maxVersion>3.9</em:maxVersion>
|
||||||
|
</Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
<!-- Front End MetaData -->
|
||||||
|
<em:name>pad.ma transcription client</em:name>
|
||||||
|
<em:description>pad.ma offline transcription client</em:description>
|
||||||
|
<em:creator>pad.ma</em:creator>
|
||||||
|
<em:updateURL>http://files.pad.ma/annot/update.rdf</em:updateURL>
|
||||||
|
<em:updateKey>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcI+EIEmZ2UMEMzksirJQ6rbRZebeQzM9LCf9i7EZny5umQOF+92G+DxlKrk1BC+B1ZhXHMZNBRWEhn9kuW8mgXL1JBKVPbg4RjbJ39XigtXL5wNWU6EVdUvzFH1D49HwGpDRWJhnlUTMFhJL2FW47Qg1yCttQSEYmmSum3Y77rwIDAQAB</em:updateKey>
|
||||||
|
<em:homepageURL>http://wiki.pad.ma/wiki/Annot</em:homepageURL>
|
||||||
|
<em:iconURL>chrome://annot/content/icon.png</em:iconURL>
|
||||||
|
</Description>
|
||||||
|
</RDF>
|
Loading…
Reference in New Issue
Block a user