401 lines
10 KiB
JavaScript
401 lines
10 KiB
JavaScript
// This is main process of Electron, started as first thing when your
|
|
// app starts. It runs through entire life of your application.
|
|
// It doesn't have any windows which you can see on screen, but we can open
|
|
// window from here.
|
|
|
|
import path from "path";
|
|
import url from "url";
|
|
import os from 'os';
|
|
import { app, Menu, ipcMain, Tray, dialog } from "electron";
|
|
import createWindow from "./helpers/window";
|
|
import { autoUpdater } from 'electron-updater'
|
|
import appPath from './electronAppPath'
|
|
import dbPatcher from './dbPatcher'
|
|
|
|
import { devMenuTemplate } from "./menu/dev_menu_template";
|
|
import { editMenuTemplate } from "./menu/edit_menu_template";
|
|
import { settingsMenuTemplate } from "./menu/config_menu_template";
|
|
import { aboutMenuTemplate } from "./menu/about_menu_template";
|
|
import { manageMenuTemplate } from "./menu/manage_menu_template";
|
|
|
|
// Special module holding environment variables which you declared
|
|
// in config/env_xxx.json file.
|
|
import env from "env";
|
|
|
|
const { spawn, exec } = require('child_process')
|
|
const fs = require('fs')
|
|
const iconv = require('iconv-lite');
|
|
|
|
// plugins and dev tool
|
|
require('electron-context-menu')({})
|
|
|
|
const setApplicationMenu = () => {
|
|
const menus = [editMenuTemplate, manageMenuTemplate, settingsMenuTemplate, aboutMenuTemplate];
|
|
if (env.name !== "production") {
|
|
menus.push(devMenuTemplate);
|
|
}
|
|
// append version as disabled menu item
|
|
menus.push({
|
|
label: app.getVersion()
|
|
})
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate(menus));
|
|
};
|
|
|
|
// Save userData in separate folders for each environment.
|
|
// Thanks to this you can use production and development versions of the app
|
|
// on same machine like those are two separate apps.
|
|
if (env.name !== "production") {
|
|
const userDataPath = app.getPath("userData");
|
|
app.setPath("userData", `${userDataPath} (${env.name})`);
|
|
}
|
|
// portative version
|
|
let portative = false
|
|
if(env.name === "production") {
|
|
if(fs.existsSync(path.dirname(process.execPath) + `/data`))
|
|
{
|
|
portative = true;
|
|
app.setPath("userData", path.dirname(process.execPath) + `/data`);
|
|
}
|
|
}
|
|
|
|
const resourcesPath = env.name === "production" ? process.resourcesPath : 'resources'
|
|
|
|
const spiderCall = require('./spider')
|
|
const appConfig = require('./config')
|
|
|
|
let mainWindow = undefined
|
|
let sphinx = undefined
|
|
let spider = undefined
|
|
|
|
const util = require('util');
|
|
if (!fs.existsSync(app.getPath("userData"))){
|
|
fs.mkdirSync(app.getPath("userData"));
|
|
}
|
|
const logFile = fs.createWriteStream(app.getPath("userData") + '/rats.log', {flags : 'w'});
|
|
const logStdout = process.stdout;
|
|
|
|
console.log = (...d) => {
|
|
const date = (new Date).toLocaleTimeString()
|
|
logFile.write(`[${date}] ` + util.format(...d) + '\n');
|
|
logStdout.write(util.format(...d) + '\n');
|
|
};
|
|
|
|
// print os info
|
|
console.log('Rats', app.getVersion())
|
|
console.log('Platform:', os.platform())
|
|
console.log('Arch:', os.arch())
|
|
console.log('OS Release:', os.release())
|
|
console.log('CPU:', os.cpus()[0].model)
|
|
console.log('CPU Logic cores:', os.cpus().length)
|
|
console.log('Total memory:', (os.totalmem() / (1024 * 1024)).toFixed(2), 'MB')
|
|
console.log('Free memory:', (os.freemem() / (1024 * 1024)).toFixed(2), 'MB')
|
|
|
|
if(portative)
|
|
console.log('portative compability')
|
|
|
|
const shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
|
|
// Someone tried to run a second instance, we should focus our window.
|
|
console.log('openned second application, just focus this one')
|
|
if (mainWindow) {
|
|
if (mainWindow.isMinimized())
|
|
mainWindow.restore();
|
|
mainWindow.focus();
|
|
}
|
|
});
|
|
|
|
if (shouldQuit) {
|
|
console.log('closed because of second application')
|
|
app.exit(0);
|
|
}
|
|
|
|
const writeSphinxConfig = (path, dbPath) => {
|
|
let config = `
|
|
index torrents
|
|
{
|
|
type = rt
|
|
path = ${dbPath}/database/torrents
|
|
|
|
rt_attr_string = hash
|
|
rt_attr_string = name
|
|
rt_field = nameIndex
|
|
rt_attr_bigint = size
|
|
rt_attr_uint = files
|
|
rt_attr_uint = piecelength
|
|
rt_attr_timestamp = added
|
|
rt_attr_string = ipv4
|
|
rt_attr_uint = port
|
|
rt_attr_string = contentType
|
|
rt_attr_string = contentCategory
|
|
rt_attr_uint = seeders
|
|
rt_attr_uint = leechers
|
|
rt_attr_uint = completed
|
|
rt_attr_timestamp = trackersChecked
|
|
rt_attr_uint = good
|
|
rt_attr_uint = bad
|
|
}
|
|
|
|
index files
|
|
{
|
|
type = rt
|
|
path = ${dbPath}/database/files
|
|
|
|
rt_attr_string = path
|
|
rt_field = pathIndex
|
|
rt_attr_string = hash
|
|
rt_attr_bigint = size
|
|
}
|
|
|
|
index version
|
|
{
|
|
type = rt
|
|
path = ${dbPath}/database/version
|
|
|
|
rt_attr_uint = version
|
|
rt_field = versionIndex
|
|
}
|
|
|
|
searchd
|
|
{
|
|
listen = 9312
|
|
listen = 9306:mysql41
|
|
read_timeout = 5
|
|
max_children = 30
|
|
seamless_rotate = 1
|
|
preopen_indexes = 1
|
|
unlink_old = 1
|
|
workers = threads # for RT to work
|
|
pid_file = ${path}/searchd.pid
|
|
log = ${path}/searchd.log
|
|
query_log = ${path}/query.log
|
|
binlog_path = ${path}
|
|
}
|
|
`;
|
|
|
|
// clear dir in test env
|
|
if(env.name === 'test')
|
|
{
|
|
if (fs.existsSync(`${dbPath}/database`)) {
|
|
fs.readdirSync(`${dbPath}/database`).forEach(function(file, index){
|
|
const curPath = `${dbPath}/database` + "/" + file;
|
|
if (!fs.lstatSync(curPath).isDirectory()) {
|
|
fs.unlinkSync(curPath);
|
|
}
|
|
});
|
|
|
|
fs.readdirSync(path).forEach(function(file, index){
|
|
if(!file.startsWith('binlog'))
|
|
return;
|
|
const curPath = path + "/" + file;
|
|
if (!fs.lstatSync(curPath).isDirectory()) {
|
|
fs.unlinkSync(curPath);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if (!fs.existsSync(`${dbPath}/database`)){
|
|
fs.mkdirSync(`${dbPath}/database`);
|
|
}
|
|
|
|
if(/^win/.test(process.platform))
|
|
config = iconv.encode(config, 'win1251')
|
|
|
|
fs.writeFileSync(`${path}/sphinx.conf`, config)
|
|
console.log(`writed sphinx config to ${path}`)
|
|
console.log('db path:', dbPath)
|
|
}
|
|
|
|
const sphinxPath = path.resolve(appPath('searchd'))
|
|
console.log('Sphinx Path:', sphinxPath)
|
|
|
|
const startSphinx = (callback) => {
|
|
const sphinxConfigDirectory = app.getPath("userData")
|
|
appConfig['dbPath'] = appConfig.dbPath && appConfig.dbPath.length > 0 ? appConfig.dbPath : sphinxConfigDirectory;
|
|
// on portable dir can move database directory
|
|
if(!fs.existsSync(appConfig.dbPath) && fs.existsSync(sphinxConfigDirectory))
|
|
{
|
|
appConfig['dbPath'] = sphinxConfigDirectory
|
|
}
|
|
|
|
writeSphinxConfig(sphinxConfigDirectory, appConfig.dbPath)
|
|
|
|
const config = `${sphinxConfigDirectory}/sphinx.conf`
|
|
const options = ['--config', config]
|
|
if(!(/^win/.test(process.platform)))
|
|
{
|
|
options.push('--nodetach')
|
|
}
|
|
sphinx = spawn(sphinxPath, options)
|
|
|
|
sphinx.stdout.on('data', (data) => {
|
|
console.log(`sphinx: ${data}`)
|
|
if (data.includes('accepting connections')) {
|
|
console.log('catched sphinx start')
|
|
if(callback)
|
|
callback()
|
|
}
|
|
})
|
|
|
|
sphinx.on('close', (code, signal) => {
|
|
console.log(`sphinx closed with code ${code} and signal ${signal}`)
|
|
app.quit()
|
|
})
|
|
|
|
sphinx.stop = () => {
|
|
console.log('sphinx closing...')
|
|
exec(`"${sphinxPath}" --config "${config}" --stopwait`)
|
|
}
|
|
}
|
|
|
|
// log autoupdate
|
|
const log = require('electron-log')
|
|
log.transports.file.level = false;
|
|
log.transports.console.level = false;
|
|
log.transports.console = function(msg) {
|
|
const text = util.format.apply(util, msg.data);
|
|
console.log(text);
|
|
};
|
|
autoUpdater.logger = log;
|
|
|
|
autoUpdater.on('update-downloaded', () => {
|
|
console.log('update-downloaded lats quitAndInstall');
|
|
if (env.name === "production") {
|
|
dialog.showMessageBox({
|
|
type: 'info',
|
|
title: 'Found Updates',
|
|
message: 'Found updates, do you want update now?',
|
|
buttons: ['Sure', 'No']
|
|
}, (buttonIndex) => {
|
|
if (buttonIndex === 0) {
|
|
const isSilent = true;
|
|
const isForceRunAfter = true;
|
|
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
let tray = undefined
|
|
|
|
app.on("ready", () => {
|
|
startSphinx(() => {
|
|
|
|
mainWindow = createWindow("main", {
|
|
width: 1000,
|
|
height: 600
|
|
});
|
|
|
|
dbPatcher(() => {
|
|
setApplicationMenu();
|
|
|
|
mainWindow.loadURL(
|
|
url.format({
|
|
pathname: path.join(__dirname, "app.html"),
|
|
protocol: "file:",
|
|
slashes: true
|
|
})
|
|
);
|
|
|
|
if (env.name === "development") {
|
|
mainWindow.openDevTools();
|
|
}
|
|
|
|
if(process.platform === 'darwin')
|
|
tray = new Tray(`${resourcesPath}/icons/19x19.png`)
|
|
else
|
|
tray = new Tray(`${resourcesPath}/icons/512x512.png`)
|
|
|
|
tray.on('click', () => {
|
|
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
|
|
})
|
|
mainWindow.on('show', () => {
|
|
tray.setHighlightMode('always')
|
|
})
|
|
mainWindow.on('hide', () => {
|
|
tray.setHighlightMode('never')
|
|
})
|
|
mainWindow.on('closed', () => {
|
|
mainWindow = undefined
|
|
})
|
|
|
|
mainWindow.on('minimize', (event) => {
|
|
event.preventDefault();
|
|
mainWindow.hide();
|
|
});
|
|
|
|
var contextMenu = Menu.buildFromTemplate([
|
|
{ label: 'Show', click: function(){
|
|
mainWindow.show();
|
|
} },
|
|
{ label: 'Quit', click: function(){
|
|
app.isQuiting = true;
|
|
if (sphinx)
|
|
stop()
|
|
else
|
|
app.quit()
|
|
} }
|
|
]);
|
|
|
|
tray.setContextMenu(contextMenu)
|
|
tray.setToolTip('Rats on The Boat search')
|
|
|
|
mainWindow.webContents.on('will-navigate', e => { e.preventDefault() })
|
|
mainWindow.webContents.on('new-window', (event, url, frameName) => {
|
|
if(frameName == '_self')
|
|
{
|
|
event.preventDefault()
|
|
mainWindow.loadURL(url)
|
|
}
|
|
})
|
|
|
|
if (env.name === "production" && !portative) { autoUpdater.checkForUpdates() }
|
|
|
|
spider = spiderCall((...data) => {
|
|
if(mainWindow)
|
|
mainWindow.webContents.send(...data)
|
|
}, (message, callback) => {
|
|
ipcMain.on(message, (event, arg) => {
|
|
if(Array.isArray(arg) && typeof arg[arg.length - 1] === 'object' && arg[arg.length - 1].callback)
|
|
{
|
|
const id = arg[arg.length - 1].callback
|
|
arg[arg.length - 1] = (responce) => {
|
|
mainWindow.webContents.send('callback', id, responce)
|
|
}
|
|
}
|
|
callback.apply(null, arg)
|
|
})
|
|
}, app.getPath("userData"), app.getVersion(), env.name)
|
|
}, mainWindow)
|
|
})
|
|
});
|
|
|
|
let stopProtect = false
|
|
const stop = () => {
|
|
if(stopProtect)
|
|
return
|
|
stopProtect = true
|
|
|
|
if(tray)
|
|
tray.destroy()
|
|
|
|
if(spider)
|
|
{
|
|
spider.stop(() => sphinx.stop())
|
|
}
|
|
else
|
|
{
|
|
sphinx.stop()
|
|
}
|
|
}
|
|
|
|
app.on("window-all-closed", () => {
|
|
if (sphinx)
|
|
stop()
|
|
else
|
|
app.quit()
|
|
});
|
|
|
|
app.on('before-quit', () => {
|
|
if (sphinx)
|
|
stop()
|
|
}) |