Added ffmpeg command generator

This commit is contained in:
Weirdo 2021-03-28 01:38:18 +01:00
parent 40cc7358e4
commit 6e83e9a025
7 changed files with 8877 additions and 4315 deletions

12860
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,17 +4,17 @@
"private": true, "private": true,
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.11.2",
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0", "@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
"react": "^16.13.1", "react": "^16.14.0",
"react-copy-to-clipboard": "^5.0.2", "react-copy-to-clipboard": "^5.0.3",
"react-dom": "^16.13.1", "react-dom": "^16.14.0",
"react-dropzone": "^11.0.2", "react-dropzone": "^11.3.2",
"react-ga": "^3.1.2", "react-ga": "^3.3.0",
"react-scripts": "3.4.1" "react-scripts": "^4.0.3"
}, },
"scripts": { "scripts": {
"predeploy": "npm run build", "predeploy": "npm run build",

View file

@ -17,7 +17,8 @@ import FileCopyOutlined from '@material-ui/icons/FileCopyOutlined';
import { CopyToClipboard } from 'react-copy-to-clipboard'; import { CopyToClipboard } from 'react-copy-to-clipboard';
import { Accordion, AccordionItem } from 'react-sanfona';
import ControlledAccordions from './ControlledAccordions'
const useStyles = theme => ({ const useStyles = theme => ({
@ -34,32 +35,29 @@ const useStyles = theme => ({
form: { form: {
width: '100%', // Fix IE 11 issue. width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
} },
//Accordeon
heading: {
fontSize: theme.typography.pxToRem(15),
flexBasis: '33.33%',
flexShrink: 0,
},
secondaryHeading: {
fontSize: theme.typography.pxToRem(15),
color: theme.palette.text.secondary,
},
}); });
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://audible-tools.github.io/">
audible-tools
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
class ChecksumResolver extends React.Component { class ChecksumResolver extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
checksum: "" checksum: "",
fileName: "input.aax"
} }
} }
DarkerDisabledTextField = withStyles({ DarkerDisabledTextField = withStyles({
root: { root: {
marginRight: 8, marginRight: 8,
@ -69,6 +67,19 @@ class ChecksumResolver extends React.Component {
} }
})(TextField); })(TextField);
Copyright = (function () {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://audible-tools.github.io/">
audible-tools
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
})
setChecksum = (value) => { setChecksum = (value) => {
if (value.length > 40) { if (value.length > 40) {
return; return;
@ -125,6 +136,7 @@ class ChecksumResolver extends React.Component {
// return; // return;
// } // }
this.setState({ fileName: file.name });
const slic = file.slice(653, 653 + 20); const slic = file.slice(653, 653 + 20);
const results = this.buf2hex(await slic.arrayBuffer()); const results = this.buf2hex(await slic.arrayBuffer());
this.setChecksum(results) this.setChecksum(results)
@ -134,11 +146,11 @@ class ChecksumResolver extends React.Component {
render() { render() {
const { classes } = this.props; const { classes } = this.props;
const { checksum, activationBytes } = this.state; const { checksum, activationBytes, fileName } = this.state;
//const acc = accAX(); //const acc = accAX();
return ( return (
<Container component="main" maxWidth="xs"> <Container component="main" maxWidth="md">
<CssBaseline /> <CssBaseline />
<div className={classes.paper}> <div className={classes.paper}>
@ -221,21 +233,15 @@ class ChecksumResolver extends React.Component {
</form> </form>
</div> </div>
<ControlledAccordions
fileName={fileName}
activationBytes={activationBytes}
/>
<Box mt={1}> <Box mt={1}>
<Copyright /> <this.Copyright />
</Box> </Box>
<Accordion backgroundColor="Black">
{[1, 2, 3, 4, 5].map(item => {
return (
<AccordionItem title={`Item ${item}`} expanded={item === 1}>
<div>
{`Item ${item} content`}
</div>
</AccordionItem>
);
})}
</Accordion>
</Container> </Container>
); );
} }

171
src/ControlledAccordions.js Normal file
View file

@ -0,0 +1,171 @@
import React from 'react';
import { makeStyles, withStyles } from '@material-ui/core/styles';
import { Accordion, AccordionDetails, AccordionSummary } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import IconButton from '@material-ui/core/IconButton';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import FileCopyOutlined from '@material-ui/icons/FileCopyOutlined';
// import {ExpandMoreIcon, FileCopyOutlined} from '@material-ui/icons';
import OutputFormatSelection from './OutputFormatSelection'
import OSSelector from './OSSelector'
import { Radio, RadioGroup } from '@material-ui/core';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
class ControlledAccordions extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: "",
outputFormat: "m4b",
operatingSystem: "win"
};
}
DarkerDisabledTextField = withStyles({
root: {
marginRight: 8,
"& .MuiInputBase-root.Mui-disabled": {
color: "rgba(0, 0, 0, 0.6)"
}
}
})(TextField);
setExpanded = x => this.setState({ expanded: x })
handleChange = (panel) => (event, isExpanded) => {
this.setExpanded(isExpanded ? panel : false);
};
getCommand = () => {
const { outputFormat, operatingSystem } = this.state;
let { fileName, activationBytes } = this.props;
activationBytes = activationBytes ?? "00000000";
// ffmpeg.exe -y -activation_bytes 9f786605 -i '.\INFINITUM - Die Ewigkeit der Sterne.AAX' -ss 5 -to 20 -c copy out-t01.m4a
// faster:
// ffmpeg.exe -y -activation_bytes 9f786605 -i '.\INFINITUM - Die Ewigkeit der Sterne.AAX' -map_metadata 0 -id3v2_version 3 -ss 5 -to 20 -vn out-t02.m4a
// ffmpeg.exe -y -activation_bytes 9f786605 -i '.\INFINITUM - Die Ewigkeit der Sterne.AAX' -map_metadata 0 -ss 5 -to 20 -vn out-t02.m4a
// -vn: As an output option, disables video recording i.e. automatic selection or mapping of any video stream. For full manual control see the -map option.
// " works on ps and cmd as discriminator
const outputFormatCodecMaps = [
{ format: "m4b", codec: "copy" },
{ format: "flac", codec: "flac" },
{ format: "mp3", codec: "libmp3lame" },
];
const osToBinMaps = [
{ os: "win", cmd: "ffmpeg.exe", discriminator: '"' },
{ os: "linux", cmd: "./ffmpeg", discriminator: '\'' },
{ os: "osx", cmd: "./ffmpeg", discriminator: '\'' },
];
let fileNameWithoutExtension = fileName.split('.')[0];
fileNameWithoutExtension = fileNameWithoutExtension == 'input' ? 'output' : fileNameWithoutExtension;
const osMap = osToBinMaps.filter(x => x.os == operatingSystem)[0];
const codec = outputFormatCodecMaps.filter(x => x.format == outputFormat)[0].codec;
const bin = osMap.cmd;
const di = osMap.discriminator;
return `${bin} -y`
+ ` -activation_bytes ${activationBytes} -i ${di}.\\${fileName}${di}`
+ ` -map_metadata 0`
+ ` -id3v2_version 3`
+ ` -codec:a ${codec}`
+ ` -vn ${di}${fileNameWithoutExtension}.${outputFormat}${di}`;
}
render() {
const { classes } = this.props;
const { expanded, outputFormat, operatingSystem } = this.state;
return (
<div className={classes.root}>
<Accordion expanded={expanded === 'panel2'} onChange={this.handleChange('panel2')}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2bh-content"
id="panel2bh-header"
>
<Typography className={classes.heading}>Command</Typography>
<Typography className={classes.secondaryHeading}>
Generate ffmpeg command
</Typography>
</AccordionSummary>
<AccordionDetails style={{ display: 'block' }}>
<OutputFormatSelection
outputFormat={outputFormat}
setOutputFormat={x => this.setState({ outputFormat: x })}
/>
<OSSelector
operatingSystem={operatingSystem}
setOperatingSystem={x => this.setState({ operatingSystem: x })}
style={{ paddingLeft: '20px' }}
/>
<this.DarkerDisabledTextField
value={this.getCommand()}
disabled
multiline
variant="outlined"
margin="normal"
fullWidth
id="activationBytes"
label="cmd"
name="activationBytes"
autoComplete="activationBytes"
aria-readonly
fontSize={5}
InputProps={{
readOnly: true,
endAdornment: (
<CopyToClipboard text={this.getCommand()}>
<IconButton >
<FileCopyOutlined />
</IconButton>
</CopyToClipboard>
)
}}
/>
</AccordionDetails>
</Accordion>
</div>
);
}
}
const useStyles = theme => ({
root: {
width: '100%',
},
heading: {
fontSize: theme.typography.pxToRem(15),
flexBasis: '33.33%',
flexShrink: 0,
},
secondaryHeading: {
fontSize: theme.typography.pxToRem(15),
color: theme.palette.text.secondary,
},
});
export default withStyles(useStyles)(ControlledAccordions);

26
src/OSSelector.js Normal file
View file

@ -0,0 +1,26 @@
import React from 'react';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
export default function OSSelector(props) {
const { operatingSystem, setOperatingSystem, style } = props;
const handleChange = (event) => setOperatingSystem(event.target.value);
return (
<FormControl component="fieldset" style={style}>
<FormLabel component="legend">Operating System</FormLabel>
<RadioGroup aria-label="format" name="format" value={operatingSystem} onChange={handleChange}>
<FormControlLabel value="win" control={<Radio />} label="Windows" />
<FormControlLabel value="linux" control={<Radio />} label="Linux" />
<FormControlLabel value="osx" control={<Radio />} label="Mac" />
</RadioGroup>
</FormControl>
);
}

View file

@ -0,0 +1,29 @@
import React from 'react';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
export default function OutputFormatSelection(props) {
const { outputFormat, setOutputFormat, style } = props;
const handleChange = (event) => {
setOutputFormat(event.target.value);
};
return (
<FormControl component="fieldset" style={style}>
<FormLabel component="legend">Output Format</FormLabel>
<RadioGroup aria-label="format" name="format" value={outputFormat} onChange={handleChange}>
<FormControlLabel value="m4b" control={<Radio />} label="AAC m4b" />
<FormControlLabel value="flac" control={<Radio />} label="FLAC hq" />
<FormControlLabel value="mp3" control={<Radio />} label="Lame mp3" />
</RadioGroup>
</FormControl>
);
}

View file

@ -1,13 +1,27 @@
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family:
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', -apple-system,
BlinkMacSystemFont,
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif; sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family:
source-code-pro,
Menlo,
Monaco,
Consolas,
'Courier New',
monospace; monospace;
} }