ai-sub-translator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +135 -0
- package/dist/gemini-client.d.ts +9 -0
- package/dist/gemini-client.d.ts.map +1 -0
- package/dist/gemini-client.js +24 -0
- package/dist/gemini-client.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/srt-parser.d.ts +4 -0
- package/dist/srt-parser.d.ts.map +1 -0
- package/dist/srt-parser.js +30 -0
- package/dist/srt-parser.js.map +1 -0
- package/dist/translator.d.ts +3 -0
- package/dist/translator.d.ts.map +1 -0
- package/dist/translator.js +46 -0
- package/dist/translator.js.map +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +91 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) <2025> <Alex>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# AI Subtitle Translator
|
|
2
|
+
|
|
3
|
+
<img src="./images/screenshot.png" alt="App screenshot" width="512" height="712">
|
|
4
|
+
|
|
5
|
+
An Electron-based desktop application for translating subtitles using AI technology. This application allows users to translate subtitle files efficiently using Google's Generative AI.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Well, it translates subtitles
|
|
10
|
+
- Progress bar
|
|
11
|
+
- Modern React-based user interface (he-he)
|
|
12
|
+
- Grab subtitles directly from video source
|
|
13
|
+
- Support for both x64 and ARM64 Windows architectures
|
|
14
|
+
- **Headless server mode** for servers (JSON-RPC API)
|
|
15
|
+
|
|
16
|
+
## ToDo
|
|
17
|
+
|
|
18
|
+
- Support .mks, .ass formats
|
|
19
|
+
- Different AI models support
|
|
20
|
+
- Download / Upload subtitles to subtitle websites
|
|
21
|
+
- Proper UX/UI
|
|
22
|
+
|
|
23
|
+
## Known bugs
|
|
24
|
+
- No proper progress for downloading ffmpeg
|
|
25
|
+
- State is not cleared when load new subtitles
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
1. Grab latest release or build the app
|
|
30
|
+
2. Obtain API key for Gemini AI [here](https://aistudio.google.com/app/apikey). **Free tier is enough**
|
|
31
|
+
3. Launch the app
|
|
32
|
+
4. Load source .srt file
|
|
33
|
+
5. Enter your API key, language to translate, name the piece of content that you want to translate (eg. Last of us season 2 episode 1)
|
|
34
|
+
6. Press Translate button. It should take couple of minutes (progress will be displayed)
|
|
35
|
+
|
|
36
|
+
## Prerequisites
|
|
37
|
+
|
|
38
|
+
- Node.js (v14 or higher)
|
|
39
|
+
- npm (v6 or higher)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
1. Clone the repository:
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/seeingred/ai-sub-translator.git
|
|
47
|
+
cd ai-sub-translator
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
2. Install dependencies:
|
|
51
|
+
```bash
|
|
52
|
+
npm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Headless Server Mode
|
|
57
|
+
|
|
58
|
+
The app can run as a headless server on Linux servers without GUI, exposing a JSON-RPC API for subtitle translation.
|
|
59
|
+
|
|
60
|
+
### Quick Start
|
|
61
|
+
```bash
|
|
62
|
+
# On Linux server (auto-detects no display)
|
|
63
|
+
./ai-sub-translator
|
|
64
|
+
|
|
65
|
+
# Force headless mode on any platform
|
|
66
|
+
./ai-sub-translator --headless
|
|
67
|
+
|
|
68
|
+
# Custom port
|
|
69
|
+
API_PORT=8080 ./ai-sub-translator --headless
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**For full server documentation, see [README-SERVER.md](./README-SERVER.md)**
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
To start the application in development mode:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm start
|
|
80
|
+
|
|
81
|
+
# Test headless mode locally
|
|
82
|
+
npm start -- --headless
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Building
|
|
86
|
+
|
|
87
|
+
To create a production build:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm run make
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This will create distributable packages for your platform.
|
|
94
|
+
|
|
95
|
+
### Building for specific architectures
|
|
96
|
+
|
|
97
|
+
For Windows, you can build for specific architectures:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Build for Windows x64
|
|
101
|
+
npm run make:win:x64
|
|
102
|
+
|
|
103
|
+
# Build for Windows ARM64
|
|
104
|
+
npm run make:win:arm64
|
|
105
|
+
|
|
106
|
+
# Build for both Windows architectures
|
|
107
|
+
npm run make:win:all
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Available Scripts
|
|
111
|
+
|
|
112
|
+
- `npm start` - Start the application in development mode
|
|
113
|
+
- `npm run make` - Create distributable packages for your current platform
|
|
114
|
+
- `npm run make:win:x64` - Build Windows x64 package
|
|
115
|
+
- `npm run make:win:arm64` - Build Windows ARM64 package
|
|
116
|
+
- `npm run make:win:all` - Build Windows packages for both architectures
|
|
117
|
+
- `npm run make:mac` - Build macOS package
|
|
118
|
+
- `npm run make:linux` - Build Linux package
|
|
119
|
+
- `npm run make:all` - Build packages for all platforms and architectures
|
|
120
|
+
|
|
121
|
+
## Technologies Used
|
|
122
|
+
|
|
123
|
+
- Electron
|
|
124
|
+
- React
|
|
125
|
+
- TypeScript
|
|
126
|
+
- Webpack
|
|
127
|
+
- Google Generative AI API
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
132
|
+
|
|
133
|
+
## Author
|
|
134
|
+
|
|
135
|
+
Alex
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-client.d.ts","sourceRoot":"","sources":["../lib/gemini-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB5E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { GoogleGenAI } from '@google/genai';
|
|
2
|
+
export async function callGemini(options) {
|
|
3
|
+
const { text, language, context, model, apiKey } = options;
|
|
4
|
+
const ai = new GoogleGenAI({ apiKey });
|
|
5
|
+
const prompt = `You are a professional subtitle translator. Translate the following SRT subtitle batch to ${language}.
|
|
6
|
+
|
|
7
|
+
CRITICAL RULES:
|
|
8
|
+
- Output ONLY the translated SRT subtitle text
|
|
9
|
+
- Preserve ALL subtitle numbers and timecodes EXACTLY as given
|
|
10
|
+
- Do NOT add any markdown formatting, backticks, or code blocks
|
|
11
|
+
- Do NOT add any explanations, notes, or comments
|
|
12
|
+
- Do NOT modify timecodes or subtitle numbering
|
|
13
|
+
- Maintain the exact SRT format structure
|
|
14
|
+
|
|
15
|
+
Context: ${context}
|
|
16
|
+
|
|
17
|
+
${text}`;
|
|
18
|
+
const response = await ai.models.generateContent({
|
|
19
|
+
model,
|
|
20
|
+
contents: prompt,
|
|
21
|
+
});
|
|
22
|
+
return response.text || '';
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=gemini-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-client.js","sourceRoot":"","sources":["../lib/gemini-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAU5C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA0B;IACzD,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3D,MAAM,EAAE,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAEvC,MAAM,MAAM,GAAG,6FAA6F,QAAQ;;;;;;;;;;WAU3G,OAAO;;EAEhB,IAAI,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC;QAC/C,KAAK;QACL,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;AAC7B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"srt-parser.d.ts","sourceRoot":"","sources":["../lib/srt-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA0BjD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAIrD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function parseSrt(text) {
|
|
2
|
+
const entries = [];
|
|
3
|
+
const normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
4
|
+
const blocks = normalized.split(/\n\n+/);
|
|
5
|
+
for (const block of blocks) {
|
|
6
|
+
const trimmed = block.trim();
|
|
7
|
+
if (!trimmed)
|
|
8
|
+
continue;
|
|
9
|
+
const lines = trimmed.split('\n');
|
|
10
|
+
if (lines.length < 2)
|
|
11
|
+
continue;
|
|
12
|
+
const indexLine = lines[0].trim();
|
|
13
|
+
const index = parseInt(indexLine, 10);
|
|
14
|
+
if (isNaN(index))
|
|
15
|
+
continue;
|
|
16
|
+
const timestampLine = lines[1].trim();
|
|
17
|
+
if (!timestampLine.includes('-->'))
|
|
18
|
+
continue;
|
|
19
|
+
const textLines = lines.slice(2);
|
|
20
|
+
const text = textLines.join('\n');
|
|
21
|
+
entries.push({ index, timestamp: timestampLine, text });
|
|
22
|
+
}
|
|
23
|
+
return entries;
|
|
24
|
+
}
|
|
25
|
+
export function formatSrt(entries) {
|
|
26
|
+
return entries
|
|
27
|
+
.map((entry) => `${entry.index}\n${entry.timestamp}\n${entry.text}`)
|
|
28
|
+
.join('\n\n') + '\n';
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=srt-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"srt-parser.js","sourceRoot":"","sources":["../lib/srt-parser.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAE/B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,KAAK,CAAC;YAAE,SAAS;QAE3B,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAE7C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAmB;IAC3C,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;SACnE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translator.d.ts","sourceRoot":"","sources":["../lib/translator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAY,MAAM,SAAS,CAAC;AA0BtE,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA+CnF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { parseSrt, formatSrt } from './srt-parser';
|
|
2
|
+
import { callGemini } from './gemini-client';
|
|
3
|
+
const DEFAULT_MODEL = 'gemini-2.0-flash';
|
|
4
|
+
const DEFAULT_BATCH_SIZE = 50;
|
|
5
|
+
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
|
+
async function translateBatch(entries, language, context, model, apiKey) {
|
|
7
|
+
const text = formatSrt(entries);
|
|
8
|
+
try {
|
|
9
|
+
return await callGemini({ text, language, context, model, apiKey });
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
console.error('Translation error, retrying in 10 seconds...', error);
|
|
13
|
+
await wait(10000);
|
|
14
|
+
return translateBatch(entries, language, context, model, apiKey);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function translate(options) {
|
|
18
|
+
const { text, targetLanguage, apiKey, model = DEFAULT_MODEL, batchSize = DEFAULT_BATCH_SIZE, context = '', onProgress, signal, } = options;
|
|
19
|
+
const entries = parseSrt(text);
|
|
20
|
+
if (entries.length === 0) {
|
|
21
|
+
return { translatedText: '', totalBatches: 0, completedBatches: 0 };
|
|
22
|
+
}
|
|
23
|
+
const totalBatches = Math.ceil(entries.length / batchSize);
|
|
24
|
+
let completedBatches = 0;
|
|
25
|
+
let translatedText = '';
|
|
26
|
+
if (onProgress) {
|
|
27
|
+
onProgress(0);
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < entries.length; i += batchSize) {
|
|
30
|
+
if (signal?.aborted) {
|
|
31
|
+
throw new Error('Translation cancelled');
|
|
32
|
+
}
|
|
33
|
+
const batch = entries.slice(i, i + batchSize);
|
|
34
|
+
const batchResult = await translateBatch(batch, targetLanguage, context, model, apiKey);
|
|
35
|
+
translatedText += (translatedText ? '\n' : '') + batchResult;
|
|
36
|
+
completedBatches++;
|
|
37
|
+
if (onProgress) {
|
|
38
|
+
onProgress(Math.min(completedBatches / totalBatches, 1));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (onProgress) {
|
|
42
|
+
onProgress(1);
|
|
43
|
+
}
|
|
44
|
+
return { translatedText, totalBatches, completedBatches };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=translator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translator.js","sourceRoot":"","sources":["../lib/translator.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface SrtEntry {
|
|
2
|
+
index: number;
|
|
3
|
+
timestamp: string;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TranslateOptions {
|
|
7
|
+
text: string;
|
|
8
|
+
targetLanguage: string;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
batchSize?: number;
|
|
12
|
+
context?: string;
|
|
13
|
+
onProgress?: (progress: number) => void;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export interface TranslateResult {
|
|
17
|
+
translatedText: string;
|
|
18
|
+
totalBatches: number;
|
|
19
|
+
completedBatches: number;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC1B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-sub-translator",
|
|
3
|
+
"productName": "ai-sub-translator",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "AI-powered subtitle translator using Google Gemini. Translates SRT subtitles to any language.",
|
|
6
|
+
"main": ".webpack/main",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "electron-forge start",
|
|
20
|
+
"package": "electron-forge package",
|
|
21
|
+
"make": "electron-forge make",
|
|
22
|
+
"make:mac": "electron-forge make --platform darwin",
|
|
23
|
+
"make:win:x64": "electron-forge make --platform win32 --arch x64",
|
|
24
|
+
"make:win:arm64": "electron-forge make --platform win32 --arch arm64",
|
|
25
|
+
"make:win": "npm run make:win:x64 && npm run make:win:arm64",
|
|
26
|
+
"make:linux": "npm run make:linux:x64 && npm run make:linux:arm64",
|
|
27
|
+
"make:linux:x64": "electron-forge make --platform linux --arch x64",
|
|
28
|
+
"make:linux:arm64": "electron-forge make --platform linux --arch arm64",
|
|
29
|
+
"make:all": "npm run make:mac && npm run make:win && npm run make:linux",
|
|
30
|
+
"publish": "electron-forge publish",
|
|
31
|
+
"lint": "eslint --ext .ts,.tsx .",
|
|
32
|
+
"build:lib": "tsc -p tsconfig.lib.json",
|
|
33
|
+
"test:lib": "node --experimental-vm-modules node_modules/.bin/jest --config jest.config.js",
|
|
34
|
+
"prepublishOnly": "npm run build:lib"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"subtitles",
|
|
38
|
+
"srt",
|
|
39
|
+
"translator",
|
|
40
|
+
"gemini",
|
|
41
|
+
"ai",
|
|
42
|
+
"translation"
|
|
43
|
+
],
|
|
44
|
+
"author": {
|
|
45
|
+
"name": "aka",
|
|
46
|
+
"email": "aka@aka.aka"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@aws-sdk/client-s3": "^3.787.0",
|
|
51
|
+
"@electron-forge/cli": "^7.8.0",
|
|
52
|
+
"@electron-forge/maker-deb": "^7.8.0",
|
|
53
|
+
"@electron-forge/maker-rpm": "^7.8.0",
|
|
54
|
+
"@electron-forge/maker-squirrel": "^7.8.0",
|
|
55
|
+
"@electron-forge/maker-zip": "^7.8.0",
|
|
56
|
+
"@electron-forge/plugin-auto-unpack-natives": "^7.8.0",
|
|
57
|
+
"@electron-forge/plugin-fuses": "^7.8.0",
|
|
58
|
+
"@electron-forge/plugin-webpack": "^7.8.0",
|
|
59
|
+
"@electron-forge/publisher-github": "^7.8.0",
|
|
60
|
+
"@electron/fuses": "^1.8.0",
|
|
61
|
+
"@types/jest": "^29.5.14",
|
|
62
|
+
"@types/react": "^19.1.2",
|
|
63
|
+
"@types/react-dom": "^19.1.2",
|
|
64
|
+
"@types/unzipper": "^0.10.11",
|
|
65
|
+
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
66
|
+
"@typescript-eslint/parser": "^5.62.0",
|
|
67
|
+
"@vercel/webpack-asset-relocator-loader": "^1.7.3",
|
|
68
|
+
"css-loader": "^6.11.0",
|
|
69
|
+
"electron": "35.1.5",
|
|
70
|
+
"electron-squirrel-startup": "^1.0.1",
|
|
71
|
+
"eslint": "^8.57.1",
|
|
72
|
+
"eslint-plugin-import": "^2.31.0",
|
|
73
|
+
"fork-ts-checker-webpack-plugin": "^7.3.0",
|
|
74
|
+
"jayson": "^4.2.0",
|
|
75
|
+
"jest": "^29.7.0",
|
|
76
|
+
"node-loader": "^2.1.0",
|
|
77
|
+
"react": "^19.1.0",
|
|
78
|
+
"react-dom": "^19.1.0",
|
|
79
|
+
"style-loader": "^3.3.4",
|
|
80
|
+
"tar": "^7.4.3",
|
|
81
|
+
"ts-jest": "^29.4.6",
|
|
82
|
+
"ts-loader": "^9.5.2",
|
|
83
|
+
"ts-node": "^10.9.2",
|
|
84
|
+
"typescript": "^5.9.3",
|
|
85
|
+
"unzipper": "^0.12.3",
|
|
86
|
+
"xz-decompress": "^0.2.2"
|
|
87
|
+
},
|
|
88
|
+
"dependencies": {
|
|
89
|
+
"@google/genai": "^0.8.0"
|
|
90
|
+
}
|
|
91
|
+
}
|