Trashymanner commited on
Commit
b4e668c
·
verified ·
1 Parent(s): 2483313

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile.txt +15 -0
  2. README.md +34 -0
  3. gitignore.txt +2 -0
  4. package.json +11 -0
  5. server.js +71 -0
Dockerfile.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package.json ./
6
+ RUN npm install --omit=dev
7
+
8
+ COPY server.js ./
9
+ COPY public/ public/
10
+
11
+ # HF Spaces wymaga portu 7860
12
+ ENV PORT=7860
13
+ EXPOSE 7860
14
+
15
+ CMD ["node", "server.js"]
README.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Realtime Translate
3
+ emoji: 🎙
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ license: other
10
+ tags:
11
+ - openai
12
+ - speech
13
+ - translation
14
+ - realtime
15
+ - webrtc
16
+ ---
17
+
18
+ # Realtime Translator — gpt-realtime-translate
19
+
20
+ Live speech-to-speech translation powered by OpenAI's `gpt-realtime-translate` model.
21
+
22
+ **Model:** [gpt-realtime-translate](https://platform.openai.com/docs/models/gpt-realtime-translate) by [OpenAI](https://openai.com)
23
+
24
+ **How to use:**
25
+ 1. Enter your OpenAI API key (starts with `sk-`) — stored only in your browser's localStorage
26
+ 2. Select source and target language
27
+ 3. Click the microphone button and speak
28
+ 4. Hear the translated audio and read the transcript
29
+
30
+ **Or** set `OPENAI_API_KEY` as a Space secret — then the key field is not required.
31
+
32
+ **Cost:** ~$0.034 / minute of audio (billed by OpenAI).
33
+
34
+ Built with Node.js + Express + WebRTC.
gitignore.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ node_modules/
2
+ .env
package.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "realtime-translate",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "node server.js"
7
+ },
8
+ "dependencies": {
9
+ "express": "^4.19.2"
10
+ }
11
+ }
server.js ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from "express";
2
+
3
+ const app = express();
4
+ app.use(express.json());
5
+ app.use(express.static("public"));
6
+
7
+ const ENV_API_KEY = process.env.OPENAI_API_KEY ?? null;
8
+
9
+ const SUPPORTED_LANGUAGES = {
10
+ en: "English", pl: "Polish", de: "German", fr: "French",
11
+ es: "Spanish", it: "Italian", ja: "Japanese", zh: "Chinese",
12
+ uk: "Ukrainian", ru: "Russian",
13
+ };
14
+
15
+ app.post("/session", async (req, res) => {
16
+ const targetLanguage = req.body.targetLanguage ?? "en";
17
+ const inputLanguage = req.body.inputLanguage ?? null;
18
+
19
+ // Klucz z env Secrets ma priorytet; fallback na klucz wpisany przez użytkownika w UI
20
+ const apiKey = ENV_API_KEY ?? req.body.apiKey ?? null;
21
+
22
+ if (!apiKey || !apiKey.startsWith("sk-")) {
23
+ return res.status(401).json({ error: "Brak lub nieprawidłowy klucz API OpenAI." });
24
+ }
25
+
26
+ if (!SUPPORTED_LANGUAGES[targetLanguage]) {
27
+ return res.status(400).json({ error: `Nieobsługiwany język: ${targetLanguage}` });
28
+ }
29
+
30
+ try {
31
+ const session = {
32
+ model: "gpt-realtime-translate",
33
+ audio: { output: { language: targetLanguage } },
34
+ };
35
+
36
+ if (inputLanguage && SUPPORTED_LANGUAGES[inputLanguage]) {
37
+ session.audio.input = { language: inputLanguage };
38
+ }
39
+
40
+ const response = await fetch(
41
+ "https://api.openai.com/v1/realtime/translations/client_secrets",
42
+ {
43
+ method: "POST",
44
+ headers: {
45
+ Authorization: `Bearer ${apiKey}`,
46
+ "Content-Type": "application/json",
47
+ },
48
+ body: JSON.stringify({ session }),
49
+ }
50
+ );
51
+
52
+ if (!response.ok) {
53
+ const err = await response.text();
54
+ console.error("OpenAI error:", err);
55
+ return res.status(response.status).json({ error: "Błąd OpenAI API", details: err });
56
+ }
57
+
58
+ res.json(await response.json());
59
+ } catch (err) {
60
+ console.error("Fetch error:", err);
61
+ res.status(500).json({ error: "Błąd serwera", details: err.message });
62
+ }
63
+ });
64
+
65
+ const PORT = process.env.PORT ?? 7860;
66
+ app.listen(PORT, "0.0.0.0", () => {
67
+ console.log(`Serwer działa na porcie ${PORT}`);
68
+ if (!ENV_API_KEY) {
69
+ console.log("INFO: OPENAI_API_KEY nie ustawiony — użytkownicy podają klucz przez UI.");
70
+ }
71
+ });