Update dependencies and fix breaking changes

This commit is contained in:
Andreas Willi 2021-10-16 11:44:44 +02:00
commit 4fb1de6e8f
No known key found for this signature in database
GPG key ID: 3A54A036E8C99FBF
11 changed files with 8394 additions and 14395 deletions

View file

@ -1,7 +1,3 @@
{
"presets": [
"react",
"env",
"stage-0"
]
"presets": ["@babel/react", "@babel/env"]
}

View file

@ -1,14 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>Meta Grabber</title>
<link rel="stylesheet" type="text/css" href="src/styles/index.css">
<link rel="stylesheet" type="text/css" href="src/styles/index.css" />
</head>
<body>
<div id="root"></div>
<script>
require('./target/index.js')
</script>
<script src="./target/index.js"></script>
</body>
</html>

31
main.js
View file

@ -1,5 +1,5 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow, Menu, shell } = require('electron')
const { app, BrowserWindow, Menu, shell, ipcMain, dialog } = require('electron')
const defaultMenu = require('electron-default-menu')
const isDev = require('electron-is-dev')
const { autoUpdater } = require('electron-updater')
@ -15,6 +15,8 @@ function createWindow() {
height: 700,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
// enableRemoteModule: true,
},
})
@ -25,7 +27,7 @@ function createWindow() {
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function() {
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
@ -45,7 +47,9 @@ app.on('ready', () => {
let menu = defaultMenu(app, shell)
// add settings shortcut
const mainMenuItem = menu.find(menuItem => menuItem.label === 'meta-grabber')
const mainMenuItem = menu.find(
(menuItem) => menuItem.label === 'meta-grabber'
)
// only do this on macOS
if (mainMenuItem) {
menu = [
@ -66,16 +70,16 @@ app.on('ready', () => {
...mainMenuItem.submenu.slice(2),
],
},
...menu.filter(menuItem => menuItem.label !== mainMenuItem.label),
...menu.filter((menuItem) => menuItem.label !== mainMenuItem.label),
]
}
// remove devtools and relaod in prod
if (!isDev) {
menu = menu.map(menuItem => ({
menu = menu.map((menuItem) => ({
...menuItem,
submenu: menuItem.submenu.filter(
subItem =>
(subItem) =>
subItem.label !== 'Toggle Developer Tools' &&
subItem.label !== 'Reload'
),
@ -86,7 +90,7 @@ app.on('ready', () => {
})
// Quit when all windows are closed.
app.on('window-all-closed', function() {
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
// if (process.platform !== 'darwin') {
@ -104,3 +108,16 @@ app.on('window-all-closed', function() {
// 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.
// expose dialogs to renderer process
ipcMain.handle('open-files', async () =>
dialog.showOpenDialog({
properties: ['openFile', 'openDirectory', 'multiSelections'],
})
)
ipcMain.handle('open-directory', async () =>
dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
})
)

15796
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -22,31 +22,30 @@
"author": "Andreas Willi",
"license": "CC0-1.0",
"devDependencies": {
"electron": "^7.3.3",
"electron": "^15.2.0",
"electron-builder": "^22.13.1",
"prettier": "^2.1.2",
"webpack-cli": "^3.3.12"
"prettier": "^2.4.1",
"webpack-cli": "^4.9.0"
},
"dependencies": {
"@svgr/webpack": "^5.4.0",
"axios": "^0.20.0",
"@babel/preset-env": "^7.15.8",
"@babel/preset-react": "^7.14.5",
"@svgr/webpack": "^5.5.0",
"axios": "^0.23.0",
"babel-core": "^6.26.3",
"babel-loader": "7",
"babel-loader": "^8.2.2",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"classnames": "^2.2.6",
"classnames": "^2.3.1",
"electron-default-menu": "^1.0.2",
"electron-is-dev": "^1.2.0",
"electron-updater": "^4.3.5",
"i18next": "^19.7.0",
"electron-is-dev": "^2.0.0",
"electron-updater": "^4.3.9",
"i18next": "^21.3.2",
"react": "^16.13.1",
"react-animate-height": "^2.0.23",
"react-autocomplete": "^1.8.1",
"react-dom": "^16.13.1",
"react-i18next": "^11.7.3",
"webpack": "^4.44.2"
"react-i18next": "^11.12.0",
"webpack": "^5.58.2"
},
"build": {
"appId": "com.andiw.metagrabber",

View file

@ -12,7 +12,7 @@ export default class Button extends Component {
'button--icon-right': this.props.iconRight,
})}
disabled={this.props.disabled}
onClick={!this.props.disabled && this.props.onClick}
onClick={!this.props.disabled ? this.props.onClick : undefined}
>
{this.props.icon}
{this.props.label && (

View file

@ -1,5 +1,5 @@
import React, { Component } from 'react'
import { remote } from 'electron'
import { ipcRenderer } from 'electron'
import { withTranslation } from 'react-i18next'
import Button from './button'
@ -11,9 +11,9 @@ import FolderIcon from '../icons/folder.svg'
class FilePicker extends Component {
async open() {
const { t } = this.props
const { filePaths: paths, canceled } = await remote.dialog.showOpenDialog({
properties: ['openFile', 'openDirectory', 'multiSelections'],
})
const { filePaths: paths, canceled } = await ipcRenderer.invoke(
'open-files'
)
if (canceled) {
return
}
@ -22,13 +22,13 @@ class FilePicker extends Component {
const files = await asyncReadRecursively(paths)
this.props.onFileOpen(
files.filter(
file =>
(file) =>
this.props.includedExtensions
.filter(ext => ext)
.some(extension => file.toLowerCase().endsWith(extension)) &&
.filter((ext) => ext)
.some((extension) => file.toLowerCase().endsWith(extension)) &&
!this.props.excludedTerms
.filter(term => term)
.some(term => file.toLowerCase().includes(term))
.filter((term) => term)
.some((term) => file.toLowerCase().includes(term))
)
)
} catch (error) {

View file

@ -1,6 +1,6 @@
import React, { Component } from 'react'
import fs from 'fs'
import { remote } from 'electron'
import { ipcRenderer } from 'electron'
import classNames from 'classnames'
import AnimateHeight from 'react-animate-height'
import util from 'util'
@ -47,16 +47,17 @@ class FileRename extends Component {
isSeasonIncluded(seasonName) {
return (
this.state.includedSeasons.find(season => season.name === seasonName) !==
undefined
this.state.includedSeasons.find(
(season) => season.name === seasonName
) !== undefined
)
}
isEpisodeIncluded(episodeName) {
return (
this.state.includedSeasons.filter(
season =>
season.includedEpisodes.filter(episode => episode === episodeName)
(season) =>
season.includedEpisodes.filter((episode) => episode === episodeName)
.length > 0
).length > 0
)
@ -64,7 +65,8 @@ class FileRename extends Component {
isWholeSeasonExcluded(seasonName) {
return (
this.state.includedSeasons.find(s => s.name === seasonName) === undefined
this.state.includedSeasons.find((s) => s.name === seasonName) ===
undefined
)
}
@ -73,11 +75,11 @@ class FileRename extends Component {
// include season
await this.setState({
includedSeasons: [
...this.state.includedSeasons.filter(s => s.name !== seasonName),
...this.state.includedSeasons.filter((s) => s.name !== seasonName),
{
name: seasonName,
includedEpisodes: this.props.seasons.find(
s => s.name === seasonName
(s) => s.name === seasonName
).episodes,
},
],
@ -86,7 +88,7 @@ class FileRename extends Component {
// exclude season
await this.setState({
includedSeasons: this.state.includedSeasons.filter(
s => s.name !== seasonName
(s) => s.name !== seasonName
),
})
}
@ -95,18 +97,18 @@ class FileRename extends Component {
async handleEpisodeChange(e, episodeName) {
const season = this.props.seasons.find(
season =>
season.episodes.filter(episode => episode === episodeName).length > 0
(season) =>
season.episodes.filter((episode) => episode === episodeName).length > 0
)
const includedSeason = this.state.includedSeasons.find(
s => s.name === season.name
(s) => s.name === season.name
)
let includedSeasons
if (e.target.checked) {
// include episode
includedSeasons = [
...this.state.includedSeasons.filter(s => s.name !== season.name),
...this.state.includedSeasons.filter((s) => s.name !== season.name),
{
name: includedSeason.name,
includedEpisodes: [...includedSeason.includedEpisodes, episodeName],
@ -116,15 +118,15 @@ class FileRename extends Component {
// exclude episode
includedSeasons = [
...this.state.includedSeasons.filter(
s => s.name !== includedSeason.name
(s) => s.name !== includedSeason.name
),
{
name: includedSeason.name,
includedEpisodes: includedSeason.includedEpisodes.filter(
e => e !== episodeName
(e) => e !== episodeName
),
},
].filter(s => s.includedEpisodes.length > 0)
].filter((s) => s.includedEpisodes.length > 0)
}
await this.setState({ includedSeasons })
this.assignEpisodesToFiles(this.props)
@ -133,7 +135,7 @@ class FileRename extends Component {
assignEpisodesToFiles(props) {
let excludedEpisodesCount = 0
this.setState({
assignments: flatten(props.seasons.map(s => s.episodes)).map((e, i) => {
assignments: flatten(props.seasons.map((s) => s.episodes)).map((e, i) => {
let included = this.isEpisodeIncluded(e)
if (!included) {
excludedEpisodesCount++
@ -149,14 +151,12 @@ class FileRename extends Component {
}
getAssignedFileName(episodeName) {
let assignment = this.state.assignments.find(a => a.name === episodeName)
let assignment = this.state.assignments.find((a) => a.name === episodeName)
return assignment ? assignment.fileName : null
}
async handleChooseOutputDir() {
const { filePaths, canceled } = await remote.dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
})
const { filePaths, canceled } = await ipcRenderer.invoke('open-directory')
if (canceled) {
return
}
@ -165,7 +165,7 @@ class FileRename extends Component {
async handleIncludeAll() {
await this.setState({
includedSeasons: this.props.seasons.map(s => ({
includedSeasons: this.props.seasons.map((s) => ({
name: s.name,
includedEpisodes: s.episodes,
})),
@ -178,7 +178,7 @@ class FileRename extends Component {
this.assignEpisodesToFiles(this.props)
}
getNewFileDir = a => {
getNewFileDir = (a) => {
let newFileDir = (
(this.props.outputDir !== '?' && this.props.outputDir) ||
formatFilePath(a.fileName)
@ -204,11 +204,11 @@ class FileRename extends Component {
async renameFiles() {
const { t } = this.props
this.props.onLoadingChange(true)
let assignments = this.state.assignments.filter(a => a.fileName)
let assignments = this.state.assignments.filter((a) => a.fileName)
try {
// make sure that none of the new files replaces any existing file
let nameMappings = await Promise.all(
assignments.map(async a => {
assignments.map(async (a) => {
return new Promise(async (resolve, reject) => {
const newFileDir = this.getNewFileDir(a)
try {
@ -255,7 +255,7 @@ class FileRename extends Component {
// none of the files are existing already, continue
await Promise.all(
nameMappings.map(mapping => {
nameMappings.map((mapping) => {
return new Promise(async (resolve, reject) => {
try {
await rename(mapping.oldFileName, mapping.newFileName)
@ -292,8 +292,9 @@ class FileRename extends Component {
onClick={this.handleIncludeAll.bind(this)}
disabled={
JSON.stringify(
this.state.includedSeasons.map(s => s.includedEpisodes)
) === JSON.stringify(this.props.seasons.map(s => s.episodes))
this.state.includedSeasons.map((s) => s.includedEpisodes)
) ===
JSON.stringify(this.props.seasons.map((s) => s.episodes))
}
/>
<Button
@ -316,7 +317,7 @@ class FileRename extends Component {
type="checkbox"
className="file-rename__item__checkbox"
checked={this.isSeasonIncluded(s.name)}
onChange={event => this.handleSeasonChange(event, s.name)}
onChange={(event) => this.handleSeasonChange(event, s.name)}
/>
<div
className={classNames('file-rename__item', {
@ -347,7 +348,7 @@ class FileRename extends Component {
tabIndex={-1}
className="file-rename__item__checkbox"
checked={this.isEpisodeIncluded(e)}
onChange={event => this.handleEpisodeChange(event, e)}
onChange={(event) => this.handleEpisodeChange(event, e)}
/>
<div className="file-rename__item__name">{e}</div>
<div className="file-rename__item__file-name">
@ -387,7 +388,7 @@ class FileRename extends Component {
icon={<CheckmarkIcon />}
onClick={this.renameFiles.bind(this)}
disabled={
this.state.assignments.filter(a => a.fileName).length === 0
this.state.assignments.filter((a) => a.fileName).length === 0
}
/>
</div>

View file

@ -1,6 +1,6 @@
import React, { Component } from 'react'
import classNames from 'classnames'
import { remote } from 'electron'
import { ipcRenderer } from 'electron'
import i18n from 'i18next'
import { withTranslation, Trans } from 'react-i18next'
@ -45,7 +45,7 @@ class SettingsPane extends Component {
query:
(
languages.find(
l => l.iso_639_1 === this.props.settings.metaDataLang
(l) => l.iso_639_1 === this.props.settings.metaDataLang
) || {}
).english_name || '',
})
@ -69,13 +69,13 @@ class SettingsPane extends Component {
)
localStorage.setItem(
'includedExtensions',
(this.props.settings.includedExtensions.filter(ext => ext).length > 0 &&
(this.props.settings.includedExtensions.filter((ext) => ext).length > 0 &&
this.props.settings.includedExtensions) ||
'(empty)'
)
localStorage.setItem(
'excludedTerms',
(this.props.settings.excludedTerms.filter(term => term).length > 0 &&
(this.props.settings.excludedTerms.filter((term) => term).length > 0 &&
this.props.settings.excludedTerms) ||
'(empty)'
)
@ -89,7 +89,7 @@ class SettingsPane extends Component {
}
handleLanguageSelect(lang) {
const language = this.state.languages.find(l => l.iso_639_1 === lang)
const language = this.state.languages.find((l) => l.iso_639_1 === lang)
this.setState({ query: language.english_name })
this.handleSettingsChange('metaDataLang', language.iso_639_1)
}
@ -109,7 +109,7 @@ class SettingsPane extends Component {
filterLanguages() {
return this.state.languages.filter(
l =>
(l) =>
l.name.toLowerCase().match(this.state.query.toLowerCase()) ||
l.english_name.toLowerCase().match(this.state.query.toLowerCase()) ||
l.iso_639_1.toLowerCase().match(this.state.query.toLowerCase())
@ -117,9 +117,7 @@ class SettingsPane extends Component {
}
async handleChooseOutputDir() {
const { filePaths, canceled } = await remote.dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
})
const { filePaths, canceled } = await ipcRenderer.invoke('open-directory')
if (canceled) {
return
}
@ -169,14 +167,14 @@ class SettingsPane extends Component {
className="settings-pane__setting__group__button settings-pane__setting__group__button--radio"
icon={
this.props.settings.uiLang === 'ch' ? (
<RadioButtonCheckedIcon />
<RadioButtonCheckedIcon />
) : (
<RadioButtonUncheckedIcon />
<RadioButtonUncheckedIcon />
)
}
label={t('uiLang.ch')}
onClick={() => this.handleUiLanguageSelect('ch')}
/>
/>
</div>
<div className="settings-pane__setting">
<div className="settings-pane__setting__label">
@ -185,15 +183,17 @@ class SettingsPane extends Component {
<Autocomplete
placeholder={t('metaLang.placeholder')}
focusable={this.props.openState}
onChange={event => this.setState({ query: event.target.value })}
onChange={(event) =>
this.setState({ query: event.target.value })
}
onSelect={this.handleLanguageSelect.bind(this)}
onBlur={() =>
this.handleLanguageSelect(this.props.settings.metaDataLang)
}
items={this.filterLanguages()}
getItemValue={item => item.iso_639_1}
getItemKey={item => item.iso_639_1}
getDisplayValue={item => item.english_name}
getItemValue={(item) => item.iso_639_1}
getItemKey={(item) => item.iso_639_1}
getDisplayValue={(item) => item.english_name}
value={this.state.query}
showDropdown={this.filterLanguages().length > 0}
/>
@ -207,12 +207,12 @@ class SettingsPane extends Component {
focusable={this.props.openState}
onSelect={this.handleApiProviderSelect.bind(this)}
items={this.apiProviders}
getItemValue={item => item.value}
getItemKey={item => item.value}
getDisplayValue={item => item.name}
getItemValue={(item) => item.value}
getItemKey={(item) => item.value}
getDisplayValue={(item) => item.name}
value={
this.apiProviders.find(
p => p.value === this.props.settings.apiProvider
(p) => p.value === this.props.settings.apiProvider
).name
}
showDropdown={true}
@ -228,8 +228,8 @@ class SettingsPane extends Component {
</Trans>
</div>
<NamingTemplate
onRef={ref => (this.namingTemplate = ref)}
onChange={template =>
onRef={(ref) => (this.namingTemplate = ref)}
onChange={(template) =>
this.handleSettingsChange('template', template)
}
template={this.props.settings.template}
@ -245,7 +245,7 @@ class SettingsPane extends Component {
type="text"
className="input"
value={this.props.settings.defaultOutputDir}
onChange={event =>
onChange={(event) =>
this.handleSettingsChange(
'defaultOutputDir',
event.target.value
@ -274,10 +274,10 @@ class SettingsPane extends Component {
type="text"
className="input"
value={this.props.settings.includedExtensions}
onChange={event =>
onChange={(event) =>
this.handleSettingsChange(
'includedExtensions',
event.target.value.split(',').map(ext => ext.trim())
event.target.value.split(',').map((ext) => ext.trim())
)
}
tabIndex={this.props.openState ? 0 : -1}
@ -294,10 +294,10 @@ class SettingsPane extends Component {
type="text"
className="input"
value={this.props.settings.excludedTerms}
onChange={event =>
onChange={(event) =>
this.handleSettingsChange(
'excludedTerms',
event.target.value.split(',').map(term => term.trim())
event.target.value.split(',').map((term) => term.trim())
)
}
tabIndex={this.props.openState ? 0 : -1}

View file

@ -8,7 +8,7 @@ i18n.init({
fallbackLng: 'en',
// debug: true,
resources: { en, de, 'ch': deCh },
resources: { en, de, ch: deCh },
interpolation: {
escapeValue: false, // not needed for react
@ -16,7 +16,7 @@ i18n.init({
// react i18next special options (optional)
react: {
wait: true,
useSuspense: true,
bindI18n: 'languageChanged loaded',
bindStore: 'added removed',
nsMode: 'default',

View file

@ -1,5 +1,4 @@
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: ['babel-polyfill', './src/index.js'],
@ -8,7 +7,7 @@ module.exports = {
filename: 'index.js',
libraryTarget: 'commonjs2',
},
target: 'electron-main',
target: 'electron-renderer',
module: {
rules: [
{
@ -23,11 +22,6 @@ module.exports = {
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
],
externals: {
react: 'commonjs react',
},