Skip to content

Instantly share code, notes, and snippets.

@temberature
Last active August 24, 2022 23:07
Show Gist options
  • Save temberature/7a02987f4f9a7c43dd0d0882542ef5bf to your computer and use it in GitHub Desktop.
Save temberature/7a02987f4f9a7c43dd0d0882542ef5bf to your computer and use it in GitHub Desktop.
rga client
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title>Hello World!</title>
</head>
<body>
<!-- <video controls autoplay>
<source src="file:///Volumes/video/Subtitles/The Big Bang Theory Season 1-11 Complete 720p/The Big Bang Theory Season 1/The.Big.Bang.Theory.S01E17.The.Tangerine.Factor.1080p.BluRay.x264-CiNEFiLE.English.mkv" type="video/mp4">
</video> -->
<div class="query">
<input type="text" value="/usr/local/bin/rga 'intervene' --json -n -C 4 /Volumes/video/Subtitles/" />
<button class="searchBtn">搜索</button>
</div>
<div class="multiChoice">
<video data-start="10" data-end="20" controls autoplay>
<source src="file:///Volumes/video/Subtitles/The Big Bang Theory Season 1-11 Complete 720p/The Big Bang Theory Season 1/The.Big.Bang.Theory.S01E17.The.Tangerine.Factor.1080p.BluRay.x264-CiNEFiLE.English.mkv">
</video>
<div class="subtitle">dawdaffawf</div>
</div>
<div class="matches">
<div>
<div class="filename">
The.Big.Bang.Theory.S09E16.The.Positive.Negative.Reaction.720p.BluRay.x264.Pahe.in.vtt
</div>
<ul>
<li>
<span class="line_number">403</span>:What, are you not h<span
class="query"
>app</span>y about this?
</li>
<li>
<span class="line_number">596</span>:so this doesn't h
<span class="query">app</span>en to us?
</li>
<li>
<span class="line_number">765</span>:all
<span class="query">App</span>le does
</li>
<li>
<span class="line_number">1320</span>:I
<span class="query">app</span>reciate this, but you really don--
</li>
<li>
<span class="line_number">1383</span>:Well, I am so h
<span class="query">app</span>y
</li>
</ul>
</div>
</div>
<hr>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
<!-- You can also require other files to run in this process -->
<script src="./renderer.js"></script>
</body>
</html>
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const { exec, spawn } = require("child_process");
let win;
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
webSecurity: true,
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
win = mainWindow;
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
ipcMain.handle('my-invokable-ipc', async (event, ...args) => {
console.log(args)
var c = args[0].split(' ');
var command = c.shift();
const process = await spawn(command, c, {shell: true})
console.log(process);
let result = '';
process.stdout.on('data', data => {
console.log(data);
result += data.toString();
// do something with the data here
// win.webContents.send('asynchronous-message', data.toString());
})
// process.stdout.on('drain', () => {
// console.log(args)
// win.webContents.send('asynchronous-message', result);
// // final checks (e.g. - expect) go here
// })
process.on('exit', () => {
console.log(args)
win.webContents.send('asynchronous-message', result);
// final checks (e.g. - expect) go here
})
})
{
"name": "american-role-adopt-oqmmz",
"productName": "american-role-adopt-oqmmz",
"description": "My Electron application description",
"keywords": [],
"main": "./main.js",
"version": "1.0.0",
"author": "tong",
"scripts": {
"start": "electron ."
},
"dependencies": {},
"devDependencies": {
"electron": "19.0.6"
}
}
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
const { shell } = require('electron')
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
// assuming $ is jQuery
document.addEventListener('click', function (event) {
const target = event.target;
if (target.nodeName === 'A') {
event.preventDefault();
shell.openExternal(target.getAttribute('href'));
}
});
document.addEventListener('loadedmetadata', function (event) {
console.log(event);
const target = event.target;
if (target.nodeName === 'VIDEO') {
event.preventDefault();
target.currentTime = target.getAttribute('data-start');
}
});
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process because
// `nodeIntegration` is turned off. Use `preload.js` to
// selectively enable features needed in the rendering
// process.
const { app, BrowserWindow, ipcRenderer } = require('electron')
var $ = document.querySelectorAll;
document.querySelectorAll('.searchBtn')[0].addEventListener('click', function (t, e) {
console.log(t, e);
(async () => {
document.querySelectorAll('.matches')[0].innerHTML = '';
const result = await ipcRenderer.invoke('my-invokable-ipc', document.querySelectorAll('input')[0].value)
// ...
})()
const transaction = db.transaction(["customers"]);
const objectStore = transaction.objectStore("customers");
const request = objectStore.get(1);
request.onerror = (event) => {
// Handle errors!
};
request.onsuccess = (event) => {
// Do something with the request.result!
console.log(request.result);
var record = request.result;
var multiChoice = createElementFromHTML(`<div class="multiChoice">
<video data-start="${record.start}" data-end="${record.end}" autoplay>
<source src="file://${record.path}">
<source src="file://${record.path.replace('mp4', 'mkv')}">
</video>
<div class="subtitle">${record.subtitle}<div class="span">${record.span}</div></div>
</div>`)[0];
multiChoice.querySelectorAll('video').forEach(video => {
var start = +video.getAttribute('data-start') + 5, end = +video.getAttribute('data-end') + 5;
video.addEventListener('loadedmetadata', function (event) {
console.log(event);
video.currentTime = start;
});
video.addEventListener('timeupdate', (event) => {
console.log(event);
if(video.currentTime > end) {
video.currentTime = start;
// video.play();
}
console.log('The currentTime attribute has been updated. Again.');
});
})
document.querySelectorAll('.multiChoice')[0].replaceWith(multiChoice)
};
})
const dbName = "the_name";
let db;
const request = indexedDB.open(dbName, 2);
request.onerror = (event) => {
// Handle errors.
};
request.onsuccess = (event) => {
db = event.target.result;
};
request.onupgradeneeded = (event) => {
db = event.target.result;
// Create an objectStore to hold information about our customers. We're
// going to use "ssn" as our key path because it's guaranteed to be
// unique - or at least that's what I was told during the kickoff meeting.
const objectStore = db.createObjectStore("customers", { keyPath: "id", autoIncrement: true });
// Create an index to search customers by name. We may have duplicates
// so we can't use a unique index.
objectStore.createIndex("subtitle", "subtitle", { unique: false });
// Create an index to search customers by email. We want to ensure that
// no two customers have the same email, so use a unique index.
objectStore.createIndex("path", "path", { unique: false });
};
ipcRenderer.on('asynchronous-message', function (evt, message) {
console.log(message); // Returns: {'SAVED': 'File Saved'}
main(message)
});
function main(message) {
var result = readJsonLines(message)
console.log(result);
if (result[0].type === 'summary') {
return;
}
var html = `${(function () {
return result.reduce((p, c) => {
// console.log(c);
let html;
if (c.type == 'begin') {
html = `<div><div class="filename">
${c.data.path.text}
</div><ul>`
} else if (c.type == 'end') {
html = `</ul></div>`;
} else if (c.type == 'summary') {
html = '';
} else {
var text = c.data.lines.text.replace(/(\d\d):(\d\d):(\d\d).(\d\d\d)/g, function (match, p1, p2, p3) {
console.log(match, p1, p2, p3);
let location = (+p1 * 60 * 60) + (+p2 * 60) + (+p3);
location = location - 5 > 0 ? location - 5 : 0;
return `<a href="sioyek://${c.data.path.text.replace('srt', 'mp4')}#${location}">${match}</a><button class="saveBtn">Save</button>`
})
text = text.replace(/Page (\d*)/g, function (match, p1) {
return `<a href="sioyek://${c.data.path.text}#${p1}">${match}</a>`
})
html = `<li>
<span class="line_number">${c.data.line_number}</span>
:<span class="text">${c.data.submatches.length > 0 ? text.replace(c.data.submatches[0].match.text, `<span class="query">${c.data.submatches[0].match.text}</span>`) : text}</span>
</li>`
}
return p + html;
}, '');
})()}`
console.log(html);
if (html == '') {
return;
}
var nodes = createElementFromHTML(html);
// nodes = nodes.length > 0 ? nodes : [nodes];
nodes.forEach(node => {
node.querySelectorAll('.saveBtn').forEach(function (saveBtn) {
saveBtn.addEventListener('click', function (e) {
console.log(e);
var container = this.parentNode.parentNode;
var span = container.querySelector('.text').innerHTML.replaceAll('<button class="saveBtn">Save</button>', '');
var path, start, end;
container.querySelectorAll('a').forEach(function (link, index) {
var parts = link.getAttribute('href').split('#');
path = parts[0].replace('sioyek://', '');
if (index === 0) {
start = +parts[1];
} else {
end = +parts[1];
}
})
var subtitle = '';
container = container.nextElementSibling;
while (container && container.querySelector('.text').innerText.replace(' ', '') != '') {
subtitle += ' ' + container.querySelector('.text').innerText;
container = container.nextElementSibling;
}
console.log(span, path, start, end, subtitle);
const customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
customerObjectStore.add({
subtitle,
path,
span, start, end,
});
this.innerText = 'success';
})
})
setTimeout(function () {
document.querySelectorAll('.matches')[0].appendChild(node);
}, 10);
})
}
function readJsonLines(jsonl) {
return jsonl.split(/(?<=})\n(?={)/).map((jsonStr, index) => {
// console.log(jsonStr);
if (jsonStr[jsonStr.length - 1] !== '}') {
jsonStr = jsonStr.slice(0, -1)
}
const match = JSON.parse(jsonStr.replaceAll('\n', '\\n').replaceAll('\r', '\\r'));
// console.log(index, match);
return match;
});
}
function createElementFromHTML(htmlString) {
var div = document.createElement('div');
div.innerHTML = htmlString.trim();
// Change this to div.childNodes to support multiple top-level nodes.
return div.childNodes;
}
/* styles.css */
/* Add styles here to customize the appearance of your app */
.query {
margin: 50px auto;
text-align: center;
}
input {
width: 50%;
}
.multiChoice {
position: relative;
}
video {
width: 100%;
}
.subtitle {
position: absolute;
bottom: 0;
/* transform: translateY(10px); */
/* padding-top: 5vw; */
color: white;
font-size: 8vw;
text-align: center;
vertical-align: middle;
width: 100%;
height: 100%;
pointer-events: none;
background: rgba(125, 125, 125, 0.7);
}
.span {
font-size: 2vw;
pointer-events: all;
}
.filename {
color: rgb(223, 82, 210)
}
.line_number {
color: rgb(4, 188, 68)
}
.query {
color: rgb(199, 53, 39)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment