Heute, am Freitag, möchte ich über eines meiner Lieblingsprojekte sprechen, welche interessanten Dinge ich bei der Arbeit tun musste und welche Probleme ich für die weitere Entwicklung nicht lösen konnte.
Ich hatte also einige Lieblingsprojekte mit unterschiedlichem Abschluss. Darunter: ein soziales Netzwerk für Autoren, ein CSS-Sprite-Generator, ein Telegramm-Bot für die Datierung nach Interessen und vieles mehr. Heute werden wir über meine neueste Entwicklung sprechen.
Wie viele Menschen heutzutage lerne ich Englisch. Ich denke, viele Menschen wissen auch, dass ein effektiver Ansatz in dieser Angelegenheit das maximale Eintauchen in die Umwelt ist. Telefonschnittstelle in Englisch, Notizen in einem Notizbuch in Englisch, Filme in Englisch mit englischen Untertiteln ansehen. Wenn Sie einen Film im Original ansehen, müssen Sie früher oder später dieses oder jenes Wort oder diesen Satz übersetzen, der alle paar Minuten auf dem Bildschirm flackert. Ohne sie ist überhaupt nichts klar.
Projektidee
Also kam mir die Idee eines Videoplayers mit übersetzbaren Untertiteln. Mit der Anwendung können Sie Wörter und ganze Sätze übersetzen, während Sie einen Film ansehen. Damit müssen Sie nicht zwischen Anwendungen wechseln oder ein Smartphone abholen. Treffen Sie LinguaPlayer .
Das Arbeitsschema ist einfach. Der Benutzer öffnet die Filmdatei und die Untertiteldatei. Sieht den Film wie gewohnt an. Zusätzlich zu den Standard-Hotkeys verfügt er jetzt über Tasten zum separaten Übersetzen jedes Wortes, zum Übersetzen ganzer Sätze und zum Zurückspulen von einer Zeile in eine andere. Es gibt auch eine Übersetzung, indem Sie den Mauszeiger über Wörter bewegen oder das gewünschte Textstück markieren. Die App ist für Windows und MacOS verfügbar. Alle Details finden Sie auf der Bewerbungsseite .
Technologie-Stack
Electron, . . Chromium, -. – . Visual Studio Code, Skype, Slack. Electron API, JavaScript, . . – , . JavaScript, Angular, jQuery, Vue – .
LinguaPlayer , : TypeScript, React, MobX, Webpack. , : , . . , , . . , DOM . , , , .
, . — srt- . – , .
node-webvtt. « ». video- «timeupdate», . , «timeupdate» , . .
hash map. (, ), – , . :
{
// 2
5: [1, 2]
// 3
7: [3, 4, 5]
}
0 4 — . , , — hash map. , . , , . 4 , . , :
// : , ( ), ,
class Cue {
public readonly index: number;
public readonly startTime: number;
public readonly endTime: number;
public readonly text: string;
constructor(index: number, startTime: number, endTime: number, text: string) {
this.index = index;
this.startTime = startTime;
this.endTime = endTime;
this.text = text;
}
}
interface CueIndex {
// ( ) ,
//
[key: number]: number[];
}
class SubtitlesTrack {
private readonly cues: Cue[];
private index: CueIndex = {};
constructor(cues: Cue[]) {
this.cues = cues;
// ,
this.indexCues();
}
private indexCues() {
this.cues.forEach((cue: Cue) => {
//
const startSecond = Math.floor(cue.startTime / 1000);
const endSecond = Math.floor(cue.endTime / 1000);
// ( )
this.addToIndex(startSecond, cue);
// , ,
//
if (endSecond !== startSecond) {
this.addToIndex(endSecond, cue);
}
});
}
private addToIndex(secondNumber: number, cue: Cue): void {
// ,
if (!this.index[secondNumber]) {
this.index[secondNumber] = [];
}
//
this.index[secondNumber].push(cue.index);
}
//
public findCueForTime(timeInSeconds: number): Cue|null {
// timeupdate
//
const flooredTime = Math.floor(timeInSeconds);
//
const cues = this.index[flooredTime];
let currentCue = null;
//
if (cues) {
//
for (let index of cues) {
const cue = this.cues[index];
// ,
if (this.isCueInTime(timeInSeconds, cue)) {
// ,
currentCue = cue;
break;
}
}
}
// null,
return currentCue;
}
public isCueInTime(timeInSeconds: number, cue: Cue): boolean {
const timeInMilliseconds: number = timeInSeconds * 1000;
return timeInMilliseconds >= cue.startTime && timeInMilliseconds <= cue.endTime;
}
}
, , 4 , , 1 4.
node-sentence-tokenizer. div sentence word , . :
import Tokenizer from 'sentence-tokenizer';
function formatCue(text: string): string {
const brMark: string = '[br]';
const tokenizer = new Tokenizer();
//
text = text
.replace(/\r\n/g, ` ${brMark}`)
.replace(/\r/g, ` ${brMark}`)
.replace(/\n/g, ` ${brMark}`);
// text
tokenizer.setEntry(text);
//
const sentenceTokens: string[] = tokenizer.getSentences();
//
const sentencesHtml: string[] = sentenceTokens.map((sentenceToken: string, index: number) => {
//
const wordTokens: string[] = tokenizer.getTokens(index);
//
const wordsHtml: string[] = wordTokens.map((wordToken: string) => {
let brTag: string = '';
// , html
if (wordToken.includes(brMark)) {
wordToken = wordToken.replace(brMark, '');
brTag = '<br/>';
}
// span word br,
return `${brTag}<span class="word">${wordToken}</span>`;
});
// , , span sentence
return `<span class="sentence">${wordsHtml.join(' ')}</span>`;
});
//
const html: string = sentencesHtml.join(' ');
return html;
}
,
, MVP, proof of concept. . , -, Urban Dictionary , . , LinguaLeo Skyeng. . Anki. .
, , . , , . , – Chromium. , , H.264 FLAC MP3. , . – . , , .
Daher ist der Hauptblockierungsfaktor derzeit der Inhalt. Es sollte ohne Probleme in der Anwendung abgespielt werden können, es sollte in der Lage sein, es einfach und schnell zu erhalten, und es sollte auch keine Lizenzen und Urheberrechte verletzen. Sobald das Inhaltsproblem behoben ist, werde ich gerne weiter an dem Projekt arbeiten. In der Zwischenzeit können Sie bei Interesse die Konzeptversion der Anwendung herunterladen und ausprobieren .