Skip to content

Commit 2677904

Browse files
authored
Merge pull request #36 from hockyy/feature/34
[#34] Add support to watch youtube video in Miteiru
2 parents 1a2bb9b + 89f347a commit 2677904

File tree

11 files changed

+641
-114
lines changed

11 files changed

+641
-114
lines changed

Diff for: copy.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const fs = require('fs-extra');
2+
const path = require('path');
3+
4+
const sourceDir = path.join(__dirname, 'renderer', 'public');
5+
const destDir = path.join(__dirname, 'app');
6+
7+
fs.copy(sourceDir, destDir)
8+
.then(() => console.log('Files copied successfully!'))
9+
.catch(err => console.error(err));

Diff for: main/background.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import fs from "fs";
1414
import path from "path";
1515
import {getTokenizer} from "kuromojin";
16+
import {getSubtitles} from "./helpers/getSubtitles";
1617

1718

1819
const isProd: boolean = process.env.NODE_ENV === 'production';
@@ -118,6 +119,16 @@ if (isProd) {
118119
ipcMain.handle('tags', (event) => {
119120
return JMDict.tags;
120121
})
122+
ipcMain.handle('getYoutubeSubtitle', async (event, videoID, lang) => {
123+
// Fetching Subtitles
124+
try {
125+
return await getSubtitles({videoID, lang})
126+
} catch (error) {
127+
console.error('Error fetching subtitles:', error);
128+
return []
129+
}
130+
131+
})
121132
ipcMain.handle('pickDirectory', async (event) => {
122133
return await dialog.showOpenDialog({
123134
properties:
@@ -160,7 +171,7 @@ if (isProd) {
160171
if (filePath && !canceled) {
161172
fs.writeFile(filePath, saveData, (err) => {
162173
if (err) throw err;
163-
console.log('The file has been saved!');
174+
console.info('The file has been saved!');
164175
});
165176
}
166177

@@ -271,7 +282,7 @@ if (isProd) {
271282
getTokenizer({dicPath: path.join(__dirname, 'dict/')}).then(loadedTokenizer => {
272283
tokenizer = loadedTokenizer;
273284
}).catch(e => {
274-
console.log(e)
285+
console.error(e)
275286
})
276287
ipcMain.handle('tokenizeUsingKuromoji', async (event, sentence) => {
277288
return tokenizer.tokenizeForSentence(sentence);

Diff for: main/helpers/getSubtitles.ts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* @flow */
2+
3+
import axios from 'axios';
4+
import {find} from 'lodash';
5+
import {decode} from 'html-entities';
6+
7+
function stripTags(input, allowedTags = [], replacement = '') {
8+
// Create a string of allowed tags, joined by '|'
9+
let tags = allowedTags.join('|');
10+
11+
// Create a new RegExp object
12+
let regex = new RegExp(`<(?!\/?(${tags})[^>]*)\/?.*?>`, 'g');
13+
14+
// Replace disallowed tags with replacement string
15+
return input.replace(regex, replacement);
16+
}
17+
18+
export async function getSubtitles({videoID, lang = 'ja'}) {
19+
const {data} = await axios.get(
20+
`https://youtube.com/watch?v=${videoID}`
21+
);
22+
23+
// * ensure we have access to captions data
24+
if (!data.includes('captionTracks'))
25+
throw new Error(`Could not find captions for video: ${videoID}`);
26+
27+
const regex = /({"captionTracks":.*isTranslatable":(true|false)}])/;
28+
const [match] = regex.exec(data);
29+
const {captionTracks} = JSON.parse(`${match}}`);
30+
31+
const subtitle =
32+
find(captionTracks, {
33+
vssId: `.${lang}`,
34+
}) ||
35+
find(captionTracks, {
36+
vssId: `a.${lang}`,
37+
}) ||
38+
find(captionTracks, ({vssId}) => vssId && vssId.match(`.${lang}`));
39+
40+
// * ensure we have found the correct subtitle lang
41+
if (!subtitle || (subtitle && !subtitle.baseUrl))
42+
throw new Error(`Could not find ${lang} captions for ${videoID}`);
43+
44+
const {data: transcript} = await axios.get(subtitle.baseUrl);
45+
return transcript
46+
.replace('<?xml version="1.0" encoding="utf-8" ?><transcript>', '')
47+
.replace('</transcript>', '')
48+
.split('</text>')
49+
.filter(line => line && line.trim())
50+
.map(line => {
51+
const startRegex = /start="([\d.]+)"/;
52+
const durRegex = /dur="([\d.]+)"/;
53+
54+
const [, start] = startRegex.exec(line);
55+
const [, dur] = durRegex.exec(line);
56+
57+
const htmlText = line
58+
.replace(/<text.+>/, '')
59+
.replace(/&amp;/gi, '&')
60+
.replace(/<\/?[^>]+(>|$)/g, '');
61+
const decodedText = decode(htmlText);
62+
const text = stripTags(decodedText);
63+
64+
return {
65+
start,
66+
dur,
67+
text,
68+
};
69+
});
70+
}

0 commit comments

Comments
 (0)