Compare commits

...

3 Commits

Author SHA1 Message Date
syuilo c5dc0fd51b Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-06-07 09:26:51 +09:00
syuilo dda2ad6bcd fix(frontend): support non-image files 2025-06-07 09:26:49 +09:00
github-actions[bot] a5429ebeee Bump version to 2025.6.1-alpha.3 2025-06-06 23:36:20 +00:00
3 changed files with 54 additions and 26 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.6.1-alpha.2", "version": "2025.6.1-alpha.3",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -24,7 +24,14 @@ export type UploaderFeatures = {
crop?: boolean; crop?: boolean;
}; };
const COMPRESSION_SUPPORTED_TYPES = [ const THUMBNAIL_SUPPORTED_TYPES = [
'image/jpeg',
'image/png',
'image/webp',
'image/svg+xml',
];
const IMAGE_COMPRESSION_SUPPORTED_TYPES = [
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/webp', 'image/webp',
@ -45,6 +52,13 @@ const IMAGE_EDITING_SUPPORTED_TYPES = [
const WATERMARK_SUPPORTED_TYPES = IMAGE_EDITING_SUPPORTED_TYPES; const WATERMARK_SUPPORTED_TYPES = IMAGE_EDITING_SUPPORTED_TYPES;
const IMAGE_PREPROCESS_NEEDED_TYPES = [
...WATERMARK_SUPPORTED_TYPES,
...IMAGE_COMPRESSION_SUPPORTED_TYPES,
...CROPPING_SUPPORTED_TYPES,
...IMAGE_EDITING_SUPPORTED_TYPES,
];
const mimeTypeMap = { const mimeTypeMap = {
'image/webp': 'webp', 'image/webp': 'webp',
'image/jpeg': 'jpg', 'image/jpeg': 'jpg',
@ -56,7 +70,7 @@ export type UploaderItem = {
name: string; name: string;
uploadName?: string; uploadName?: string;
progress: { max: number; value: number } | null; progress: { max: number; value: number } | null;
thumbnail: string; thumbnail: string | null;
preprocessing: boolean; preprocessing: boolean;
uploading: boolean; uploading: boolean;
uploaded: Misskey.entities.DriveFile | null; uploaded: Misskey.entities.DriveFile | null;
@ -121,7 +135,7 @@ export function useUploader(options: {
id, id,
name: prefer.s.keepOriginalFilename ? filename : id + extension, name: prefer.s.keepOriginalFilename ? filename : id + extension,
progress: null, progress: null,
thumbnail: window.URL.createObjectURL(file), thumbnail: THUMBNAIL_SUPPORTED_TYPES.includes(file.type) ? window.URL.createObjectURL(file) : null,
preprocessing: false, preprocessing: false,
uploading: false, uploading: false,
aborted: false, aborted: false,
@ -144,7 +158,7 @@ export function useUploader(options: {
} }
function removeItem(item: UploaderItem) { function removeItem(item: UploaderItem) {
URL.revokeObjectURL(item.thumbnail); if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
items.value.splice(items.value.indexOf(item), 1); items.value.splice(items.value.indexOf(item), 1);
} }
@ -196,7 +210,7 @@ export function useUploader(options: {
text: i18n.ts.cropImage, text: i18n.ts.cropImage,
action: async () => { action: async () => {
const cropped = await os.cropImageFile(item.file, { aspectRatio: null }); const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
URL.revokeObjectURL(item.thumbnail); if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
items.value.splice(items.value.indexOf(item), 1, { items.value.splice(items.value.indexOf(item), 1, {
...item, ...item,
file: markRaw(cropped), file: markRaw(cropped),
@ -225,7 +239,7 @@ export function useUploader(options: {
image: item.file, image: item.file,
}, { }, {
ok: (file) => { ok: (file) => {
URL.revokeObjectURL(item.thumbnail); if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
items.value.splice(items.value.indexOf(item), 1, { items.value.splice(items.value.indexOf(item), 1, {
...item, ...item,
file: markRaw(file), file: markRaw(file),
@ -295,7 +309,7 @@ export function useUploader(options: {
} }
if ( if (
COMPRESSION_SUPPORTED_TYPES.includes(item.file.type) && IMAGE_COMPRESSION_SUPPORTED_TYPES.includes(item.file.type) &&
!item.preprocessing && !item.preprocessing &&
!item.uploading && !item.uploading &&
!item.uploaded !item.uploaded
@ -461,10 +475,25 @@ export function useUploader(options: {
async function preprocess(item: UploaderItem): Promise<void> { async function preprocess(item: UploaderItem): Promise<void> {
item.preprocessing = true; item.preprocessing = true;
let file: Blob | File = item.file; try {
const imageBitmap = await window.createImageBitmap(file); if (IMAGE_PREPROCESS_NEEDED_TYPES.includes(item.file.type)) {
await preprocessForImage(item);
}
} catch (err) {
console.error('Failed to preprocess image', err);
const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(file.type); // nop
}
item.preprocessing = false;
}
async function preprocessForImage(item: UploaderItem): Promise<void> {
const imageBitmap = await window.createImageBitmap(item.file);
let preprocessedFile: Blob | File = item.file;
const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(preprocessedFile.type);
const preset = prefer.s.watermarkPresets.find(p => p.id === item.watermarkPresetId); const preset = prefer.s.watermarkPresets.find(p => p.id === item.watermarkPresetId);
if (needsWatermark && preset != null) { if (needsWatermark && preset != null) {
const canvas = window.document.createElement('canvas'); const canvas = window.document.createElement('canvas');
@ -479,7 +508,7 @@ export function useUploader(options: {
renderer.render(); renderer.render();
file = await new Promise<Blob>((resolve) => { preprocessedFile = await new Promise<Blob>((resolve) => {
canvas.toBlob((blob) => { canvas.toBlob((blob) => {
if (blob == null) { if (blob == null) {
throw new Error('Failed to convert canvas to blob'); throw new Error('Failed to convert canvas to blob');
@ -491,7 +520,7 @@ export function useUploader(options: {
} }
const compressionSettings = getCompressionSettings(item.compressionLevel); const compressionSettings = getCompressionSettings(item.compressionLevel);
const needsCompress = item.compressionLevel !== 0 && compressionSettings && COMPRESSION_SUPPORTED_TYPES.includes(file.type) && !(await isAnimated(file)); const needsCompress = item.compressionLevel !== 0 && compressionSettings && IMAGE_COMPRESSION_SUPPORTED_TYPES.includes(preprocessedFile.type) && !(await isAnimated(preprocessedFile));
if (needsCompress) { if (needsCompress) {
const config = { const config = {
@ -502,13 +531,13 @@ export function useUploader(options: {
}; };
try { try {
const result = await readAndCompressImage(file, config); const result = await readAndCompressImage(preprocessedFile, config);
if (result.size < file.size || file.type === 'image/webp') { if (result.size < preprocessedFile.size || preprocessedFile.type === 'image/webp') {
// The compression may not always reduce the file size // The compression may not always reduce the file size
// (and WebP is not browser safe yet) // (and WebP is not browser safe yet)
file = result; preprocessedFile = result;
item.compressedSize = result.size; item.compressedSize = result.size;
item.uploadName = file.type !== config.mimeType ? `${item.name}.${mimeTypeMap[config.mimeType]}` : item.name; item.uploadName = preprocessedFile.type !== config.mimeType ? `${item.name}.${mimeTypeMap[config.mimeType]}` : item.name;
} }
} catch (err) { } catch (err) {
console.error('Failed to resize image', err); console.error('Failed to resize image', err);
@ -518,17 +547,16 @@ export function useUploader(options: {
item.uploadName = item.name; item.uploadName = item.name;
} }
URL.revokeObjectURL(item.thumbnail);
item.thumbnail = window.URL.createObjectURL(file);
item.preprocessedFile = markRaw(file);
item.preprocessing = false;
imageBitmap.close(); imageBitmap.close();
if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
item.thumbnail = THUMBNAIL_SUPPORTED_TYPES.includes(preprocessedFile.type) ? window.URL.createObjectURL(preprocessedFile) : null;
item.preprocessedFile = markRaw(preprocessedFile);
} }
onUnmounted(() => { onUnmounted(() => {
for (const item of items.value) { for (const item of items.value) {
URL.revokeObjectURL(item.thumbnail); if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
} }
}); });

View File

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2025.6.1-alpha.2", "version": "2025.6.1-alpha.3",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",