510 lines
15 KiB
JavaScript
510 lines
15 KiB
JavaScript
|
//init globals
|
||
|
//var seekBar;
|
||
|
var textArea;
|
||
|
var Video;
|
||
|
var filePath = false;
|
||
|
var videoListener;
|
||
|
var seekBar;
|
||
|
var globalUser;
|
||
|
var srtFilePath = undefined;
|
||
|
var videoPath = undefined;
|
||
|
var videoHash = undefined;
|
||
|
|
||
|
function adjustSize() {
|
||
|
var baseHeight = $(window).height() - 24 - 16;
|
||
|
$('#txt').height(baseHeight);
|
||
|
var baseWidth = $(window).width() - 400 - 64;
|
||
|
$('#txt').width(baseWidth);
|
||
|
}
|
||
|
|
||
|
$(document).ready(function() {
|
||
|
//alert("hi");
|
||
|
globalUser = new User();
|
||
|
adjustSize();
|
||
|
$(document).keydown(function(e) {
|
||
|
if (!Video)
|
||
|
return;
|
||
|
//Esc
|
||
|
if (e.keyCode == 27) {
|
||
|
Video.togglePause();
|
||
|
if (!textArea.hasFocus) {
|
||
|
textArea.elem.focus();
|
||
|
}
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
|
||
|
//The weird `~ key, currently in as a silly hack because Ranjana's Esc key does not work.
|
||
|
if (e.keyCode == 192) {
|
||
|
Video.togglePause();
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
|
||
|
//Ins or TAB
|
||
|
if (e.keyCode == 45 || e.keyCode == 9) {
|
||
|
if (!textArea.isTc()) {
|
||
|
textArea.insertTc();
|
||
|
return false;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
//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();
|
||
|
}
|
||
|
|
||
|
//PageUp - volume Up:
|
||
|
if (e.keyCode == 33) {
|
||
|
Video.volUp();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (e.keyCode == 34) {
|
||
|
Video.volDown();
|
||
|
return false;
|
||
|
}
|
||
|
});
|
||
|
$(window).bind('resize', adjustSize);
|
||
|
var loadingIcon = new Ox.LoadingIcon({
|
||
|
size: "medium"
|
||
|
})
|
||
|
.css({
|
||
|
marginLeft: "4px"
|
||
|
});
|
||
|
var mainMenu = new Ox.MainMenu({
|
||
|
extras: [],
|
||
|
menus: [
|
||
|
{
|
||
|
id: "speedtrans",
|
||
|
title: "SpeedTrans",
|
||
|
items: [
|
||
|
{ id: "about", title: "About" },
|
||
|
{},
|
||
|
{ id: "contact", title: "Contact", disabled: true}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
id: "open",
|
||
|
title: "Open",
|
||
|
items: [
|
||
|
{ id: "load_video", title: "Video"},
|
||
|
{ id: "load_srt", title: "Srt File"},
|
||
|
{},
|
||
|
{ id: "convertvideo", title: "Convert and load Video", disabled: true }
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
id: "save",
|
||
|
title: "Save",
|
||
|
items: [
|
||
|
//{ id: "save_txt", title: "Save" },
|
||
|
{ id: "save_srt", title: "Save Srt"},
|
||
|
{ id: "save_as", title: "Save Srt As.."}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
id: "edit",
|
||
|
title: "Edit",
|
||
|
items: [
|
||
|
{ id: "preferences", title: "Preferences" }
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
id: "tools",
|
||
|
title: "Tools",
|
||
|
items: [
|
||
|
{ id: "add_time", title: "Add / Subtract time and export", disabled: true },
|
||
|
{ id: "export_encore", title: "Export Adobe Encore Subtitle Format" },
|
||
|
{ id: "export_html", title: "Export HTML" },
|
||
|
{},
|
||
|
{ id: "load_cuts", title: "Load cuts from pan.do/ra", disabled: false }
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
id: "upload",
|
||
|
title: "Upload",
|
||
|
items: [
|
||
|
{ id: "upload_padma", title: "Upload Transcript to Pad.ma", disabled: true }
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
id: "help",
|
||
|
title: "Help",
|
||
|
items: [
|
||
|
{ id: "shortcuts", title: "Keyboard Shortcuts" }
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
size: "large"
|
||
|
});
|
||
|
mainMenu.css({'position': 'fixed', 'top': '0px', 'left': '0px', 'right': '0px'}).appendTo('body');
|
||
|
textArea = new TextArea("txt");
|
||
|
textArea.elem.val('');
|
||
|
Ox.Event.bind(null, "click_load_video", function() {
|
||
|
saveTxt();
|
||
|
videoPath = undefined;
|
||
|
videoHash = undefined;
|
||
|
srtFilePath = undefined;
|
||
|
textArea.elem.val('');
|
||
|
var filters = {'Video Files': '*.dv;*.ogg;*.ogv;*.ogx;*.avi;*.mov;*.mp4;*.mpeg;*.mpg;*.vob;*.mp3;*.wav;*.webm;*.mkv;*.mts;*.m4v'};
|
||
|
fileObject = selectFile(filters);
|
||
|
if (!fileObject) { return false; }
|
||
|
var path = fileObject.file.path;
|
||
|
|
||
|
if ($.inArray(getFileNameExt(path.toLowerCase()), [
|
||
|
'oga', 'ogg', 'ogv', 'ogx', 'wav', 'webm', 'mp4', 'm4v'
|
||
|
]) != -1) {
|
||
|
loadVideo(path);
|
||
|
} else {
|
||
|
if (typeof Firefogg != 'undefined') {
|
||
|
var encodeConfirmDialog = new Ox.Dialog({
|
||
|
buttons: [
|
||
|
{'value': 'Continue', 'click': function() { encodeVideo(fileObject.file); encodeConfirmDialog.close(); }},
|
||
|
{'value': 'Cancel', 'click': function() { encodeConfirmDialog.close(); }}
|
||
|
],
|
||
|
title: "Convert Video"
|
||
|
});
|
||
|
var e = new Ox.Element();
|
||
|
var html = "Video needs to be encoded. Continue? (may take some time)";
|
||
|
e.html(html);
|
||
|
|
||
|
// Ox.tmpl("dialog_encode_confirm", {'filename': path});
|
||
|
// alert(html);
|
||
|
// e.html(html);
|
||
|
// e.from_tmpl("dialog_encode_confirm", {'filename': path });
|
||
|
encodeConfirmDialog.append(e);
|
||
|
encodeConfirmDialog.open();
|
||
|
// encodeVideo(fileObject.file);
|
||
|
} else {
|
||
|
stDialog("Download Firefogg", "You will need the <a href='http://firefogg.org'>Firefogg</a> add-on to be able to import videos that are not in the Ogg Theora format. Once you install <a href='http://firefogg.org'>Firefogg</a>, restart your browser and try opening the video again and you should be given an option to transcode the video.");
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Ox.Event.bind(null, "click_about", function() {
|
||
|
var html = Ox.tmpl("dialog_about", {});
|
||
|
stDialog("About", html);
|
||
|
});
|
||
|
|
||
|
Ox.Event.bind(null, "click_preferences", function() {
|
||
|
var prefs = globalUser.get_prefs(["fontSize", "theme", "autosave"]);
|
||
|
var html = Ox.tmpl("dialog_preferences", {});
|
||
|
stDialog("Preferences", html);
|
||
|
$('.pref_select').each(function() {
|
||
|
var that = this;
|
||
|
var pref = $(this).attr("data-pref");
|
||
|
var prefVal = prefs[pref];
|
||
|
$(this).children('option').each(function() {
|
||
|
if ($(this).val() == prefVal) {
|
||
|
$(this).attr("selected", "selected");
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
$('.pref_select').change(function() {
|
||
|
var key = $(this).attr("data-pref");
|
||
|
var val = $(this).val();
|
||
|
globalUser.set_pref(key, val);
|
||
|
});
|
||
|
/*
|
||
|
$('#pref_fontSize > option').each(function() {
|
||
|
if ($(this).val() == prefs.fontSize) {
|
||
|
$(this).attr("selected", "selected");
|
||
|
}
|
||
|
});
|
||
|
$('#pref_theme > option').each(function() {
|
||
|
if ($(this).val() == prefs.theme) {
|
||
|
$(this).attr("selected", "selected");
|
||
|
}
|
||
|
});
|
||
|
*/
|
||
|
});
|
||
|
|
||
|
Ox.Event.bind(null, "click_shortcuts", function() {
|
||
|
var html = Ox.tmpl("dialog_shortcuts", {});
|
||
|
stDialog("Keyboard Shortcuts", html);
|
||
|
});
|
||
|
|
||
|
Ox.Event.bind(null, "click_save_srt", function() {
|
||
|
if (typeof srtFilePath == 'undefined') {
|
||
|
// srtFilePath = filePath + ".srt";
|
||
|
var path = mozillaSaveAs();
|
||
|
if (path) {
|
||
|
srtFilePath = path;
|
||
|
}
|
||
|
}
|
||
|
saveSrt(srtFilePath);
|
||
|
});
|
||
|
Ox.Event.bind(null, "click_load_srt", function() {
|
||
|
var filters = {'Srt files': '*.srt'};
|
||
|
var fileObj = selectFile(filters);
|
||
|
if (fileObj) {
|
||
|
var srtPath = fileObj.file.path;
|
||
|
loadSrt(srtPath);
|
||
|
}
|
||
|
});
|
||
|
Ox.Event.bind(null, "click_save_as", function() {
|
||
|
var path = mozillaSaveAs();
|
||
|
if (path) {
|
||
|
srtFilePath = path;
|
||
|
saveSrt(path);
|
||
|
return true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Ox.Event.bind(null, "click_export_html", function() {
|
||
|
var path = srtFilePath.replace(".srt", ".html");
|
||
|
saveHTML(path);
|
||
|
});
|
||
|
Ox.Event.bind(null, "click_load_cuts", function() {
|
||
|
var html = Ox.tmpl("dialog_load_cuts");
|
||
|
var loadDialog = new Ox.Dialog({
|
||
|
buttons: [
|
||
|
{'value': 'Cancel', 'click': function() {
|
||
|
loadDialog.close();
|
||
|
}
|
||
|
},
|
||
|
{'value': 'Load Cuts', 'click': function() {
|
||
|
var url = loadDialog.find('input')[0].value.trim();
|
||
|
var match = url.match(/(.*)\/([A-Z]+)$/);
|
||
|
if (match) {
|
||
|
loadCuts(match[1] + '/api/', match[2]);
|
||
|
}
|
||
|
loadDialog.close();
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}).append(html).open();
|
||
|
});
|
||
|
|
||
|
if (globalUser.get_pref("recentVideo") != '') {
|
||
|
var html = Ox.tmpl("dialog_load_video", globalUser.get_prefs(["recentVideo", "recentSrt"]));
|
||
|
var loadDialog = new Ox.Dialog({
|
||
|
buttons: [
|
||
|
{'value': 'Load Recent', 'click': function() {
|
||
|
var isRecent = true;
|
||
|
loadVideo(globalUser.get_pref("recentVideo"), isRecent);
|
||
|
loadDialog.close();
|
||
|
}
|
||
|
},
|
||
|
{'value': 'Load New', 'click': function() {
|
||
|
Ox.Event.trigger("click_load_video");
|
||
|
loadDialog.close();
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}).append(html).open();
|
||
|
} else {
|
||
|
Ox.Event.trigger("click_load_video");
|
||
|
}
|
||
|
globalUser.init();
|
||
|
|
||
|
//autosave every 1 minute.
|
||
|
window.autosaveInterval = setInterval(function() {
|
||
|
if (typeof(videoHash) !== 'undefined') {
|
||
|
saveTxt();
|
||
|
}
|
||
|
}, 60000);
|
||
|
|
||
|
});
|
||
|
|
||
|
function loadTxt() {
|
||
|
var txt = globalUser.get_txt(videoHash);
|
||
|
//console.log('loadTxt', videoHash, txt.length);
|
||
|
textArea.elem.val(txt);
|
||
|
}
|
||
|
|
||
|
function saveTxt() {
|
||
|
var content = textArea.toSrt();
|
||
|
var txt = textArea.elem.val();
|
||
|
//console.log('saveTxt', videoHash, txt.length);
|
||
|
globalUser.set_txt(videoHash, txt);
|
||
|
}
|
||
|
|
||
|
//pass second argument = 'dont_confirm' to not show confirmation dialog.
|
||
|
function saveSrt(path) {
|
||
|
if (!path) { return }
|
||
|
var content = textArea.toSrt();
|
||
|
var txt = textArea.elem.val();
|
||
|
if (mozillaSaveFile(path, content)) {
|
||
|
if (arguments[1] != 'dont_confirm') {
|
||
|
stDialog("Saved File", "Saved File at " + path);
|
||
|
}
|
||
|
} else {
|
||
|
stDialog("Error Saving File", "Your file could not be saved. Please make a backup of your transcription and email b@pad.ma for help.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function saveHTML(path) {
|
||
|
if (!path) { return }
|
||
|
var content = textArea.toHTML();
|
||
|
if (mozillaSaveFile(path, content)) {
|
||
|
if (arguments[1] != 'dont_confirm') {
|
||
|
stDialog("Saved File", "Saved File at " + path);
|
||
|
}
|
||
|
} else {
|
||
|
stDialog("Error Saving File", "Your file could not be saved. Please make a backup of your transcription and email b@pad.ma for help.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
function saveEncore() {
|
||
|
if (!filePath) { return }
|
||
|
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");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function stDialog(titleTxt, text) {
|
||
|
var dialog = new Ox.Dialog({
|
||
|
buttons:
|
||
|
[
|
||
|
{ value: "Close", click: function() { dialog.close(); } }
|
||
|
],
|
||
|
title: titleTxt
|
||
|
}).append(text);
|
||
|
dialog.open();
|
||
|
return dialog;
|
||
|
}
|
||
|
|
||
|
function loadVideo(videoFile, isRecent) {
|
||
|
videoPath = videoFile;
|
||
|
filePath = getFileNameSansExt(videoFile);
|
||
|
srtFilePath = filePath + ".srt";
|
||
|
Ox.oshash(new File(videoPath), function(hash) {
|
||
|
videoHash = hash;
|
||
|
var storageKey = 'txt_' + videoHash;
|
||
|
if (storageKey in storage) {
|
||
|
loadTxt();
|
||
|
} else if (isRecent) {
|
||
|
/* If contents are not in local storage,
|
||
|
attempt to load recent srt file. This is mostly
|
||
|
to aid in users migrating to version that saves
|
||
|
in localStorage */
|
||
|
var srtPath = globalUser.get_pref('recentSrt');
|
||
|
if (srtPath) {
|
||
|
loadSrt(srtPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
});
|
||
|
$('#video').attr("src", "file://" + videoFile);
|
||
|
$('#filepath').text(videoFile);
|
||
|
document.getElementById("video").load();
|
||
|
$('#video').one("loadedmetadata", function() {
|
||
|
Video = new VideoPlayer();
|
||
|
Video.init("video");
|
||
|
Video.setDuration(Video.player.duration);
|
||
|
$('#insertTc').click(textArea.insertTc);
|
||
|
videoListener = setInterval(Video.listener, 250);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
function encodeVideo(file) {
|
||
|
var ogg = new Firefogg();
|
||
|
if(!ogg.setInputVideo(file)) {
|
||
|
$('#encodingStatus').text('Could not select file.');
|
||
|
return;
|
||
|
}
|
||
|
var options = JSON.stringify({'maxSize': 640, 'videoQuality': 5, 'audioQuality': 3, 'channels': 2, 'noUpscaling': true, 'videoCodec': 'vp8'});
|
||
|
ogg.encode(options, function(result, video) {
|
||
|
result = JSON.parse(result);
|
||
|
if (result.progress != 1) {
|
||
|
$('#encodingStatus').text('Encoding failed.');
|
||
|
} else {
|
||
|
statusDialog.close();
|
||
|
loadVideo(oggPath);
|
||
|
}
|
||
|
}, function(progress, video) {
|
||
|
var v = document.getElementById('previewVideo');
|
||
|
if(video && !v.src) {
|
||
|
v.removeEventListener("loadedmetadata", seekToEnd, true);
|
||
|
v.addEventListener("loadedmetadata", seekToEnd, true);
|
||
|
v.removeEventListener("seeked", getFrame, true);
|
||
|
v.addEventListener("seeked", getFrame, true);
|
||
|
v.src = URL.createObjectURL(video);
|
||
|
}
|
||
|
progress = JSON.parse(progress).progress || 0;
|
||
|
var percentProgress = parseInt(progress * 100);
|
||
|
$('#progressBar').css("width", percentProgress.toString() + "%");
|
||
|
$('#encodedPercent').text(percentProgress.toString());
|
||
|
});
|
||
|
var oggPath = getFileNameSansExt(file.path) + ".webm";
|
||
|
var statusDialog = new Ox.Dialog({
|
||
|
title: 'Encoding Video',
|
||
|
buttons: [{ value: "Cancel", click: function() { window.location.reload(); } }],
|
||
|
height: 480,
|
||
|
width: 440
|
||
|
});
|
||
|
var e = new Ox.Element();
|
||
|
e.from_tmpl("dialog_encoding", {'filename': file.path});
|
||
|
statusDialog.append(e);
|
||
|
statusDialog.open();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
Functions to generate preview while encoding: see http://firefogg.org/examples
|
||
|
*/
|
||
|
//intialize interval to update preview and add event liseners.
|
||
|
//any previous listeners and intervals are cleared
|
||
|
var previewI=null;
|
||
|
|
||
|
function seekToEnd() {
|
||
|
// console.log("seeking to end of video");
|
||
|
var v = document.getElementById('previewVideo');
|
||
|
v.currentTime = v.duration-0.4;
|
||
|
}
|
||
|
//callback function render frame into canvas after seeking
|
||
|
function getFrame() {
|
||
|
var v = document.getElementById('previewVideo');
|
||
|
var canvas = document.getElementById('previewCanvas');
|
||
|
canvas.width = 400;
|
||
|
canvas.height = canvas.width * v.videoHeight/v.videoWidth;
|
||
|
var ctx = canvas.getContext("2d");
|
||
|
ctx.drawImage(v, 0, 0, canvas.width, canvas.height);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
End Preview functions
|
||
|
*/
|
||
|
|
||
|
|
||
|
$(window).unload(function() {
|
||
|
console.log('unload');
|
||
|
if (typeof filePath != 'undefined' && typeof srtFilePath != 'undefined') {
|
||
|
globalUser.set_prefs({
|
||
|
'recentVideo': $('#video').attr("src").replace("file://", ""),
|
||
|
'recentSrt': srtFilePath
|
||
|
});
|
||
|
}
|
||
|
saveTxt();
|
||
|
});
|
||
|
|
||
|
/*
|
||
|
This is just to kill console.logs that maybe generated by Ox.print calls, because that seems to mess with Firefogg. Debug and remove.
|
||
|
*/
|
||
|
Ox.print = function() {
|
||
|
$.noop();
|
||
|
}
|
||
|
|