initial commit, first workable version
This commit is contained in:
commit
0abcd235c9
3 changed files with 173 additions and 0 deletions
26
index.html
Normal file
26
index.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Zoom Marks Analyzer</title>
|
||||||
|
<link rel="stylesheet" href="main.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<div id="dropmark">Click or drop file here</div>
|
||||||
|
<div id="label"></div>
|
||||||
|
<div id="cuelist-outer">
|
||||||
|
<div>MARKS</div>
|
||||||
|
<ol id="cuelist"></ol>
|
||||||
|
</div>
|
||||||
|
<button id="export-button" disabled>Export as TSV...
|
||||||
|
<span class="subtitle">(For Adobe Audition)</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/wavefile@11.0.0/dist/wavefile.min.js"
|
||||||
|
integrity="sha384-W3PirarYaOAUEL6KdD+DTxgNwNV2WjP01eFWsWWBBzy4goLvqOd//bovJESrq043"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
61
main.css
Normal file
61
main.css
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dropmark {
|
||||||
|
font-size: 16pt;
|
||||||
|
border: 4px dashed gray;
|
||||||
|
padding: 2em 3em;
|
||||||
|
|
||||||
|
background: #f5f5f5;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#label {
|
||||||
|
margin: 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cuelist-outer {
|
||||||
|
visibility: hidden;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cuelist-outer div {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cuelist li {
|
||||||
|
margin: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-button {
|
||||||
|
font-size: 16pt;
|
||||||
|
border: 4px solid gray;
|
||||||
|
|
||||||
|
padding: 1.5em 3em;
|
||||||
|
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-button .subtitle {
|
||||||
|
display: block;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
86
main.js
Normal file
86
main.js
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
const dropElement = document.getElementById("dropmark");
|
||||||
|
const label = document.getElementById("label");
|
||||||
|
const infoElement = document.getElementById("cuelist-outer");
|
||||||
|
const cueListElement = document.getElementById("cuelist");
|
||||||
|
const exportButton = document.getElementById("export-button");
|
||||||
|
|
||||||
|
|
||||||
|
function processFile (file) {
|
||||||
|
window.currentFile = file;
|
||||||
|
console.debug(window.currentFile);
|
||||||
|
file.arrayBuffer().then((arrayBuffer) => {
|
||||||
|
const buffer = new Uint8Array(arrayBuffer);
|
||||||
|
window.resultWav = new wavefile.WaveFile(buffer);
|
||||||
|
console.debug(window.resultWav);
|
||||||
|
window.cuePoints = window.resultWav.listCuePoints().map((point) => {
|
||||||
|
return new Date(point.position).toISOString().substr(11, 12);
|
||||||
|
});
|
||||||
|
console.debug(window.cuePoints);
|
||||||
|
displayMarks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayMarks () {
|
||||||
|
const length = window.resultWav.data.chunkSize / window.resultWav.fmt.byteRate;
|
||||||
|
label.innerText = `${window.currentFile.name}: ${length.toFixed(2)}s`;
|
||||||
|
|
||||||
|
while (cueListElement.lastElementChild) {
|
||||||
|
cueListElement.removeChild(cueListElement.lastElementChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.cuePoints.forEach((point) => {
|
||||||
|
const row = document.createElement("li");
|
||||||
|
row.innerText = point;
|
||||||
|
cueListElement.appendChild(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
infoElement.style.visibility = "inherit";
|
||||||
|
exportButton.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportAsTSV () {
|
||||||
|
const filename = `${window.currentFile.name.replace(/\.(wav|WAV)$/, "")}_marks.csv`;
|
||||||
|
let text = "Name\tStart\tDuration\tTime Format\tType\tDescription\n";
|
||||||
|
window.cuePoints.forEach((point, idx) => {
|
||||||
|
text += `Mark ${idx}\t${point}\t0:00.000\tdecimal\tCue\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.debug(text);
|
||||||
|
|
||||||
|
const aElement = document.createElement("a");
|
||||||
|
aElement.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
|
||||||
|
aElement.setAttribute("download", filename);
|
||||||
|
aElement.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileSelect (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
processFile(evt.dataTransfer.files.item(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragOver (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const fileInput = document.createElement("input");
|
||||||
|
fileInput.type = "file";
|
||||||
|
fileInput.accept = ".wav";
|
||||||
|
fileInput.onchange = () => {
|
||||||
|
if (fileInput.files !== null) {
|
||||||
|
processFile(fileInput.files.item(0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
dropElement.addEventListener("dragover", handleDragOver, false);
|
||||||
|
dropElement.addEventListener("drop", handleFileSelect, false);
|
||||||
|
dropElement.addEventListener("click", handleClick, false);
|
||||||
|
exportButton.addEventListener("click", exportAsTSV, false);
|
Loading…
Reference in a new issue