Browse Source

imlement live typing

master
Zolfa 1 month ago
parent
commit
1f1ecbe54a
4 changed files with 114 additions and 8 deletions
  1. +9
    -0
      app.js
  2. +5
    -2
      package.json
  3. +61
    -6
      public/audience.html
  4. +39
    -0
      public/live_typing.html

+ 9
- 0
app.js View File

@ -58,6 +58,11 @@ app.get('/speaker', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'speaker.html')); res.sendFile(path.join(__dirname, 'public', 'speaker.html'));
}); });
// Serve speaker view
app.get('/live-typing', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'live_typing.html'));
});
// Serve audience/projector view // Serve audience/projector view
app.get('/audience', (req, res) => { app.get('/audience', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'audience.html')); res.sendFile(path.join(__dirname, 'public', 'audience.html'));
@ -96,6 +101,10 @@ io.on('connection', (socket) => {
updateOBSSubtitle(currentLine); updateOBSSubtitle(currentLine);
generateSRT(currentLine); generateSRT(currentLine);
}); });
socket.on('live_typing', (text) => {
socket.broadcast.emit('live_typing', text); // Broadcast to audience
});
}); });
async function updateOBSSubtitle(captionText) { async function updateOBSSubtitle(captionText) {


+ 5
- 2
package.json View File

@ -1,6 +1,6 @@
{ {
"name": "speechsub", "name": "speechsub",
"version": "1.0.0",
"version": "0.2.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@ -18,7 +18,10 @@
"socket.io": "^4.8.0" "socket.io": "^4.8.0"
}, },
"pkg": { "pkg": {
"assets": "public/*",
"assets": [
"public/*",
"node_modules/socket.io/client-dist/**/*"
],
"outputPath": "dist" "outputPath": "dist"
} }
} }

+ 61
- 6
public/audience.html View File

@ -15,14 +15,14 @@
padding: 0; padding: 0;
-webkit-text-fill-color: white; -webkit-text-fill-color: white;
-webkit-text-stroke-color: black; -webkit-text-stroke-color: black;
-webkit-text-stroke-width: 6.00px;
/*-webkit-text-stroke-width: 6.00px; */
paint-order: stroke fill; paint-order: stroke fill;
} }
#container { #container {
overflow: hidden; overflow: hidden;
height: 200px; /* Ensure container is tall enough for 4 lines */ height: 200px; /* Ensure container is tall enough for 4 lines */
max-width: 42ch;
width: 42ch;
position: relative; position: relative;
padding-left: 20px; /* Add some padding to the left */ padding-left: 20px; /* Add some padding to the left */
margin-left: auto; margin-left: auto;
@ -30,17 +30,26 @@
} }
#content { #content {
position: absolute;
position: relative;
top: 0; top: 0;
transition: top 0.5s ease; /* Smooth scrolling */
left: auto;
right: auto;
/*transition: top 0.5s ease;*/ /* Smooth scrolling */
} }
.line { .line {
opacity: 1; opacity: 1;
min-height: 44px;
white-space: normal; /* Allow lines to wrap */ white-space: normal; /* Allow lines to wrap */
word-wrap: break-word; /* Break long words if needed */ word-wrap: break-word; /* Break long words if needed */
margin: 5px 0; margin: 5px 0;
}
line-height: 48px;
}
.line span {
background-color: rgba(0, 0, 0, 0.7);
padding: 2px;
}
.currentLine { .currentLine {
font-weight: bold; font-weight: bold;
@ -77,7 +86,9 @@
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('line'); div.classList.add('line');
if (index === 2) div.classList.add('currentLine'); // Highlight the current line if (index === 2) div.classList.add('currentLine'); // Highlight the current line
div.textContent = line;
const span = document.createElement('span');
span.textContent = line;
div.appendChild(span);
return div; return div;
}); });
@ -101,6 +112,50 @@
currentLineIndex = 0; currentLineIndex = 0;
updateDisplay(); // Display the initial lines updateDisplay(); // Display the initial lines
}); });
// Update audience text based on live typing
socket.on('live_typing', (text) => {
const lines = text.split('\n'); // Split by new lines
const fullLines = lines.slice(-3); // Get the last three lines
// Check if the last line is complete
let lastLine = fullLines[fullLines.length - 1];
const isLastWordComplete = lastLine.endsWith(' ') || lastLine.endsWith('.') || lastLine.endsWith(',') || lastLine.endsWith('!') || lastLine.endsWith('?') || lastLine.endsWith('\n');
// If the last word is not complete, hide it
if (!isLastWordComplete) {
lastLine = lastLine.trim().split(' ').slice(0, -1).join(' '); // Remove the last word
}
// Join back the lines and update content
contentElement.innerHTML = '';
// Create a new block of lines
let displayLines = (fullLines.slice(0, -1)).map((line, index) => {
const div = document.createElement('div');
div.classList.add('line');
const span = document.createElement('span');
span.textContent = line;
div.appendChild(span);
return div;
});
const lastLineDiv = document.createElement('div');
lastLineDiv.classList.add('line');
lastLineDiv.classList.add('currentLine');
const lastLineSpan = document.createElement('span');
lastLineSpan.textContent = lastLine;
lastLineDiv.appendChild(lastLineSpan);
displayLines.push(lastLineDiv)
displayLines.forEach(div => contentElement.appendChild(div));
// Scroll animation based on the height of each line
//const lineHeight = document.querySelector('.line').offsetHeight;
//const previousLinesHeight = (currentLineIndex - start - 1) * lineHeight; // Offset for lines above the current line
const previousLinesHeight = document.querySelectorAll('.line')[1].offsetHeight;
contentElement.style.top = `-${previousLinesHeight}px`;
});
</script> </script>
</body> </body>
</html> </html>

+ 39
- 0
public/live_typing.html View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Typing Speaker View</title>
<style>
body {
font-family: Futura, sans-serif;
font-size: 32px;
padding: 20px;
}
#typingArea {
width: 100%;
height: 200px;
font-size: 32px;
padding: 10px;
}
</style>
</head>
<body>
<h1>Live Typing Speaker View</h1>
<textarea id="typingArea" placeholder="Type subtitles here..."></textarea>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const typingArea = document.getElementById('typingArea');
// Emit typed text to audience view on keyup event
typingArea.addEventListener('keyup', () => {
const typedText = typingArea.value;
socket.emit('live_typing', typedText); // Send current text to audience
});
</script>
</body>
</html>

Loading…
Cancel
Save