mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-19 14:09:31 +02:00
feat: gif resize
This commit is contained in:
34
.idea/workspace.xml
generated
34
.idea/workspace.xml
generated
@@ -4,11 +4,13 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: image resize init">
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/types.ts" afterDir="false" />
|
||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: svg resize">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/ToolLayout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolLayout.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/tools/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/index.ts" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -399,15 +401,7 @@
|
||||
<workItem from="1743047367993" duration="986000" />
|
||||
<workItem from="1743103182313" duration="4264000" />
|
||||
<workItem from="1743348610793" duration="21855000" />
|
||||
<workItem from="1743556259185" duration="5197000" />
|
||||
</task>
|
||||
<task id="LOCAL-00129" summary="feat: json pretty">
|
||||
<option name="closed" value="true" />
|
||||
<created>1740661424202</created>
|
||||
<option name="number" value="00129" />
|
||||
<option name="presentableId" value="LOCAL-00129" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1740661424202</updated>
|
||||
<workItem from="1743556259185" duration="6368000" />
|
||||
</task>
|
||||
<task id="LOCAL-00130" summary="feat: json pretty">
|
||||
<option name="closed" value="true" />
|
||||
@@ -793,7 +787,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1743565606951</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="178" />
|
||||
<task id="LOCAL-00178" summary="feat: svg resize">
|
||||
<option name="closed" value="true" />
|
||||
<created>1743566704552</created>
|
||||
<option name="number" value="00178" />
|
||||
<option name="presentableId" value="LOCAL-00178" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1743566704552</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="179" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -840,7 +842,6 @@
|
||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||
<option name="CHECK_NEW_TODO" value="false" />
|
||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||
<MESSAGE value="feat: change pgn opacity" />
|
||||
<MESSAGE value="feat: crop png" />
|
||||
<MESSAGE value="chore: remove unnecessary files" />
|
||||
<MESSAGE value="refactor: validateJson" />
|
||||
@@ -865,7 +866,8 @@
|
||||
<MESSAGE value="chore: compress video icon" />
|
||||
<MESSAGE value="chore: tool description" />
|
||||
<MESSAGE value="feat: image resize init" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat: image resize init" />
|
||||
<MESSAGE value="feat: svg resize" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat: svg resize" />
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
|
@@ -5,6 +5,7 @@ import { capitalizeFirstLetter } from '../utils/string';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Icon, IconifyIcon } from '@iconify/react';
|
||||
import { categoriesColors } from '../config/uiConfig';
|
||||
import { getToolsByCategory } from '@tools/index';
|
||||
|
||||
const StyledButton = styled(Button)(({ theme }) => ({
|
||||
backgroundColor: 'white',
|
||||
@@ -70,7 +71,9 @@ export default function ToolHeader({
|
||||
items={[
|
||||
{ title: 'All tools', link: '/' },
|
||||
{
|
||||
title: capitalizeFirstLetter(type),
|
||||
title: getToolsByCategory().find(
|
||||
(category) => category.type === type
|
||||
)!.rawTitle,
|
||||
link: '/categories/' + type
|
||||
},
|
||||
{ title }
|
||||
|
@@ -53,7 +53,10 @@ export default function ToolLayout({
|
||||
{children}
|
||||
<Separator backgroundColor="#5581b5" margin="50px" />
|
||||
<AllTools
|
||||
title={`All ${capitalizeFirstLetter(type)} tools`}
|
||||
title={`All ${capitalizeFirstLetter(
|
||||
getToolsByCategory().find((category) => category.type === type)!
|
||||
.rawTitle
|
||||
)} tools`}
|
||||
toolCards={otherCategoryTools}
|
||||
/>
|
||||
</Box>
|
||||
|
@@ -43,10 +43,11 @@ export default function Home() {
|
||||
<IconButton onClick={() => navigate('/')}>
|
||||
<ArrowBackIcon color={'primary'} />
|
||||
</IconButton>
|
||||
<Typography
|
||||
fontSize={22}
|
||||
color={theme.palette.primary.main}
|
||||
>{`All ${capitalizeFirstLetter(categoryName)} Tools`}</Typography>
|
||||
<Typography fontSize={22} color={theme.palette.primary.main}>{`All ${
|
||||
getToolsByCategory().find(
|
||||
(category) => category.type === categoryName
|
||||
)!.rawTitle
|
||||
} Tools`}</Typography>
|
||||
</Stack>
|
||||
<Grid container spacing={2} mt={2}>
|
||||
{getToolsByCategory()
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import { InitialValuesType } from './types';
|
||||
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
||||
import { fetchFile, toBlobURL } from '@ffmpeg/util';
|
||||
|
||||
export const processImage = async (
|
||||
file: File,
|
||||
@@ -97,6 +99,60 @@ export const processImage = async (
|
||||
console.error('Error processing SVG:', error);
|
||||
// Fall back to canvas method if SVG processing fails
|
||||
}
|
||||
} else if (file.type === 'image/gif') {
|
||||
try {
|
||||
const ffmpeg = new FFmpeg();
|
||||
|
||||
await ffmpeg.load({
|
||||
wasmURL:
|
||||
'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.9/dist/esm/ffmpeg-core.wasm'
|
||||
});
|
||||
|
||||
// Write the input file to memory
|
||||
await ffmpeg.writeFile('input.gif', await fetchFile(file));
|
||||
|
||||
// Calculate new dimensions
|
||||
let newWidth = 0;
|
||||
let newHeight = 0;
|
||||
let scaleFilter = '';
|
||||
|
||||
if (resizeMethod === 'pixels') {
|
||||
if (dimensionType === 'width') {
|
||||
newWidth = parseInt(width);
|
||||
if (maintainAspectRatio) {
|
||||
scaleFilter = `scale=${newWidth}:-1`;
|
||||
} else {
|
||||
newHeight = parseInt(height);
|
||||
scaleFilter = `scale=${newWidth}:${newHeight}`;
|
||||
}
|
||||
} else {
|
||||
// height
|
||||
newHeight = parseInt(height);
|
||||
if (maintainAspectRatio) {
|
||||
scaleFilter = `scale=-1:${newHeight}`;
|
||||
} else {
|
||||
newWidth = parseInt(width);
|
||||
scaleFilter = `scale=${newWidth}:${newHeight}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// percentage
|
||||
const scale = parseInt(percentage) / 100;
|
||||
scaleFilter = `scale=iw*${scale}:ih*${scale}`;
|
||||
}
|
||||
|
||||
// Run FFmpeg command
|
||||
await ffmpeg.exec(['-i', 'input.gif', '-vf', scaleFilter, 'output.gif']);
|
||||
|
||||
// Read the output file
|
||||
const data = await ffmpeg.readFile('output.gif');
|
||||
|
||||
// Create a new File object
|
||||
return new File([data], file.name, { type: 'image/gif' });
|
||||
} catch (error) {
|
||||
console.error('Error processing GIF with FFmpeg:', error);
|
||||
// Fall back to canvas method if FFmpeg processing fails
|
||||
}
|
||||
}
|
||||
// Create canvas
|
||||
const canvas = document.createElement('canvas');
|
||||
|
@@ -130,6 +130,7 @@ export const filterTools = (
|
||||
|
||||
export const getToolsByCategory = (): {
|
||||
title: string;
|
||||
rawTitle: string;
|
||||
description: string;
|
||||
icon: IconifyIcon | string;
|
||||
type: string;
|
||||
@@ -144,6 +145,7 @@ export const getToolsByCategory = (): {
|
||||
(config) => config.type === type
|
||||
);
|
||||
return {
|
||||
rawTitle: categoryConfig?.title ?? capitalizeFirstLetter(type),
|
||||
title: `${categoryConfig?.title ?? capitalizeFirstLetter(type)} Tools`,
|
||||
description: categoryConfig?.value ?? '',
|
||||
type,
|
||||
|
Reference in New Issue
Block a user