Browse Source

Update content utils

Make utils methods consistent.
pull/1/head
TheoryOfNekomata 1 year ago
parent
commit
8391669d6c
12 changed files with 110 additions and 91 deletions
  1. +26
    -0
      packages/audio-utils/src/client.ts
  2. +3
    -0
      packages/audio-utils/src/common.ts
  3. +12
    -34
      packages/audio-utils/src/index.ts
  4. +4
    -9
      packages/image-utils/src/client.ts
  5. +4
    -2
      packages/image-utils/src/index.ts
  6. +3
    -3
      packages/image-utils/src/server.ts
  7. +4
    -4
      packages/text-utils/src/index.ts
  8. +25
    -0
      packages/video-utils/src/client.ts
  9. +5
    -0
      packages/video-utils/src/common.ts
  10. +12
    -32
      packages/video-utils/src/index.ts
  11. +1
    -0
      packages/web-kitchensink-reactnext/src/packages/react-wavesurfer/SpectrogramCanvas/index.tsx
  12. +11
    -7
      packages/web-kitchensink-reactnext/src/utils/blob.ts

+ 26
- 0
packages/audio-utils/src/client.ts View File

@@ -0,0 +1,26 @@
import WaveSurfer from 'wavesurfer.js';
import { AudioMetadata } from './common';

export const getMetadataFromUrl = (audioUrl: string) => new Promise<Partial<AudioMetadata>>((resolve, reject) => {
try {
const dummyContainer = window.document.createElement('div');
// TODO remove wavesurfer dependency
const waveSurferInstance = WaveSurfer.create({
container: dummyContainer,
});

waveSurferInstance.on('ready', () => {
const metadata = {
duration: waveSurferInstance.getDuration(),
};

waveSurferInstance.destroy();
dummyContainer.remove();
resolve(metadata);
});

void waveSurferInstance.load(audioUrl);
} catch (err) {
reject(err);
}
});

+ 3
- 0
packages/audio-utils/src/common.ts View File

@@ -0,0 +1,3 @@
export interface AudioMetadata {
duration?: number;
}

+ 12
- 34
packages/audio-utils/src/index.ts View File

@@ -1,39 +1,17 @@
import WaveSurfer from 'wavesurfer.js';
import {AudioMetadata} from './common';


export interface AudioMetadata {
duration?: number;
}

export const getAudioMetadata = (audioUrl?: string, fileType?: string) => new Promise<AudioMetadata>(async (resolve, reject) => {
// TODO server side
if (fileType === 'audio/mid') {
resolve({});
return;
export const getMetadataFromUrl = async (audioUrl?: string) => {
if (typeof audioUrl === 'undefined') {
return {} as Partial<AudioMetadata>;
} }


try {
const dummyContainer = window.document.createElement('div');
const waveSurferInstance = WaveSurfer.create({
container: dummyContainer,
});

waveSurferInstance.on('ready', async () => {
const metadata = {
duration: waveSurferInstance.getDuration(),
};

waveSurferInstance.destroy();
dummyContainer.remove();
resolve(metadata);
});
if (typeof window !== 'undefined') {
const { getMetadataFromUrl: client } = await import('./client');
return client(audioUrl);
}


if (audioUrl === undefined) {
resolve({});
return;
}
// TODO server side
return {} as Partial<AudioMetadata>;
};


await waveSurferInstance.load(audioUrl);
} catch (err) {
reject(err);
}
});
export * from './common';

+ 4
- 9
packages/image-utils/src/client.ts View File

@@ -1,7 +1,7 @@
import ColorThief from 'colorthief'; import ColorThief from 'colorthief';
import {DEFAULT_PALETTE_COLOR_COUNT, GetImageMetadataOptions, ImageMetadata} from './common'; import {DEFAULT_PALETTE_COLOR_COUNT, GetImageMetadataOptions, ImageMetadata} from './common';


const doGetImageMetadata = async (thisImage: HTMLImageElement, options = {} as GetImageMetadataOptions) => {
const doGetImageMetadata = async (thisImage: HTMLImageElement, options = {} as GetImageMetadataOptions): Promise<Partial<ImageMetadata>> => {
const { paletteColorCount = DEFAULT_PALETTE_COLOR_COUNT } = options; const { paletteColorCount = DEFAULT_PALETTE_COLOR_COUNT } = options;
const colorThief = new ColorThief(); const colorThief = new ColorThief();
const palette = await colorThief.getPalette(thisImage, paletteColorCount); const palette = await colorThief.getPalette(thisImage, paletteColorCount);
@@ -12,9 +12,9 @@ const doGetImageMetadata = async (thisImage: HTMLImageElement, options = {} as G
}; };
}; };


export const getMetadataFromElement = (thisImage: HTMLImageElement, options?: GetImageMetadataOptions): Promise<ImageMetadata> => doGetImageMetadata(thisImage, options);
export const getMetadataFromElement = (thisImage: HTMLImageElement, options?: GetImageMetadataOptions): Promise<Partial<ImageMetadata>> => doGetImageMetadata(thisImage, options);


export const getMetadataFromUrl = (imageUrl: string, options = {} as GetImageMetadataOptions): Promise<ImageMetadata> => new Promise((resolve, reject) => {
export const getMetadataFromUrl = (imageUrl: string, options = {} as GetImageMetadataOptions): Promise<Partial<ImageMetadata>> => new Promise((resolve, reject) => {
const image = new Image(); const image = new Image();


image.addEventListener('load', async (imageLoadEvent) => { image.addEventListener('load', async (imageLoadEvent) => {
@@ -25,14 +25,9 @@ export const getMetadataFromUrl = (imageUrl: string, options = {} as GetImageMet
}); });


image.addEventListener('error', () => { image.addEventListener('error', () => {
reject(new Error('Could not load file as image'));
reject(new Error('Could not load URL as image'));
image.remove(); image.remove();
}); });


if (imageUrl === undefined) {
resolve({});
return;
}

image.src = imageUrl; image.src = imageUrl;
}); });

+ 4
- 2
packages/image-utils/src/index.ts View File

@@ -1,6 +1,8 @@
import {ImageMetadata} from './common';

export const getMetadataFromUrl = async (imageUrl?: string) => { export const getMetadataFromUrl = async (imageUrl?: string) => {
if (imageUrl === undefined) {
return {};
if (typeof imageUrl === 'undefined') {
return {} as Partial<ImageMetadata>;
} }


if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {


+ 3
- 3
packages/image-utils/src/server.ts View File

@@ -1,6 +1,6 @@
import {DEFAULT_PALETTE_COLOR_COUNT, GetImageMetadataOptions, ImageMetadata} from './common'; import {DEFAULT_PALETTE_COLOR_COUNT, GetImageMetadataOptions, ImageMetadata} from './common';


const doGetImageMetadata = async (input: string | Buffer, options = {} as GetImageMetadataOptions) => {
const doGetImageMetadata = async (input: string | Buffer, options = {} as GetImageMetadataOptions): Promise<Partial<ImageMetadata>> => {
const { paletteColorCount = DEFAULT_PALETTE_COLOR_COUNT } = options; const { paletteColorCount = DEFAULT_PALETTE_COLOR_COUNT } = options;
const { imageSize, disableFS } = await import('image-size'); const { imageSize, disableFS } = await import('image-size');
disableFS(true); disableFS(true);
@@ -14,6 +14,6 @@ const doGetImageMetadata = async (input: string | Buffer, options = {} as GetIma
}; };
}; };


export const getMetadataFromUrl = (imageUrl: string, options?: GetImageMetadataOptions): Promise<ImageMetadata> => doGetImageMetadata(imageUrl, options);
export const getMetadataFromUrl = (imageUrl: string, options?: GetImageMetadataOptions): Promise<Partial<ImageMetadata>> => doGetImageMetadata(imageUrl, options);


export const getMetadataFromBuffer = (buffer: Buffer, options?: GetImageMetadataOptions): Promise<ImageMetadata> => doGetImageMetadata(buffer, options);
export const getMetadataFromBuffer = (buffer: Buffer, options?: GetImageMetadataOptions): Promise<Partial<ImageMetadata>> => doGetImageMetadata(buffer, options);

+ 4
- 4
packages/text-utils/src/index.ts View File

@@ -50,7 +50,7 @@ const countLinesOfCode = (lines: string[], scheme: string): number => {
//return lines.filter((line) => !line.trim().startsWith('//')).length; //return lines.filter((line) => !line.trim().startsWith('//')).length;


return lines.filter((line) => line.trim().length > 0).length; return lines.filter((line) => line.trim().length > 0).length;
}
};


export const getTextMetadata = (contents: string, filename?: string): Promise<TextMetadata> => { export const getTextMetadata = (contents: string, filename?: string): Promise<TextMetadata> => {
const lineNormalizedContents = contents.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); const lineNormalizedContents = contents.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
@@ -65,7 +65,7 @@ export const getTextMetadata = (contents: string, filename?: string): Promise<Te
scheme: value.aliasOf, scheme: value.aliasOf,
schemeTitle: value.title, schemeTitle: value.title,
linesOfCode: countLinesOfCode(lines, value.aliasOf), linesOfCode: countLinesOfCode(lines, value.aliasOf),
}
};
} }


return { return {
@@ -81,7 +81,7 @@ export const getTextMetadata = (contents: string, filename?: string): Promise<Te
{ {
contents, contents,
lineCount, lineCount,
} as TextMetadata
} as TextMetadata,
); );


if (typeof metadata.scheme !== 'string') { if (typeof metadata.scheme !== 'string') {
@@ -90,4 +90,4 @@ export const getTextMetadata = (contents: string, filename?: string): Promise<Te
} }


return Promise.resolve(metadata); return Promise.resolve(metadata);
}
};

+ 25
- 0
packages/video-utils/src/client.ts View File

@@ -0,0 +1,25 @@
import {VideoMetadata} from './common';

export const getMetadataFromUrl = async (videoUrl: string) => new Promise<Partial<VideoMetadata>>((resolve, reject) => {
const video = window.document.createElement('video');
const source = window.document.createElement('source');

video.addEventListener('loadedmetadata', (videoLoadEvent) => {
const thisVideo = videoLoadEvent.currentTarget as HTMLVideoElement;
const metadata = {
width: thisVideo.videoWidth,
height: thisVideo.videoHeight,
duration: thisVideo.duration,
};
video.remove();
resolve(metadata);
});

video.addEventListener('error', () => {
reject(new Error('Could not load file as video'));
video.remove();
});

source.src = videoUrl;
video.appendChild(source);
});

+ 5
- 0
packages/video-utils/src/common.ts View File

@@ -0,0 +1,5 @@
export interface VideoMetadata {
width?: number;
height?: number;
duration?: number;
}

+ 12
- 32
packages/video-utils/src/index.ts View File

@@ -1,35 +1,15 @@
export interface VideoFileMetadata {
width?: number;
height?: number;
duration?: number;
}

export const getVideoMetadata = (videoUrl?: string) => new Promise<VideoFileMetadata>((resolve, reject) => {
// TODO server side
const video = window.document.createElement('video');
const source = window.document.createElement('source');

video.addEventListener('loadedmetadata', (videoLoadEvent) => {
const thisVideo = videoLoadEvent.currentTarget as HTMLVideoElement;
const metadata = {
width: thisVideo.videoWidth,
height: thisVideo.videoHeight,
duration: thisVideo.duration,
};
video.remove();
resolve(metadata);
});

video.addEventListener('error', () => {
reject(new Error('Could not load file as video'));
video.remove();
});

export const getMetadataFromUrl = async (videoUrl?: string) => {
if (typeof videoUrl === 'undefined') { if (typeof videoUrl === 'undefined') {
resolve({});
return;
return {};
} }


source.src = videoUrl;
video.appendChild(source);
});
if (typeof window !== 'undefined') {
const { getMetadataFromUrl: client } = await import('./client');
return client(videoUrl);
}

// TODO server side
return {};
};

export * from './common';

+ 1
- 0
packages/web-kitchensink-reactnext/src/packages/react-wavesurfer/SpectrogramCanvas/index.tsx View File

@@ -16,6 +16,7 @@ export const SpectrogramCanvas = React.forwardRef<SpectrogramCanvasDerivedElemen
className, className,
children, children,
controls, controls,
// TODO organize props for color
waveColor, waveColor,
progressColor, progressColor,
cursorColor, cursorColor,


+ 11
- 7
packages/web-kitchensink-reactnext/src/utils/blob.ts View File

@@ -1,8 +1,8 @@
import * as mimeTypes from 'mime-types'; import * as mimeTypes from 'mime-types';
import {getTextMetadata, TextMetadata} from '@modal-soft/text-utils'; import {getTextMetadata, TextMetadata} from '@modal-soft/text-utils';
import {getMetadataFromUrl, ImageMetadata} from '@modal-soft/image-utils';
import {getAudioMetadata, AudioMetadata} from '@modal-soft/audio-utils';
import {getVideoMetadata, VideoFileMetadata} from '@modal-soft/video-utils';
import {getMetadataFromUrl as getImageMetadataFromUrl, ImageMetadata} from '@modal-soft/image-utils';
import {getMetadataFromUrl as getAudioMetadataFromUrl, AudioMetadata} from '@modal-soft/audio-utils';
import {getMetadataFromUrl as getVideoMetadataFromUrl, VideoMetadata} from '@modal-soft/video-utils';


const MIME_TYPE_DESCRIPTIONS = { const MIME_TYPE_DESCRIPTIONS = {
'image/gif': 'GIF Image', 'image/gif': 'GIF Image',
@@ -164,7 +164,7 @@ export interface ImageFile extends FileWithResolvedContentType {


export const augmentImageFile = async <T extends Partial<FileWithDataUrl>>(file: T): Promise<ImageFile> => { export const augmentImageFile = async <T extends Partial<FileWithDataUrl>>(file: T): Promise<ImageFile> => {
const fileMutable = file as unknown as Record<string, ImageMetadata>; const fileMutable = file as unknown as Record<string, ImageMetadata>;
fileMutable.metadata = await getMetadataFromUrl(file.url);
fileMutable.metadata = await getImageMetadataFromUrl(file.url);
return fileMutable as unknown as ImageFile; return fileMutable as unknown as ImageFile;
}; };


@@ -175,7 +175,11 @@ export interface AudioFile extends FileWithResolvedContentType {


export const augmentAudioFile = async <T extends Partial<FileWithDataUrl>>(file: T): Promise<AudioFile> => { export const augmentAudioFile = async <T extends Partial<FileWithDataUrl>>(file: T): Promise<AudioFile> => {
const fileMutable = file as unknown as Record<string, AudioMetadata>; const fileMutable = file as unknown as Record<string, AudioMetadata>;
fileMutable.metadata = await getAudioMetadata(file.url, file.type);
if (file.type === 'audio/mid') {
fileMutable.metadata = {};
} else {
fileMutable.metadata = await getAudioMetadataFromUrl(file.url);
}
return fileMutable as unknown as AudioFile; return fileMutable as unknown as AudioFile;
}; };


@@ -200,11 +204,11 @@ export const augmentBinaryFile = async <T extends Partial<FileWithDataUrl>>(file


export interface VideoFile extends FileWithResolvedContentType { export interface VideoFile extends FileWithResolvedContentType {
resolvedType: ContentType.VIDEO; resolvedType: ContentType.VIDEO;
metadata?: VideoFileMetadata;
metadata?: VideoMetadata;
} }


export const augmentVideoFile = async <T extends Partial<FileWithDataUrl>>(file: T): Promise<VideoFile> => { export const augmentVideoFile = async <T extends Partial<FileWithDataUrl>>(file: T): Promise<VideoFile> => {
const fileMutable = file as unknown as Record<string, AudioMetadata>; const fileMutable = file as unknown as Record<string, AudioMetadata>;
fileMutable.metadata = await getVideoMetadata(file.url);
fileMutable.metadata = await getVideoMetadataFromUrl(file.url);
return fileMutable as unknown as VideoFile; return fileMutable as unknown as VideoFile;
}; };

Loading…
Cancel
Save