Working authentication for funimation
This commit is contained in:
parent
0d5d0f0102
commit
75888c23a5
18 changed files with 1681 additions and 18572 deletions
2
@types/messageHandler.d.ts
vendored
2
@types/messageHandler.d.ts
vendored
|
|
@ -4,6 +4,7 @@ import type { AvailableMuxer } from '../modules/module.args';
|
|||
|
||||
export interface MessageHandler {
|
||||
auth: (data: AuthData) => Promise<AuthResponse>;
|
||||
checkToken: () => Promise<CheckTokenResponse>;
|
||||
}
|
||||
|
||||
export type FuniEpisodeData = {
|
||||
|
|
@ -25,6 +26,7 @@ export type AuthResponse = ResponseBase<undefined>;
|
|||
export type FuniSearchReponse = ResponseBase<FunimationSearch>;
|
||||
export type FuniShowResponse = ResponseBase<FuniEpisodeData[]>;
|
||||
export type FuniGetEpisodeResponse = ResponseBase<undefined>;
|
||||
export type CheckTokenResponse = ResponseBase<undefined>;
|
||||
|
||||
export type ResponseBase<T> = ({
|
||||
isOk: true,
|
||||
|
|
|
|||
8
funi.ts
8
funi.ts
|
|
@ -37,7 +37,7 @@ import { FunimationMediaDownload } from './@types/funiTypes';
|
|||
import * as langsData from './modules/module.langsData';
|
||||
import { TitleElement } from './@types/episode';
|
||||
import { AvailableFilenameVars } from './modules/module.args';
|
||||
import { AuthData, AuthResponse, FuniGetEpisodeData, FuniGetEpisodeResponse, FuniGetShowData, FuniSearchData, FuniSearchReponse, FuniShowResponse, FuniStreamData, FuniSubsData } from './@types/messageHandler';
|
||||
import { AuthData, AuthResponse, CheckTokenResponse, FuniGetEpisodeData, FuniGetEpisodeResponse, FuniGetShowData, FuniSearchData, FuniSearchReponse, FuniShowResponse, FuniStreamData, FuniSubsData } from './@types/messageHandler';
|
||||
// check page
|
||||
|
||||
// fn variables
|
||||
|
|
@ -59,6 +59,11 @@ export default class Funi {
|
|||
this.token = yamlCfg.loadFuniToken();
|
||||
}
|
||||
|
||||
public checkToken(): CheckTokenResponse {
|
||||
const isOk = typeof this.token === 'string';
|
||||
return isOk ? { isOk, value: undefined } : { isOk, reason: new Error('Not authenticated') };
|
||||
}
|
||||
|
||||
public async init() {
|
||||
this.cfg.bin = await yamlCfg.loadBinCfg();
|
||||
}
|
||||
|
|
@ -141,6 +146,7 @@ export default class Funi {
|
|||
if(resJSON.token){
|
||||
console.log('[INFO] Authentication success, your token: %s%s\n', resJSON.token.slice(0,8),'*'.repeat(32));
|
||||
yamlCfg.saveFuniToken({'token': resJSON.token});
|
||||
this.token = resJSON.token;
|
||||
return { isOk: true, value: undefined };
|
||||
} else {
|
||||
console.log('[ERROR]%s\n', ' No token found');
|
||||
|
|
|
|||
1
gui/electron/src/.env
Normal file
1
gui/electron/src/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
USE_BROWSER=true
|
||||
|
|
@ -3,13 +3,15 @@ import path from 'path/posix';
|
|||
import json from '../../../package.json';
|
||||
import registerMessageHandler from './messageHandler';
|
||||
import fs from "fs";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
if (fs.existsSync(path.join(__dirname, '.env')))
|
||||
dotenv.config({ path: path.join(__dirname, '.env'), debug: true });
|
||||
|
||||
if (require('electron-squirrel-startup')) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
console.log(process.argv, process.env);
|
||||
|
||||
const createWindow = (): void => {
|
||||
registerMessageHandler();
|
||||
// Create the browser window.
|
||||
|
|
@ -25,7 +27,7 @@ const createWindow = (): void => {
|
|||
|
||||
const htmlFile = path.join(__dirname, '..', 'build', 'index.html');
|
||||
|
||||
if (fs.existsSync(htmlFile)) {
|
||||
if (!process.env.USE_BROWSER) {
|
||||
mainWindow.loadFile(htmlFile);
|
||||
} else {
|
||||
mainWindow.loadURL('http://localhost:3000');
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import Funimation from './serviceHandler/funimation';
|
|||
export default () => {
|
||||
let handler: MessageHandler|undefined;
|
||||
|
||||
ipcMain.handle('setup', (ev, data) => {
|
||||
ipcMain.handle('setup', (_, data) => {
|
||||
if (data === 'funi') {
|
||||
handler = new Funimation();
|
||||
} else if (data === 'crunchy') {
|
||||
|
|
@ -13,5 +13,6 @@ export default () => {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('auth', async (ev, data) => handler?.auth(data));
|
||||
ipcMain.handle('auth', async (_, data) => handler?.auth(data));
|
||||
ipcMain.handle('checkToken', async () => handler?.checkToken());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { AuthData, AuthResponse, MessageHandler } from "../../../../@types/messageHandler";
|
||||
import { AuthData, AuthResponse, CheckTokenResponse, MessageHandler } from "../../../../@types/messageHandler";
|
||||
|
||||
import Funimation from '../../../../funi';
|
||||
|
||||
|
|
@ -8,6 +8,10 @@ class FunimationHandler implements MessageHandler {
|
|||
this.funi = new Funimation();
|
||||
}
|
||||
|
||||
public async checkToken(): Promise<CheckTokenResponse> {
|
||||
return this.funi.checkToken();
|
||||
}
|
||||
|
||||
public auth(data: AuthData) {
|
||||
return this.funi.auth(data);
|
||||
}
|
||||
|
|
|
|||
72
gui/react/package-lock.json
generated
72
gui/react/package-lock.json
generated
|
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@mui/icons-material": "^5.3.1",
|
||||
"@mui/material": "^5.3.1",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
"@types/node": "^16.11.21",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"notistack": "^2.0.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "5.0.0",
|
||||
|
|
@ -2849,6 +2851,31 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/icons-material": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.3.1.tgz",
|
||||
"integrity": "sha512-8zBWCaE8DHjIGZhGgMod92p6Rm38EhXrS+cZtaV0+jOTMeWh7z+mvswXzb/rVKc0ZYqw6mQYBcn2uEs2yclI9w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mui/material": "^5.0.0",
|
||||
"@types/react": "^16.8.6 || ^17.0.0",
|
||||
"react": "^17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.3.1.tgz",
|
||||
|
|
@ -11880,6 +11907,34 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/notistack": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.3.tgz",
|
||||
"integrity": "sha512-krmVFtTO9kEY1Pa4NrbyexrjiRcV6TqBM2xLx8nuDea1g96Z/OZfkvVLmYKkTvoSJ3jyQntWK16z86ssW5kt4A==",
|
||||
"dependencies": {
|
||||
"clsx": "^1.1.0",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/notistack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^5.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/npm-conf": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",
|
||||
|
|
@ -18894,6 +18949,14 @@
|
|||
"react-is": "^17.0.2"
|
||||
}
|
||||
},
|
||||
"@mui/icons-material": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.3.1.tgz",
|
||||
"integrity": "sha512-8zBWCaE8DHjIGZhGgMod92p6Rm38EhXrS+cZtaV0+jOTMeWh7z+mvswXzb/rVKc0ZYqw6mQYBcn2uEs2yclI9w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.7"
|
||||
}
|
||||
},
|
||||
"@mui/material": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.3.1.tgz",
|
||||
|
|
@ -25470,6 +25533,15 @@
|
|||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
|
||||
},
|
||||
"notistack": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.3.tgz",
|
||||
"integrity": "sha512-krmVFtTO9kEY1Pa4NrbyexrjiRcV6TqBM2xLx8nuDea1g96Z/OZfkvVLmYKkTvoSJ3jyQntWK16z86ssW5kt4A==",
|
||||
"requires": {
|
||||
"clsx": "^1.1.0",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"npm-conf": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@mui/icons-material": "^5.3.1",
|
||||
"@mui/material": "^5.3.1",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
"@types/node": "^16.11.21",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"notistack": "^2.0.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "5.0.0",
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
import React from 'react';
|
||||
import { Button, TextField, Box } from '@mui/material';
|
||||
import { messageChannelContext } from './provider/MessageChannel';
|
||||
import Layout from './Layout';
|
||||
|
||||
function App() {
|
||||
const channel = React.useContext(messageChannelContext);
|
||||
|
||||
const [data, setData] = React.useState<{username: string|undefined, password: string|undefined}>({ username: undefined, password: undefined });
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<Box>
|
||||
<TextField variant='outlined' value={data.username ?? ''} onChange={(e) => setData({ password: data.password, username: e.target.value })} />
|
||||
<TextField variant='outlined' value={data.password ?? ''} onChange={(e) => setData({ password: e.target.value, username: data.username })} />
|
||||
<Button variant='contained' size='large' onClick={async () => {
|
||||
console.log(await channel?.auth({ username: data.username ?? '', password: data.password ?? ''}));
|
||||
}}>
|
||||
Test auth
|
||||
</Button>
|
||||
</Box>
|
||||
<Layout />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
13
gui/react/src/Layout.tsx
Normal file
13
gui/react/src/Layout.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import AuthButton from "./components/AuthButton";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const Layout: React.FC = () => {
|
||||
return <Box>
|
||||
<Box sx={{ height: 50, mb: 4 }}>
|
||||
<AuthButton />
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
114
gui/react/src/components/AuthButton.tsx
Normal file
114
gui/react/src/components/AuthButton.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import { Avatar, Box, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, List, ListItem, ListItemAvatar, TextField } from "@mui/material";
|
||||
import { grey } from '@mui/material/colors'
|
||||
import { Check, Close, CloseOutlined, PortraitOutlined } from '@mui/icons-material'
|
||||
import React from "react";
|
||||
import { messageChannelContext } from "../provider/MessageChannel";
|
||||
import Require from "./Require";
|
||||
import { useSnackbar } from "notistack";
|
||||
|
||||
const AuthButton: React.FC = () => {
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const [username, setUsername] = React.useState('');
|
||||
const [password, setPassword] = React.useState('');
|
||||
|
||||
const [usernameError, setUsernameError] = React.useState(false);
|
||||
const [passwordError, setPasswordError] = React.useState(false);
|
||||
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [error, setError] = React.useState<Error|undefined>(undefined);
|
||||
const [authed, setAuthed] = React.useState(false);
|
||||
|
||||
const checkAuth = async () => {
|
||||
console.log(await messageChannel?.checkToken());
|
||||
setAuthed((await messageChannel?.checkToken())?.isOk ?? false);
|
||||
}
|
||||
|
||||
React.useEffect(() => { checkAuth(); return () => {}; }, []);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!messageChannel)
|
||||
throw new Error('Invalid state'); //The components to confirm only render if the messageChannel is not undefinded
|
||||
if (username.trim().length === 0)
|
||||
return setUsernameError(true);
|
||||
if (password.trim().length === 0)
|
||||
return setPasswordError(true);
|
||||
setUsernameError(false);
|
||||
setPasswordError(false);
|
||||
setLoading(true);
|
||||
|
||||
const res = await messageChannel.auth({ username, password });
|
||||
if (res.isOk) {
|
||||
setOpen(false);
|
||||
snackbar.enqueueSnackbar('Logged in', {
|
||||
variant: 'success'
|
||||
});
|
||||
setUsername('');
|
||||
setPassword('');
|
||||
} else {
|
||||
setError(res.reason);
|
||||
}
|
||||
await checkAuth();
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
return <Require value={messageChannel}>
|
||||
<Dialog open={open}>
|
||||
<Dialog open={!!error}>
|
||||
<DialogTitle>Error during Authentication</DialogTitle>
|
||||
<DialogContentText>
|
||||
{error?.name}
|
||||
{error?.message}
|
||||
</DialogContentText>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setError(undefined)}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<DialogTitle sx={{ flexGrow: 1 }}>Authentication</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Here, you need to enter your username (most likely your Email) and your password.<br />
|
||||
These information are not stored anywhere and are only used to authenticate with the service once.
|
||||
</DialogContentText>
|
||||
<TextField
|
||||
error={usernameError}
|
||||
helperText={usernameError ? 'Please enter something before submiting' : undefined}
|
||||
margin="dense"
|
||||
id="username"
|
||||
label="Username"
|
||||
type="text"
|
||||
fullWidth
|
||||
variant="standard"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<TextField
|
||||
error={passwordError}
|
||||
helperText={passwordError ? 'Please enter something before submiting' : undefined}
|
||||
margin="dense"
|
||||
id="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
fullWidth
|
||||
variant="standard"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{loading && <CircularProgress size={30}/>}
|
||||
<Button disabled={loading} onClick={() => setOpen(false)}>Close</Button>
|
||||
<Button disabled={loading} onClick={() => handleSubmit()}>Authenticate</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Button startIcon={authed ? <Check />: <Close />} variant="contained" onClick={() => setOpen(true)}>Authenticate</Button>
|
||||
</Require>
|
||||
}
|
||||
|
||||
export default AuthButton;
|
||||
14
gui/react/src/components/Require.tsx
Normal file
14
gui/react/src/components/Require.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
import { Box, Backdrop, CircularProgress } from "@mui/material";
|
||||
|
||||
export type RequireType<T> = {
|
||||
value?: T
|
||||
}
|
||||
|
||||
const Require = <T, >(props: React.PropsWithChildren<RequireType<T>>) => {
|
||||
return props.value === undefined ? <Backdrop open>
|
||||
<CircularProgress />
|
||||
</Backdrop> : <Box>{props.children}</Box>
|
||||
}
|
||||
|
||||
export default Require;
|
||||
|
|
@ -4,16 +4,34 @@ import App from './App';
|
|||
import ServiceProvider from './provider/ServiceProvider';
|
||||
import Style from './Style';
|
||||
import MessageChannel from './provider/MessageChannel';
|
||||
import { IconButton } from "@mui/material";
|
||||
import { CloseOutlined } from "@mui/icons-material";
|
||||
import { SnackbarProvider, SnackbarKey } from 'notistack';
|
||||
|
||||
const notistackRef = React.createRef<SnackbarProvider>();
|
||||
const onClickDismiss = (key: SnackbarKey | undefined) => () => {
|
||||
if (notistackRef.current)
|
||||
notistackRef.current.closeSnackbar(key);
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Style>
|
||||
<ServiceProvider>
|
||||
<MessageChannel>
|
||||
<App />
|
||||
</MessageChannel>
|
||||
</ServiceProvider>
|
||||
</Style>
|
||||
<SnackbarProvider
|
||||
ref={notistackRef}
|
||||
action={(key) => (
|
||||
<IconButton onClick={onClickDismiss(key)} color="inherit">
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
>
|
||||
<Style>
|
||||
<ServiceProvider>
|
||||
<MessageChannel>
|
||||
<App />
|
||||
</MessageChannel>
|
||||
</ServiceProvider>
|
||||
</Style>
|
||||
</SnackbarProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
|
@ -16,7 +16,8 @@ const MessageChannelProvider: React.FC = ({ children }) => {
|
|||
}, [service])
|
||||
|
||||
const messageHandler: MessageHandler = {
|
||||
auth: async (data) => await ipcRenderer.invoke('auth', data)
|
||||
auth: async (data) => await ipcRenderer.invoke('auth', data),
|
||||
checkToken: async () => await ipcRenderer.invoke('checkToken')
|
||||
}
|
||||
|
||||
return <messageChannelContext.Provider value={messageHandler}>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react-jsx",
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"./src"
|
||||
|
|
|
|||
19907
package-lock.json
generated
19907
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -27,6 +27,7 @@
|
|||
"main": "gui/electron/src/index.js",
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"dotenv": "^14.3.2",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"form-data": "^4.0.0",
|
||||
|
|
@ -68,7 +69,7 @@
|
|||
"typescript": "^4.5.5"
|
||||
},
|
||||
"scripts": {
|
||||
"prestart": "npm run tsc",
|
||||
"prestart": "npm run tsc test",
|
||||
"start": "cd lib && electron-forge start",
|
||||
"docs": "ts-node modules/build-docs.ts",
|
||||
"tsc": "ts-node tsc.ts",
|
||||
|
|
|
|||
44
tsc.ts
44
tsc.ts
|
|
@ -3,22 +3,30 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
import { removeSync, copyFileSync } from 'fs-extra';
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
let buildIgnore: string[] = [];
|
||||
|
||||
if (argv.length > 0 && argv[0] !== 'test')
|
||||
buildIgnore = [
|
||||
'*/\\.env'
|
||||
];
|
||||
|
||||
const ignore = [
|
||||
'*SEP\\.git*',
|
||||
'*SEPlib*',
|
||||
'*SEPnode_modules*',
|
||||
'*SEP@types*',
|
||||
'*SEPout*',
|
||||
'*SEPbinSEPmkvtoolnix*',
|
||||
'*SEPtoken.yml$',
|
||||
'*SEPupdates.json$',
|
||||
'*SEPcr_token.yml$',
|
||||
'*SEPfuni_token.yml$',
|
||||
'*SEP\\.eslint*',
|
||||
'*SEP*\\.tsx?$',
|
||||
'SEP*fonts',
|
||||
'SEPreact*',
|
||||
].map(a => a.replace(/\*/g, '[^]*').replace(/SEP/g, path.sep === '\\' ? '\\\\' : '/')).map(a => new RegExp(a, 'i'));
|
||||
...buildIgnore,
|
||||
'*/\\.git*',
|
||||
'./lib*',
|
||||
'*/@types*',
|
||||
'./out*',
|
||||
'./bin/mkvtoolnix*',
|
||||
'./config/token.yml$',
|
||||
'./config/updates.json$',
|
||||
'./config/cr_token.yml$',
|
||||
'./config/funi_token.yml$',
|
||||
'*/\\.eslint*',
|
||||
'*/*\\.tsx?$',
|
||||
'./fonts*',
|
||||
'./gui/react*',
|
||||
].map(a => a.replace(/\*/g, '[^]*').replace(/\.\//g, escapeRegExp(__dirname) + '/').replace(/\//g, path.sep === '\\' ? '\\\\' : '/')).map(a => new RegExp(a, 'i'));
|
||||
|
||||
export { ignore };
|
||||
|
||||
|
|
@ -46,10 +54,10 @@ export { ignore };
|
|||
});
|
||||
|
||||
await waitForProcess(react);
|
||||
process.stdout.write('✓\nCopying files... ');
|
||||
|
||||
copyDir(path.join(__dirname, 'gui', 'react', 'build'), path.join(__dirname, 'lib', 'gui', 'electron', 'build'));
|
||||
|
||||
process.stdout.write('✓\nCopying files... ');
|
||||
const files = readDir(__dirname);
|
||||
files.forEach(item => {
|
||||
const itemPath = path.join(__dirname, 'lib', item.path.replace(__dirname, ''));
|
||||
|
|
@ -102,4 +110,8 @@ async function copyDir(src: string, dest: string) {
|
|||
await copyDir(srcPath, destPath) :
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeRegExp(string: string): string {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
Loading…
Reference in a new issue