mirror of
https://git.v0l.io/florian/bouquet.git
synced 2025-03-17 21:13:02 +01:00
feat: Added image resizing
This commit is contained in:
parent
c2ac9a5481
commit
694cb4cc49
@ -1,5 +1,5 @@
|
||||
import { formatFileSize, formatDate } from '../../utils/utils';
|
||||
import { ClipboardDocumentIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { ClipboardDocumentIcon } from '@heroicons/react/24/outline';
|
||||
import { BlobDescriptor } from 'blossom-client-sdk';
|
||||
import { AudioBlob, ID3Tag, fetchId3Tag } from '../../utils/id3';
|
||||
import { useQueries } from '@tanstack/react-query';
|
||||
|
@ -33,7 +33,12 @@ const Badge = ({ ev }: { ev: NDKEvent }) => {
|
||||
relays: ev.onRelays.map(r => r.url),
|
||||
} as AddressPointer);
|
||||
return (
|
||||
<a target="_blank" className="badge badge-primary mr-2 tooltip" href={`https://blossom.hzrd149.com/#/drive/${naddr}`} data-tip={driveIdentifier}>
|
||||
<a
|
||||
target="_blank"
|
||||
className="badge badge-primary mr-2 tooltip"
|
||||
href={`https://blossom.hzrd149.com/#/drive/${naddr}`}
|
||||
data-tip={driveIdentifier}
|
||||
>
|
||||
🌸 drive
|
||||
</a>
|
||||
);
|
||||
|
@ -61,6 +61,7 @@ const BlobList = ({ blobs, onDelete, title, className = '' }: BlobListProps) =>
|
||||
className="link link-primary tooltip"
|
||||
data-tip="Copy link to clipboard"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(blob.url);
|
||||
}}
|
||||
|
@ -54,16 +54,16 @@ function Home() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServerList
|
||||
servers={Object.values(serverInfo).sort()}
|
||||
selectedServer={selectedServer}
|
||||
setSelectedServer={setSelectedServer}
|
||||
onTransfer={() => navigate('/transfer/' + selectedServer)}
|
||||
onCheck={() => navigate('/check/' + selectedServer)}
|
||||
title={<>Servers</>}
|
||||
manageServers={true}
|
||||
withVirtualServers={true}
|
||||
></ServerList>
|
||||
<ServerList
|
||||
servers={Object.values(serverInfo).sort()}
|
||||
selectedServer={selectedServer}
|
||||
setSelectedServer={setSelectedServer}
|
||||
onTransfer={() => navigate('/transfer/' + selectedServer)}
|
||||
onCheck={() => navigate('/check/' + selectedServer)}
|
||||
title={<>Servers</>}
|
||||
manageServers={true}
|
||||
withVirtualServers={true}
|
||||
></ServerList>
|
||||
|
||||
{selectedServer && serverInfo[selectedServer] && selectedServerBlobs && (
|
||||
<BlobList
|
||||
|
@ -12,6 +12,7 @@ import { formatFileSize } from '../utils/utils';
|
||||
import FileEventEditor, { FileEventData } from '../components/FileEventEditor/FileEventEditor';
|
||||
import pLimit from 'p-limit';
|
||||
import { Server, useUserServers } from '../utils/useUserServers';
|
||||
import { resizeImage } from '../utils/resize';
|
||||
|
||||
type TransferStats = {
|
||||
enabled: boolean;
|
||||
@ -33,6 +34,31 @@ steps
|
||||
-
|
||||
*/
|
||||
|
||||
type ResizeOptionType = {
|
||||
name: string;
|
||||
format?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
const ResizeOptions: ResizeOptionType[] = [
|
||||
{
|
||||
name: 'Orignal Image',
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
},
|
||||
{
|
||||
name: 'max. 2048x2048 pixels',
|
||||
width: 2048,
|
||||
height: 2048,
|
||||
},
|
||||
{
|
||||
name: 'max. 1080x1080 pixels',
|
||||
width: 1080,
|
||||
height: 1080,
|
||||
},
|
||||
];
|
||||
|
||||
function Upload() {
|
||||
const servers = useUserServers();
|
||||
const { signEventTemplate } = useNDK();
|
||||
@ -48,6 +74,8 @@ function Upload() {
|
||||
|
||||
const [fileEventsToPublish, setFileEventsToPublish] = useState<FileEventData[]>([]);
|
||||
const [uploadBusy, setUploadBusy] = useState(false);
|
||||
const [imageResize, setImageResize] = useState(0);
|
||||
|
||||
// const [resizeImages, setResizeImages] = useState(false);
|
||||
// const [publishToNostr, setPublishToNostr] = useState(false);
|
||||
|
||||
@ -94,15 +122,22 @@ function Upload() {
|
||||
|
||||
const filesToUpload: File[] = [];
|
||||
for (const f of files) {
|
||||
if (cleanPrivateData) {
|
||||
filesToUpload.push(await removeExifData(f));
|
||||
} else {
|
||||
filesToUpload.push(f);
|
||||
let processedFile = f;
|
||||
|
||||
if (processedFile.type.startsWith('image/')) {
|
||||
// Do image processing according to options
|
||||
if (imageResize > 0) {
|
||||
const { width, height } = ResizeOptions[imageResize];
|
||||
processedFile = await resizeImage(processedFile, width, height);
|
||||
}
|
||||
if (cleanPrivateData) {
|
||||
processedFile = await removeExifData(processedFile);
|
||||
}
|
||||
}
|
||||
|
||||
filesToUpload.push(processedFile);
|
||||
}
|
||||
|
||||
// TODO use https://github.com/davejm/client-compress
|
||||
// for image resizing
|
||||
const fileDimensions: { [key: string]: FileEventData } = {};
|
||||
for (const file of filesToUpload) {
|
||||
let data = {
|
||||
@ -232,30 +267,44 @@ function Upload() {
|
||||
return (
|
||||
<>
|
||||
<h2 className=" py-4">Upload</h2>
|
||||
|
||||
<button className='btn btn-primary' onClick={async () => {
|
||||
|
||||
const url = "https://media-server.slidestr.net/3c3f3f0b67c17953e59ebdb53b7fd83bf68b552823b927fa9718a52e12d53c0a";
|
||||
const targetServer= "https://test-store.slidestr.net";
|
||||
{/*
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={async () => {
|
||||
const url =
|
||||
'https://media-server.slidestr.net/3c3f3f0b67c17953e59ebdb53b7fd83bf68b552823b927fa9718a52e12d53c0a';
|
||||
const targetServer = 'https://test-store.slidestr.net';
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'Content-Type':'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const blossomClient = new BlossomClient(targetServer, signEventTemplate);
|
||||
const mirrorAuth = await blossomClient.getMirrorAuth('3c3f3f0b67c17953e59ebdb53b7fd83bf68b552823b927fa9718a52e12d53c0a', 'Upload Blob');
|
||||
const mirrorAuth = await blossomClient.getMirrorAuth(
|
||||
'3c3f3f0b67c17953e59ebdb53b7fd83bf68b552823b927fa9718a52e12d53c0a',
|
||||
'Upload Blob'
|
||||
);
|
||||
|
||||
const res = await axios.put<BlobDescriptor>(`${targetServer}/mirror`, { url }, {
|
||||
headers: mirrorAuth ? { ...headers, authorization: BlossomClient.encodeAuthorizationHeader(mirrorAuth) } : headers,
|
||||
|
||||
});
|
||||
const res = await axios.put<BlobDescriptor>(
|
||||
`${targetServer}/mirror`,
|
||||
{ url },
|
||||
{
|
||||
headers: mirrorAuth
|
||||
? { ...headers, authorization: BlossomClient.encodeAuthorizationHeader(mirrorAuth) }
|
||||
: headers,
|
||||
}
|
||||
);
|
||||
|
||||
console.log(res.status);
|
||||
console.log(res.data);
|
||||
}}
|
||||
>
|
||||
Test Mirror
|
||||
</button>
|
||||
*/}
|
||||
|
||||
}}>Test Mirror</button>
|
||||
|
||||
<div className=" bg-base-200 rounded-xl p-4 text-neutral-content gap-4 flex flex-col">
|
||||
<input
|
||||
id="browse"
|
||||
@ -299,8 +348,8 @@ function Upload() {
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
<h3 className="text-lg text-neutral-content">Options</h3>
|
||||
<div className="cursor-pointer grid gap-2" style={{ gridTemplateColumns: '1.5em auto' }}>
|
||||
<h3 className="text-lg text-neutral-content">Image Options</h3>
|
||||
<div className="cursor-pointer grid gap-2 items-center" style={{ gridTemplateColumns: '1.5em auto' }}>
|
||||
<CheckBox
|
||||
name="cleanData"
|
||||
disabled={uploadBusy}
|
||||
@ -308,20 +357,31 @@ function Upload() {
|
||||
setChecked={c => setCleanPrivateData(c)}
|
||||
label="Clean private data in images (EXIF)"
|
||||
></CheckBox>
|
||||
{/*
|
||||
<CheckBox
|
||||
name="resize"
|
||||
checked={resizeImages}
|
||||
setChecked={c => setResizeImages(c)}
|
||||
label="Resize images to max. 2048 x 2048 (NOT IMPLEMENTED YET!)"
|
||||
></CheckBox>
|
||||
<CheckBox
|
||||
name="publish"
|
||||
checked={publishToNostr}
|
||||
setChecked={c => setPublishToNostr(c)}
|
||||
label="Publish to NOSTR (as 1063 file metadata event) (NOT IMPLEMENTED YET!)"
|
||||
></CheckBox>
|
||||
*/}
|
||||
<input
|
||||
className="checkbox checkbox-primary "
|
||||
id="resizeOption"
|
||||
disabled={uploadBusy}
|
||||
type="checkbox"
|
||||
checked={imageResize > 0}
|
||||
onChange={() => setImageResize(irs => (irs > 0 ? 0 : 1))}
|
||||
/>
|
||||
<div>
|
||||
<label htmlFor="resizeOption" className="cursor-pointer select-none">
|
||||
Resize Image
|
||||
</label>
|
||||
<select
|
||||
disabled={uploadBusy || imageResize == 0}
|
||||
className="select select-bordered select-sm ml-4 w-full max-w-xs"
|
||||
onChange={e => setImageResize(e.target.selectedIndex)}
|
||||
value={imageResize}
|
||||
>
|
||||
{ResizeOptions.map((ro, i) => (
|
||||
<option key={ro.name} disabled={i == 0}>
|
||||
{ro.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2">
|
||||
<button className="btn btn-primary" onClick={() => upload()} disabled={uploadBusy || files.length == 0}>
|
||||
@ -347,7 +407,7 @@ function Upload() {
|
||||
<>
|
||||
<h2 className="py-4">Publish events</h2>
|
||||
{fileEventsToPublish.map(fe => (
|
||||
<FileEventEditor data={fe} />
|
||||
<FileEventEditor key={fe.x} data={fe} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
58
src/utils/resize.ts
Normal file
58
src/utils/resize.ts
Normal file
@ -0,0 +1,58 @@
|
||||
async function compress(
|
||||
file: File,
|
||||
{ quality = 0.95, maxWidth, maxHeight }: { quality: number; maxWidth?: number; maxHeight?: number }
|
||||
): Promise<File> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = async function () {
|
||||
const source = {
|
||||
width: img.naturalWidth,
|
||||
height: img.naturalHeight,
|
||||
};
|
||||
|
||||
const target = {
|
||||
width: maxWidth || source.width,
|
||||
height: maxHeight || source.height,
|
||||
};
|
||||
const aspectRatio = Math.min(1, target.width / source.width, target.height / source.height);
|
||||
target.width = source.width * aspectRatio;
|
||||
target.height = source.height * aspectRatio;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = target.width;
|
||||
canvas.height = target.height;
|
||||
const context = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
context?.drawImage(img, 0, 0, target.width, target.height);
|
||||
|
||||
canvas.toBlob(
|
||||
blob => {
|
||||
if (blob == null) {
|
||||
reject('Could not convert image.');
|
||||
return;
|
||||
}
|
||||
|
||||
const newFile = new File([blob], file.name, {
|
||||
type: file.type,
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
|
||||
resolve(newFile);
|
||||
},
|
||||
file.type,
|
||||
quality
|
||||
);
|
||||
};
|
||||
|
||||
img.src = URL.createObjectURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
export async function resizeImage(input: File, width?: number, height?: number): Promise<File> {
|
||||
const result = await compress(input, {
|
||||
quality: 0.95,
|
||||
maxWidth: width,
|
||||
maxHeight: height,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
@ -14,7 +14,7 @@ export interface ServerInfo extends Server {
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
blobs?: BlobDescriptor[];
|
||||
};
|
||||
}
|
||||
|
||||
type BlobDictionary = {
|
||||
[key: string]: { blob: BlobDescriptor; servers: string[] };
|
||||
@ -87,7 +87,7 @@ export const useServerInfo = () => {
|
||||
name: 'All servers',
|
||||
url: 'all',
|
||||
blobs: [],
|
||||
type: 'blossom'
|
||||
type: 'blossom',
|
||||
};
|
||||
const allInfo = serversInfos.reduce(
|
||||
(acc, server) => ({
|
||||
|
@ -36,7 +36,7 @@ export const useUserServers = (): Server[] => {
|
||||
url: s.toLocaleLowerCase().replace(/\/$/, ''),
|
||||
type: 'blossom' as ServerType,
|
||||
})),
|
||||
/* ...(nip96ServerListEvent?.getMatchingTags('server').map(t => t[1]) || []).map(s => ({
|
||||
/* ...(nip96ServerListEvent?.getMatchingTags('server').map(t => t[1]) || []).map(s => ({
|
||||
url: s.toLocaleLowerCase().replace(/\/$/, ''),
|
||||
type: 'nip96' as ServerType,
|
||||
})),*/
|
||||
@ -48,7 +48,7 @@ export const useUserServers = (): Server[] => {
|
||||
url: s.url,
|
||||
}));
|
||||
}, [blossomServerListEvent, nip96ServerListEvent]);
|
||||
|
||||
|
||||
// console.log(servers);
|
||||
return servers;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user