Start of gui

This commit is contained in:
Izuco 2022-01-23 20:26:38 +01:00
parent a9a61c717e
commit 2e7271afcb
No known key found for this signature in database
GPG key ID: E9CBE9E4EF3A1BFA
23 changed files with 25354 additions and 7423 deletions

View file

@ -1,7 +1,8 @@
{
"env": {
"es2021": true,
"node": true
"node": true,
"browser": true
},
"extends": [
"eslint:recommended",
@ -13,7 +14,12 @@
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/electron",
"plugin:import/typescript"
],
"rules": {
"@typescript-eslint/no-explicit-any": "off",

7
.gitignore vendored
View file

@ -1,7 +1,7 @@
/bin/ff*
/bin/mkv*
/_builds/*
/node_modules/
**/node_modules/
/videos/*.json
/videos/*.ts
.DS_Store
@ -22,4 +22,7 @@ updates.json
funi_token.yml
cr_token.yml
archive.json
fonts
fonts
.webpack/
out/
dist/

16
@types/messageHandler.d.ts vendored Normal file
View file

@ -0,0 +1,16 @@
export interface MessageHandler {
auth: (data: AuthData) => Promise<AuthResponse>;
}
export type AuthData = { username: string, password: string };
export type AuthResponse = ResponseBase<undefined>;
export type ResponseBase<T> = ({
isOk: true,
value: T
} | {
isOk: false,
reason: Error
});
export type PossibleMessanges = keyof ServiceHandler;

16
electron/.eslintrc.json Normal file
View file

@ -0,0 +1,16 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/electron",
"plugin:import/typescript"
],
"parser": "@typescript-eslint/parser"
}

View file

@ -0,0 +1,13 @@
import { ipcMain, MessagePortMain } from "electron";
import { MessageHandler } from "../../../@types/messageHandler";
import Funimation from './serviceHandler/funimation';
let handler: MessageHandler|undefined;
ipcMain.on('setup', (ev, data) => {
if (data === 'funi') {
handler = new Funimation();
} else if (data === 'crunchy') {
}
});

View file

@ -0,0 +1,11 @@
import { AuthData, AuthResponse, MessageHandler } from "../../../../@types/messageHandler";
import * as funi from '../../../../funi';
class FunimationHandler implements MessageHandler {
public auth(data: AuthData) {
return funi.auth(data.username, data.password);
}
}
export default FunimationHandler;

14
electron/src/index.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-eval'"
/>
</head>
<body>
<div id="root"></div>
</body>
</html>

40
electron/src/index.ts Normal file
View file

@ -0,0 +1,40 @@
import { app, BrowserWindow } from 'electron';
import json from '../../package.json';
import './app/messageHandler';
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = (): void => {
// Create the browser window.
const mainWindow = new BrowserWindow({
height: 600,
width: 800,
title: json.name,
webPreferences: {
nodeIntegration: true,
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY
},
});
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
mainWindow.webContents.openDevTools();
};
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

5
electron/src/preload.ts Normal file
View file

@ -0,0 +1,5 @@
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('Electron', {
ipcRenderer
});

View file

@ -0,0 +1,23 @@
import React from 'react';
function App() {
return (
<div className="App">
<header className="App-header">
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View file

@ -0,0 +1,22 @@
import React from "react";
import { Container, Box, ThemeProvider, createTheme, Theme } from "@mui/material";
const makeTheme = (mode: 'dark'|'light') : Partial<Theme> => {
console.log(mode);
return createTheme({
palette: {
mode,
},
});
};
const Style: React.FC = ({children}) => {
return <ThemeProvider theme={makeTheme('dark')}>
<Container sx={{ mt: 3 }} maxWidth='xl'>
<Box sx={{ position: 'fixed', height: '100%', width: '100%', zIndex: -500, backgroundColor: 'rgb(0, 30, 60)', top: 0, left: 0 }}/>
{children}
</Container>
</ThemeProvider>;
}
export default Style;

View file

@ -0,0 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import ServiceProvider from './provider/ServiceProvider';
import Style from './Style';
import MessageChannel from './provider/MessageChannel';
ReactDOM.render(
<React.StrictMode>
<Style>
<ServiceProvider>
<MessageChannel>
<App />
</MessageChannel>
</ServiceProvider>
</Style>
</React.StrictMode>,
document.getElementById('root')
);

View file

@ -0,0 +1,27 @@
import React from 'react';
import type { AuthData, AuthResponse, MessageHandler, ResponseBase } from '../../../../@types/messageHandler';
import { serviceContext } from './ServiceProvider';
const messageChannelContext = React.createContext<MessageHandler|undefined>(undefined);
const MessageChannelProvider: React.FC = ({ children }) => {
const service = React.useContext(serviceContext);
const { ipcRenderer } = window.Electron;
React.useEffect(() => {
ipcRenderer.invoke('setup', service);
}, [service])
const messageHandler: MessageHandler = {
auth: async (data) => await ipcRenderer.invoke('auth', data)
}
return <messageChannelContext.Provider value={messageHandler}>
{children}
</messageChannelContext.Provider>;
};
export default MessageChannelProvider;

View file

@ -0,0 +1,26 @@
import React from 'react';
import {Divider, Box, Button, Typography} from '@mui/material';
type Services = 'funi'|'crunchy';
export const serviceContext = React.createContext<Services|undefined>(undefined);
const ServiceProvider: React.FC = ({ children }) => {
const [service, setService] = React.useState<Services|undefined>();
return service === undefined ?
<Box>
<Typography color="text.primary" variant='h3' sx={{ textAlign: 'center', mb: 5 }}>Please choose your service</Typography>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
<Button size='large' variant="contained" onClick={() => setService('funi')} >Funimation</Button>
<Divider orientation='vertical' flexItem />
<Button size='large' variant="contained" onClick={() => setService('crunchy')}>Crunchyroll</Button>
</Box>
</Box>
: <serviceContext.Provider value={service}>
{children}
</serviceContext.Provider>;
};
export default ServiceProvider;

1
electron/src/renderer.ts Normal file
View file

@ -0,0 +1 @@
import './react/index.tsx';

17
funi.ts
View file

@ -40,6 +40,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 { AuthResponse } from './@types/messageHandler';
// check page
argv.p = 1;
@ -63,10 +64,10 @@ export default (async () => {
}
// select mode
if (argv.silentAuth && !argv.auth) {
await auth();
await auth(argv.username, argv.password);
}
if(argv.auth){
auth();
auth(argv.username, argv.password);
}
else if(argv.search){
searchShow();
@ -80,10 +81,10 @@ export default (async () => {
});
// auth
async function auth(){
async function auth(username?: string, password?: string, ask = true): Promise<AuthResponse> {
const authOpts = {
user: argv.username ?? await shlp.question('[Q] LOGIN/EMAIL'),
pass: argv.password ?? await shlp.question('[Q] PASSWORD ')
user: username ? username : ask ? await shlp.question('[Q] LOGIN/EMAIL') : '',
pass: password ? password : ask ? await shlp.question('[Q] PASSWORD ') : ''
};
const authData = await getData({
baseUrl: api_host,
@ -96,14 +97,16 @@ async function auth(){
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});
return { isOk: true, value: undefined };
} else {
console.log('[ERROR]%s\n', ' No token found');
if (argv.debug) {
console.log(resJSON);
}
process.exit(1);
return { isOk: false, reason: new Error(resJSON) }
}
}
return { isOk: false, reason: new Error('Login request failed') }
}
// search show
@ -789,3 +792,5 @@ async function downloadFile(filename: string, chunkList: {
return downloadStatus.ok;
}
export { auth };

32240
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,67 +1,140 @@
{
"name": "multi-downloader-nx",
"short_name": "aniDL",
"version": "2.0.18",
"description": "Download videos from Funimation or Crunchyroll via cli",
"keywords": [
"download",
"downloader",
"funimation",
"funimationnow",
"crunchy",
"crunchyroll",
"util",
"utility",
"cli"
],
"author": "AniDL/Izu-co",
"homepage": "https://github.com/anidl/multi-downloader-nx",
"repository": {
"type": "git",
"url": "https://github.com/anidl/multi-downloader-nx.git"
},
"bugs": {
"url": "https://github.com/anidl/multi-downloader-nx/issues"
},
"license": "MIT",
"main": "index.js",
"dependencies": {
"cheerio": "^1.0.0-rc.10",
"form-data": "^4.0.0",
"fs-extra": "^10.0.0",
"got": "^11.8.3",
"hls-download": "^2.6.7",
"iso-639": "^0.2.2",
"lookpath": "^1.1.0",
"m3u8-parsed": "^1.3.0",
"sei-helper": "^3.3.0",
"yaml": "^1.10.0",
"yargs": "^17.2.1"
},
"devDependencies": {
"@types/fs-extra": "^9.0.13",
"@types/node": "^16.11.9",
"@types/yargs": "^17.0.7",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"eslint": "^7.30.0",
"pkg": "^5.4.1",
"removeNPMAbsolutePaths": "^2.0.0",
"ts-node": "^10.4.0",
"typescript": "^4.5.2"
},
"scripts": {
"docs": "ts-node modules/build-docs.ts",
"tsc": "ts-node tsc.ts",
"prebuild-win64": "npm run tsc",
"prebuild-linux64": "npm run tsc",
"prebuild-macos64": "npm run tsc",
"build-win64": "cd lib && node modules/build win64",
"build-linux64": "cd lib && node modules/build linux64",
"build-macos64": "cd lib && node modules/build macos64",
"eslint": "eslint *.js modules",
"eslint-fix": "eslint *.js modules --fix",
"pretest": "npm run tsc",
"test": "cd lib && node modules/build win64 && node modules/build linux64 && node modules/build macos64"
"name": "multi-downloader-nx",
"short_name": "aniDL",
"version": "2.0.18",
"description": "Download videos from Funimation or Crunchyroll via cli",
"keywords": [
"download",
"downloader",
"funimation",
"funimationnow",
"crunchy",
"crunchyroll",
"util",
"utility",
"cli"
],
"author": "AniDL/Izu-co",
"homepage": "https://github.com/anidl/multi-downloader-nx",
"repository": {
"type": "git",
"url": "https://github.com/anidl/multi-downloader-nx.git"
},
"bugs": {
"url": "https://github.com/anidl/multi-downloader-nx/issues"
},
"license": "MIT",
"main": ".webpack/main/index.js",
"dependencies": {
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@mui/material": "^5.3.0",
"cheerio": "^1.0.0-rc.10",
"electron-squirrel-startup": "^1.0.0",
"eslint-plugin-import": "^2.25.4",
"form-data": "^4.0.0",
"fs-extra": "^10.0.0",
"got": "^11.8.3",
"hls-download": "^2.6.7",
"iso-639": "^0.2.2",
"lookpath": "^1.1.0",
"m3u8-parsed": "^1.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sei-helper": "^3.3.0",
"typescript-eslint": "^0.0.1-alpha.0",
"yaml": "^1.10.0",
"yargs": "^17.2.1"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.61",
"@electron-forge/maker-deb": "^6.0.0-beta.61",
"@electron-forge/maker-rpm": "^6.0.0-beta.61",
"@electron-forge/maker-squirrel": "^6.0.0-beta.61",
"@electron-forge/maker-zip": "^6.0.0-beta.61",
"@electron-forge/plugin-webpack": "^6.0.0-beta.61",
"@types/fs-extra": "^9.0.13",
"@types/node": "^16.11.9",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/yargs": "^17.0.7",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@vercel/webpack-asset-relocator-loader": "^1.7.0",
"css-loader": "^6.5.1",
"electron": "16.0.7",
"eslint": "^7.30.0",
"eslint-plugin-import": "^2.25.4",
"fork-ts-checker-webpack-plugin": "^6.5.0",
"node-loader": "^2.0.0",
"pkg": "^5.4.1",
"removeNPMAbsolutePaths": "^2.0.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.2.6",
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
},
"scripts": {
"start": "electron-forge start",
"docs": "ts-node modules/build-docs.ts",
"tsc": "ts-node tsc.ts",
"prebuild-win64": "npm run tsc",
"prebuild-linux64": "npm run tsc",
"prebuild-macos64": "npm run tsc",
"build-win64": "cd lib && node modules/build win64",
"build-linux64": "cd lib && node modules/build linux64",
"build-macos64": "cd lib && node modules/build macos64",
"eslint": "eslint *.js modules",
"eslint-fix": "eslint *.js modules --fix",
"pretest": "npm run tsc",
"test": "cd lib && node modules/build win64 && node modules/build linux64 && node modules/build macos64"
},
"config": {
"forge": {
"packagerConfig": {},
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {
"name": "electron"
}
},
{
"name": "@electron-forge/maker-zip",
"platforms": [
"darwin"
]
},
{
"name": "@electron-forge/maker-deb",
"config": {}
},
{
"name": "@electron-forge/maker-rpm",
"config": {}
}
],
"plugins": [
[
"@electron-forge/plugin-webpack",
{
"mainConfig": "./webpack.main.config.js",
"renderer": {
"config": "./webpack.renderer.config.js",
"entryPoints": [
{
"html": "./electron/src/index.html",
"js": "./electron/src/renderer.ts",
"name": "main_window",
"preload": {
"js": "./electron/src/preload.ts"
}
}
]
}
}
]
]
}
}
}

View file

@ -8,7 +8,8 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"downlevelIteration": true
"downlevelIteration": true,
"jsx": "react"
},
"exclude": [
"./videos",

14
webpack.main.config.js Normal file
View file

@ -0,0 +1,14 @@
module.exports = {
/**
* This is the main entry point for your application, it's the first file
* that runs in the main process.
*/
entry: './electron/src/index.ts',
// Put your normal webpack config below here
module: {
rules: require('./webpack.rules'),
},
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json']
},
};

5
webpack.plugins.js Normal file
View file

@ -0,0 +1,5 @@
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = [
new ForkTsCheckerWebpackPlugin()
];

View file

@ -0,0 +1,18 @@
const rules = require('./webpack.rules');
const plugins = require('./webpack.plugins');
rules.push({
test: /\.css$/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});
module.exports = {
module: {
rules,
},
plugins: plugins,
resolve: {
fallback: { path: false },
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css']
},
};

29
webpack.rules.js Normal file
View file

@ -0,0 +1,29 @@
module.exports = [
// Add support for native node modules
{
// We're specifying native_modules in the test because the asset relocator loader generates a
// "fake" .node file which is really a cjs file.
test: /native_modules\/.+\.node$/,
use: 'node-loader',
},
{
test: /\.(m?js|node)$/,
parser: { amd: false },
use: {
loader: '@vercel/webpack-asset-relocator-loader',
options: {
outputAssetBase: 'native_modules',
},
},
},
{
test: /\.tsx?$/,
exclude: /(node_modules|\.webpack)/,
use: {
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
},
];