index.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. /**
  2. * LiveMe Pro Tools
  3. */
  4. const appName = 'LiveMe Pro Tools'
  5. const { app, BrowserWindow, ipcMain, Menu, shell, dialog } = require('electron')
  6. const { exec } = require('child_process')
  7. const os = require('os')
  8. const fs = require('fs')
  9. const path = require('path')
  10. const request = require('request')
  11. const tarfs = require('tar-fs')
  12. const DataManager = new (require('./datamanager').DataManager)()
  13. const LivemeAPI = require('./livemeapi')
  14. const LiveMe = new LivemeAPI({})
  15. const isDev = require('electron-is-dev')
  16. const ffmpeg = require('fluent-ffmpeg')
  17. const async = require('async')
  18. let mainWindow = null
  19. let playerWindow = null
  20. let bookmarksWindow = null
  21. let chatWindow = null
  22. let wizardWindow = null
  23. let homeWindow = null
  24. let menu = null
  25. let appSettings = require('electron-settings')
  26. function createWindow () {
  27. let isFreshInstall = appSettings.get('general.fresh_install') == null
  28. if (isFreshInstall === true) {
  29. appSettings.set('general', {
  30. fresh_install: true,
  31. playerpath: '',
  32. hide_zeroreplay_fans: false,
  33. hide_zeroreplay_followings: true
  34. })
  35. appSettings.set('position', {
  36. mainWindow: [-1, -1],
  37. playerWindow: [-1, -1],
  38. bookmarksWindow: [-1, -1],
  39. fansWindow: [-1, -1],
  40. followingsWindow: [-1, -1]
  41. })
  42. appSettings.set('size', {
  43. mainWindow: [1024, 600],
  44. playerWindow: [370, 680],
  45. bookmarksWindow: [400, 720]
  46. })
  47. appSettings.set('downloads', {
  48. path: path.join(app.getPath('home'), 'Downloads'),
  49. template: '%%replayid%%',
  50. chunkthreads: 1,
  51. ffmpegquality: 1
  52. })
  53. appSettings.set('lamd', {
  54. enabled: false,
  55. url: 'http://localhost:8280',
  56. handle_downloads: false
  57. })
  58. }
  59. if (!appSettings.get('downloads.path')) {
  60. appSettings.set('downloads', {
  61. path: path.join(app.getPath('home'), 'Downloads'),
  62. template: '%%replayid%%',
  63. chunkthreads: 1,
  64. ffmpegquality: 1
  65. })
  66. }
  67. if (!appSettings.get('downloads.chunks')) {
  68. appSettings.set('downloads.chunks', 1)
  69. }
  70. if (!appSettings.get('lamd.enabled')) {
  71. appSettings.set('lamd', {
  72. enabled: false,
  73. url: 'http://localhost:8280',
  74. handle_downloads: false
  75. })
  76. }
  77. if (!appSettings.get('history.viewed_maxage')) {
  78. appSettings.set('history', {
  79. viewed_maxage: 1
  80. })
  81. }
  82. let test = appSettings.get('position')
  83. if (test.mainWindow[1] === undefined) {
  84. appSettings.set('position', {
  85. mainWindow: [-1, -1],
  86. playerWindow: [-1, -1],
  87. bookmarksWindow: [-1, -1]
  88. })
  89. }
  90. /**
  91. * Create our window definitions
  92. */
  93. // let winposition = appSettings.get('position.mainWindow')
  94. let winsize = appSettings.get('size.mainWindow')
  95. mainWindow = new BrowserWindow({
  96. icon: path.join(__dirname, 'appicon.ico'),
  97. width: winsize[0],
  98. height: winsize[1],
  99. minWidth: 1024,
  100. maxWidth: 1024,
  101. minHeight: 480,
  102. maxHeight: 1200,
  103. autoHideMenuBar: true,
  104. disableAutoHideCursor: true,
  105. titleBarStyle: 'default',
  106. fullscreen: false,
  107. maximizable: false,
  108. frame: false,
  109. show: false,
  110. backgroundColor: '#000000',
  111. webPreferences: {
  112. webSecurity: false,
  113. textAreasAreResizable: false,
  114. plugins: true
  115. }
  116. })
  117. wizardWindow = new BrowserWindow({
  118. icon: path.join(__dirname, 'appicon.ico'),
  119. width: 520,
  120. height: 300,
  121. darkTheme: true,
  122. autoHideMenuBar: false,
  123. disableAutoHideCursor: true,
  124. titleBarStyle: 'default',
  125. resizable: false,
  126. fullscreen: false,
  127. maximizable: false,
  128. show: false,
  129. frame: false,
  130. backgroundColor: 'transparent',
  131. webPreferences: {
  132. webSecurity: false,
  133. textAreasAreResizable: false,
  134. plugins: true
  135. }
  136. })
  137. /**
  138. * Configure our window contents and callbacks
  139. */
  140. mainWindow.loadURL(`file://${__dirname}/app/index.html`)
  141. mainWindow
  142. .on('open', () => {})
  143. .on('close', () => {
  144. appSettings.set('position.mainWindow', mainWindow.getPosition())
  145. appSettings.set('size.mainWindow', mainWindow.getSize())
  146. DataManager.saveToDisk()
  147. if (playerWindow != null) {
  148. playerWindow.close()
  149. }
  150. if (bookmarksWindow != null) {
  151. bookmarksWindow.close()
  152. }
  153. if (chatWindow != null) {
  154. chatWindow.close()
  155. }
  156. mainWindow.webContents.session.clearCache(() => {
  157. // Purge the cache to help avoid eating up space on the drive
  158. })
  159. mainWindow = null
  160. setTimeout(() => {
  161. app.quit()
  162. }, 500)
  163. })
  164. wizardWindow.on('close', () => {
  165. wizardWindow.webContents.session.clearCache(() => {
  166. // Purge the cache to help avoid eating up space on the drive
  167. })
  168. if (mainWindow != null) {
  169. setTimeout(() => {
  170. let pos = appSettings.get('position.mainWindow')
  171. mainWindow.setPosition(pos[0], pos[1], false)
  172. mainWindow.show()
  173. }, 250)
  174. }
  175. wizardWindow = null
  176. })
  177. /**
  178. * Build our application menus using the templates provided
  179. * further down.
  180. */
  181. menu = Menu.buildFromTemplate(getMenuTemplate())
  182. Menu.setApplicationMenu(menu)
  183. global.isDev = isDev
  184. global.LiveMe = LiveMe
  185. global.DataManager = DataManager
  186. DataManager.loadFromDisk()
  187. setTimeout(() => {
  188. const dt = new Date()
  189. let ma = appSettings.get('history.viewed_maxage')
  190. let od = Math.floor((dt.getTime() - (ma * 86400000)) / 1000)
  191. DataManager.unviewProfiles(od, false)
  192. }, 250)
  193. if (isFreshInstall) {
  194. DataManager.disableWrites()
  195. wizardWindow.loadURL(`file://${__dirname}/app/wizard.html`)
  196. wizardWindow.show()
  197. } else {
  198. mainWindow.show()
  199. let pos = appSettings.get('position.mainWindow').length > 1 ? appSettings.get('position.mainWindow') : [null, null]
  200. if (pos[0] != null) {
  201. mainWindow.setPosition(pos[0], pos[1], false)
  202. }
  203. }
  204. }
  205. let shouldQuit = app.makeSingleInstance(function (commandLine, workingDirectory) {
  206. if (mainWindow) {
  207. mainWindow.focus()
  208. }
  209. })
  210. if (shouldQuit) {
  211. app.quit()
  212. process.exit()
  213. }
  214. app.on('ready', () => {
  215. createWindow()
  216. })
  217. app.on('window-all-closed', () => {
  218. app.quit()
  219. })
  220. app.on('activate', () => {
  221. if (mainWindow === null) {
  222. createWindow()
  223. }
  224. })
  225. /**
  226. * IPC Event Handlers
  227. */
  228. ipcMain.on('import-queue', (event, arg) => {})
  229. ipcMain.on('import-users', (event, arg) => {})
  230. ipcMain.on('export-users', (event, arg) => {})
  231. ipcMain.on('downloads-parallel', (event, arg) => {
  232. dlQueue.concurrency = arg
  233. })
  234. ipcMain.on('open-home-window', (event, arg) => {
  235. var homeWindow = new BrowserWindow({
  236. width: 400,
  237. minWidth: 400,
  238. maxWidth: 400,
  239. height: 720,
  240. minHeight: 480,
  241. resizable: true,
  242. darkTheme: false,
  243. autoHideMenuBar: true,
  244. skipTaskbar: false,
  245. backgroundColor: '#000000',
  246. disableAutoHideCursor: true,
  247. titleBarStyle: 'default',
  248. fullscreen: false,
  249. maximizable: false,
  250. closable: true,
  251. frame: false,
  252. show: false
  253. })
  254. homeWindow.setMenu(Menu.buildFromTemplate(getMiniMenuTemplate()))
  255. homeWindow.on('ready-to-show', () => {
  256. homeWindow.show()
  257. }).loadURL(`file://${__dirname}/app/newhome.html`)
  258. })
  259. ipcMain.on('download-replay', (event, arg) => {
  260. DataManager.addToQueueList(arg.videoid)
  261. dlQueue.push(arg.videoid, err => {
  262. if (err) {
  263. mainWindow.webContents.send('download-error', err)
  264. } else {
  265. mainWindow.webContents.send('download-complete', { videoid: arg.videoid })
  266. }
  267. })
  268. })
  269. /**
  270. * Cannot cancel active download, only remove queued entries.
  271. */
  272. ipcMain.on('download-cancel', (event, arg) => {
  273. dlQueue.remove(function (task) {
  274. if (task.data === arg.videoid) {
  275. DataManager.removeFromQueueList(task.data)
  276. return true
  277. }
  278. return false
  279. })
  280. })
  281. /**
  282. * It is done this way in case the API call to jDownloader returns an error or doesn't connect.
  283. */
  284. const dlQueue = async.queue((task, done) => {
  285. // Set custom FFMPEG path if defined
  286. if (appSettings.get('downloads.ffmpeg')) ffmpeg.setFfmpegPath(appSettings.get('downloads.ffmpeg'))
  287. // Get video info
  288. LiveMe.getVideoInfo(task).then(video => {
  289. const path = appSettings.get('downloads.path')
  290. const dt = new Date(video.vtime * 1000)
  291. const mm = dt.getMonth() + 1
  292. const dd = dt.getDate()
  293. let ffmpegOpts = []
  294. let filename = appSettings.get('downloads.template')
  295. .replace(/%%broadcaster%%/g, video.uname)
  296. .replace(/%%longid%%/g, video.userid)
  297. .replace(/%%replayid%%/g, video.vid)
  298. .replace(/%%replayviews%%/g, video.playnumber)
  299. .replace(/%%replaylikes%%/g, video.likenum)
  300. .replace(/%%replayshares%%/g, video.sharenum)
  301. .replace(/%%replaytitle%%/g, video.title ? video.title : 'untitled')
  302. .replace(/%%replayduration%%/g, video.videolength)
  303. .replace(/%%replaydatepacked%%/g, (dt.getFullYear() + (mm < 10 ? '0' : '') + mm + (dd < 10 ? '0' : '') + dd))
  304. .replace(/%%replaydateus%%/g, ((mm < 10 ? '0' : '') + mm + '-' + (dd < 10 ? '0' : '') + dd + '-' + dt.getFullYear()))
  305. .replace(/%%replaydateeu%%/g, ((dd < 10 ? '0' : '') + dd + '-' + (mm < 10 ? '0' : '') + mm + '-' + dt.getFullYear()))
  306. filename = filename.replace(/[/\\?%*:|"<>]/g, '-')
  307. filename = filename.replace(/([^a-z0-9\s]+)/gi, '-')
  308. filename = filename.replace(/[\u{0080}-\u{FFFF}]/gu, '')
  309. filename += '.mp4'
  310. video._filename = filename
  311. mainWindow.webContents.send('download-start', {
  312. videoid: task,
  313. filename: filename
  314. })
  315. switch (parseInt(appSettings.get('downloads.ffmpegquality'))) {
  316. case 2: // Best
  317. ffmpegOpts = [
  318. '-c:v h264',
  319. '-preset fast',
  320. '-c:a copy',
  321. '-bsf:a aac_adtstoasc',
  322. '-vsync 2',
  323. '-movflags faststart'
  324. ]
  325. break
  326. case 1: // Fast
  327. ffmpegOpts = [
  328. '-c:v h264',
  329. '-preset superfast',
  330. '-q:v 0',
  331. '-c:a copy',
  332. '-bsf:a aac_adtstoasc',
  333. '-vsync 2',
  334. '-movflags faststart'
  335. ]
  336. break
  337. default: // None
  338. ffmpegOpts = [
  339. '-c copy',
  340. '-bsf:a aac_adtstoasc',
  341. '-vsync 2',
  342. '-movflags faststart'
  343. ]
  344. break
  345. }
  346. switch (appSettings.get('downloads.method')) {
  347. case 'chunk':
  348. request(video.hlsvideosource, (err, res, body) => {
  349. if (err || !body) {
  350. fs.writeFileSync(`${path}/${filename}-error.log`, JSON.stringify(err, null, 2))
  351. return done({ videoid: task, error: err || 'Failed to fetch m3u8 file.' })
  352. }
  353. // Separate ts names from m3u8
  354. let concatList = ''
  355. const tsList = []
  356. body.split('\n').forEach(line => {
  357. if (line.indexOf('.ts') !== -1) {
  358. const tsName = line.split('?')[0]
  359. const tsPath = `${path}/lpt_temp/${video.vid}_${tsName}`
  360. // Check if TS has already been added to array
  361. if (concatList.indexOf(tsPath) === -1) {
  362. // We'll use this later to merge downloaded chunks
  363. concatList += `${tsPath}|`
  364. // Push data to list
  365. tsList.push({ name: tsName, path: tsPath })
  366. }
  367. }
  368. })
  369. // remove last |
  370. concatList = concatList.slice(0, -1)
  371. // Check if tmp dir exists
  372. if (!fs.existsSync(`${path}/lpt_temp`)) {
  373. // create temporary dir for ts files
  374. fs.mkdirSync(`${path}/lpt_temp`)
  375. }
  376. // Download chunks
  377. let downloadedChunks = 0
  378. async.eachLimit(tsList, 3, (file, next) => {
  379. const stream = request(`${video.hlsvideosource.split('/').slice(0, -1).join('/')}/${file.name}`)
  380. .on('error', err => {
  381. fs.writeFileSync(`${path}/${filename}-error.log`, JSON.stringify(err, null, 2))
  382. return done({ videoid: task, error: err })
  383. })
  384. .pipe(
  385. fs.createWriteStream(file.path)
  386. )
  387. // Events
  388. stream.on('finish', () => {
  389. downloadedChunks += 1
  390. mainWindow.webContents.send('download-progress', {
  391. videoid: task,
  392. state: `Downloading stream chunks.. (${downloadedChunks}/${tsList.length})`,
  393. percent: Math.round((downloadedChunks / tsList.length) * 100)
  394. })
  395. next()
  396. })
  397. }, () => {
  398. // Chunks downloaded
  399. ffmpeg()
  400. .on('start', c => {
  401. mainWindow.webContents.send('download-progress', {
  402. videoid: task,
  403. state: `Converting to MP4 file, please wait..`,
  404. percent: 0
  405. })
  406. })
  407. .on('progress', function (progress) {
  408. // FFMPEG doesn't always have this >.<
  409. if (!progress.percent) {
  410. progress.percent = ((progress.targetSize * 1000) / +video.videosize) * 100
  411. }
  412. mainWindow.webContents.send('download-progress', {
  413. videoid: task,
  414. state: `Converting to MP4 file (${Math.round(progress.percent)}%)`,
  415. percent: progress.percent
  416. })
  417. })
  418. .on('end', (stdout, stderr) => {
  419. DataManager.addDownloaded(video.vid)
  420. if (appSettings.get('downloads.deltmp')) {
  421. tsList.forEach(file => fs.unlinkSync(file.path))
  422. }
  423. return done()
  424. })
  425. .on('error', (err, stdout, stderr) => {
  426. fs.writeFileSync(`${path}/${filename}-error.log`, JSON.stringify([err, stdout, stderr], null, 2))
  427. if (appSettings.get('downloads.deltmp')) {
  428. //tsList.forEach(file => fs.unlinkSync(file.path))
  429. }
  430. return done({ videoid: task, error: err })
  431. })
  432. .input(`concat:${concatList}`)
  433. .output(`${path}/${filename}`)
  434. .outputOptions(ffmpegOpts)
  435. .run()
  436. })
  437. })
  438. break
  439. case 'ffmpeg':
  440. ffmpeg(video.hlsvideosource)
  441. .outputOptions(ffmpegOpts)
  442. .output(path + '/' + filename)
  443. .on('end', function (stdout, stderr) {
  444. DataManager.addDownloaded(video.vid)
  445. return done()
  446. })
  447. .on('progress', function (progress) {
  448. // FFMPEG doesn't always have this >.<
  449. if (!progress.percent) {
  450. progress.percent = ((progress.targetSize * 1000) / +video.videosize) * 100
  451. }
  452. mainWindow.webContents.send('download-progress', {
  453. videoid: task,
  454. state: `Downloading (${Math.round(progress.percent)}%)`,
  455. percent: progress.percent
  456. })
  457. })
  458. .on('start', function (c) {
  459. console.log('started', c)
  460. mainWindow.webContents.send('download-start', {
  461. videoid: task,
  462. filename: filename
  463. })
  464. })
  465. .on('error', function (err, stdout, stderr) {
  466. fs.writeFileSync(`${path}/${filename}-error.log`, JSON.stringify([err, stdout, stderr], null, 2))
  467. return done({ videoid: task, error: err })
  468. })
  469. .run()
  470. break
  471. }
  472. })
  473. }, +appSettings.get('downloads.parallel') || 3)
  474. /**
  475. * Watch a Replay - Use either internal player or external depending on settings
  476. */
  477. ipcMain.on('watch-replay', (event, arg) => {
  478. DataManager.addWatched(arg.videoid)
  479. LiveMe.getVideoInfo(arg.videoid)
  480. .then(video => {
  481. let internalplayer = appSettings.get('general.playerpath')
  482. let playerpath = internalplayer
  483. if (playerpath.length > 5) {
  484. exec(playerpath.replace('%url%', video.hlsvideosource))
  485. } else {
  486. // Open internal player
  487. if (playerWindow == null) {
  488. let winposition = appSettings.get('position.playerWindow')
  489. let winsize = appSettings.get('size.playerWindow')
  490. playerWindow = new BrowserWindow({
  491. icon: path.join(__dirname, 'appicon.ico'),
  492. width: winsize[0],
  493. height: winsize[1],
  494. x: winposition[0] !== -1 ? winposition[0] : null,
  495. y: winposition[1] !== -1 ? winposition[1] : null,
  496. minWidth: 380,
  497. minHeight: 708,
  498. darkTheme: true,
  499. autoHideMenuBar: false,
  500. disableAutoHideCursor: true,
  501. titleBarStyle: 'default',
  502. fullscreen: false,
  503. maximizable: false,
  504. frame: false,
  505. backgroundColor: '#000000',
  506. webPreferences: {
  507. webSecurity: false,
  508. textAreasAreResizable: false,
  509. plugins: true
  510. }
  511. })
  512. playerWindow.setMenu(Menu.buildFromTemplate(getMiniMenuTemplate()))
  513. playerWindow.on('close', () => {
  514. appSettings.set('position.playerWindow', playerWindow.getPosition())
  515. appSettings.set('size.playerWindow', playerWindow.getSize())
  516. playerWindow.webContents.session.clearCache(() => {
  517. // Purge the cache to help avoid eating up space on the drive
  518. })
  519. playerWindow = null
  520. })
  521. }
  522. playerWindow.loadURL(`file://${__dirname}/app/player.html?${video.vid}`)
  523. }
  524. })
  525. .catch(err => {
  526. console.log('[watch-replay] getVideoInfo Error:', err)
  527. })
  528. })
  529. ipcMain.on('open-bookmarks', (event, arg) => {})
  530. ipcMain.on('show-user', (event, arg) => {
  531. mainWindow.webContents.send('show-user', { userid: arg.userid })
  532. })
  533. ipcMain.on('open-followings-window', (event, arg) => {
  534. let winposition = appSettings.get('position.followingsWindow') ? appSettings.get('position.followingsWindow') : [-1, -1]
  535. let win = new BrowserWindow({
  536. x: winposition[0] !== -1 ? winposition[0] : null,
  537. y: winposition[1] !== -1 ? winposition[1] : null,
  538. width: 420,
  539. minWidth: 420,
  540. maxWidth: 420,
  541. height: 720,
  542. minHeight: 600,
  543. resizable: true,
  544. darkTheme: false,
  545. autoHideMenuBar: true,
  546. skipTaskbar: false,
  547. backgroundColor: '#000000',
  548. disableAutoHideCursor: true,
  549. titleBarStyle: 'default',
  550. fullscreen: false,
  551. maximizable: false,
  552. closable: true,
  553. frame: false,
  554. show: false
  555. })
  556. win.setMenu(Menu.buildFromTemplate(getMiniMenuTemplate()))
  557. win.on('ready-to-show', () => {
  558. win.show()
  559. }).on('close', () => {
  560. appSettings.set('position.followingsWindow', win.getPosition())
  561. }).loadURL(`file://${__dirname}/app/listwindow.html?1&` + arg.userid)
  562. })
  563. ipcMain.on('open-followers-window', (event, arg) => {
  564. let winposition = appSettings.get('position.fansWindow') ? appSettings.get('position.fansWindow') : [-1, -1]
  565. var win = new BrowserWindow({
  566. x: winposition[0] !== -1 ? winposition[0] : null,
  567. y: winposition[1] !== -1 ? winposition[1] : null,
  568. width: 420,
  569. minWidth: 420,
  570. maxWidth: 420,
  571. height: 720,
  572. minHeight: 600,
  573. resizable: true,
  574. darkTheme: false,
  575. autoHideMenuBar: true,
  576. skipTaskbar: false,
  577. backgroundColor: '#000000',
  578. disableAutoHideCursor: true,
  579. titleBarStyle: 'default',
  580. fullscreen: false,
  581. maximizable: false,
  582. closable: true,
  583. frame: false,
  584. show: false
  585. })
  586. win.setMenu(Menu.buildFromTemplate(getMiniMenuTemplate()))
  587. win.on('ready-to-show', () => {
  588. win.show()
  589. }).on('close', () => {
  590. appSettings.set('position.fansWindow', win.getPosition())
  591. }).loadURL(`file://${__dirname}/app/listwindow.html?0&` + arg.userid)
  592. })
  593. ipcMain.on('read-comments', (event, arg) => {
  594. let win = new BrowserWindow({
  595. width: 400,
  596. width: 400,
  597. minWidth: 400,
  598. maxWidth: 400,
  599. height: 600,
  600. minHeight: 600,
  601. resizable: true,
  602. darkTheme: false,
  603. autoHideMenuBar: true,
  604. skipTaskbar: false,
  605. backgroundColor: '#000000',
  606. disableAutoHideCursor: true,
  607. titleBarStyle: 'default',
  608. fullscreen: false,
  609. maximizable: false,
  610. closable: true,
  611. frame: false,
  612. show: false
  613. })
  614. win.setMenu(Menu.buildFromTemplate(getMiniMenuTemplate()))
  615. win.on('ready-to-show', () => {
  616. win.showInactive()
  617. }).loadURL(`file://${__dirname}/app/comments.html?` + arg.userid)
  618. })
  619. ipcMain.on('open-bookmarks', (event, arg) => {
  620. if (bookmarksWindow == null) {
  621. let winposition = appSettings.get('position.bookmarksWindow')
  622. let winsize = appSettings.get('size.bookmarksWindow')
  623. bookmarksWindow = new BrowserWindow({
  624. x: winposition[0] > -1 ? winposition[0] : null,
  625. y: winposition[1] > -1 ? winposition[1] : null,
  626. width: 480,
  627. height: winsize[1],
  628. minWidth: 400,
  629. maxWidth: 400,
  630. minHeight: 480,
  631. darkTheme: true,
  632. autoHideMenuBar: false,
  633. disableAutoHideCursor: true,
  634. titleBarStyle: 'default',
  635. fullscreen: false,
  636. maximizable: false,
  637. frame: false,
  638. show: false,
  639. backgroundColor: '#000000'
  640. })
  641. bookmarksWindow.setMenu(Menu.buildFromTemplate(getMiniMenuTemplate()))
  642. bookmarksWindow.on('close', () => {
  643. appSettings.set('position.bookmarksWindow', bookmarksWindow.getPosition())
  644. appSettings.set('size.bookmarksWindow', bookmarksWindow.getSize())
  645. bookmarksWindow.webContents.session.clearCache(() => {
  646. // Purge the cache to help avoid eating up space on the drive
  647. })
  648. bookmarksWindow = null
  649. })
  650. } else {
  651. bookmarksWindow.restore()
  652. }
  653. bookmarksWindow.on('ready-to-show', () => {
  654. bookmarksWindow.show()
  655. }).loadURL(`file://${__dirname}/app/bookmarks.html`)
  656. })
  657. ipcMain.on('restore-backup', (event, arg) => {
  658. dialog.showOpenDialog(
  659. {
  660. properties: [
  661. 'openFile'
  662. ],
  663. buttonLabel: 'Restore',
  664. filters: [
  665. {
  666. name: 'TAR files',
  667. extensions: ['tar']
  668. }
  669. ]
  670. },
  671. (filePath) => {
  672. if (filePath != null) {
  673. mainWindow.webContents.send('shutdown')
  674. DataManager.disableWrites()
  675. let configPath = path.join(app.getPath('appData'), app.getName(), '/')
  676. fs.createReadStream(filePath[0]).pipe(tarfs.extract(configPath))
  677. setTimeout(() => {
  678. app.relaunch()
  679. app.quit()
  680. }, 1000)
  681. }
  682. }
  683. )
  684. })
  685. ipcMain.on('create-backup', (event, arg) => {
  686. let configPath = path.join(app.getPath('appData'), app.getName()), dt = new Date()
  687. let fname = 'liveme_pro_tools_backup-' + dt.getFullYear() + (dt.getMonth() < 10 ? '0' : '') + dt.getMonth() + (dt.getDate() < 10 ? '0' : '') + dt.getDate()
  688. let backupFile = path.join(app.getPath('home'), 'Downloads', fname)
  689. tarfs.pack(
  690. configPath,
  691. {
  692. entries: [ 'bookmarks.json', 'downloaded.json', 'profiles.json', 'watched.json' ]
  693. }
  694. ).pipe(fs.createWriteStream(backupFile))
  695. })
  696. function getMenuTemplate () {
  697. let template = [
  698. {
  699. label: 'Edit',
  700. submenu: [
  701. { role: 'undo' },
  702. { role: 'redo' },
  703. { type: 'separator' },
  704. { role: 'cut' },
  705. { role: 'copy' },
  706. { role: 'paste' },
  707. { role: 'delete' },
  708. { role: 'selectall' }
  709. ]
  710. },
  711. {
  712. role: 'window',
  713. submenu: [
  714. { role: 'minimize' },
  715. { role: 'close' },
  716. { type: 'separator' },
  717. {
  718. label: 'Developer Tools',
  719. submenu: [
  720. { role: 'reload' },
  721. { role: 'forcereload' },
  722. { role: 'toggledevtools' }
  723. ]
  724. }
  725. ]
  726. },
  727. {
  728. role: 'help',
  729. submenu: [
  730. {
  731. label: 'LiveMe Pro Tools Page',
  732. click: () => shell.openExternal('https://github.com/lewdninja/liveme-pro-tools/')
  733. }
  734. ]
  735. }
  736. ]
  737. /**
  738. * This is here in case macOS version gets added back end after all the bugs/issues are figured out.
  739. * Requires a contributor running macOS now.
  740. */
  741. if (process.platform === 'darwin') {
  742. template.unshift({
  743. label: appName,
  744. submenu: [
  745. {
  746. label: 'About ' + appName,
  747. click: () => {}
  748. },
  749. { type: 'separator' },
  750. { role: 'services', submenu: [] },
  751. { type: 'separator' },
  752. { role: 'hide' },
  753. { role: 'hideothers' },
  754. { role: 'unhide' },
  755. { type: 'separator' },
  756. {
  757. label: 'Quit ' + appName,
  758. accelerator: 'CommandOrControl+Q',
  759. click: () => { mainWindow.close() }
  760. }
  761. ]
  762. })
  763. }
  764. return template
  765. }
  766. function getMiniMenuTemplate () {
  767. let template = [
  768. {
  769. label: 'Edit',
  770. submenu: [
  771. { role: 'undo' },
  772. { role: 'redo' },
  773. { type: 'separator' },
  774. { role: 'cut' },
  775. { role: 'copy' },
  776. { role: 'paste' },
  777. { role: 'delete' },
  778. { role: 'selectall' }
  779. ]
  780. },
  781. {
  782. role: 'window',
  783. submenu: [
  784. { role: 'minimize' },
  785. { role: 'close' },
  786. { type: 'separator' },
  787. {
  788. label: 'Developer Tools',
  789. submenu: [
  790. { role: 'reload' },
  791. { role: 'forcereload' },
  792. { role: 'toggledevtools' }
  793. ]
  794. }
  795. ]
  796. }
  797. ]
  798. return template
  799. }