feat(media hosting): add imgbb provider

This commit is contained in:
Tommaso Casaburi
2026-05-01 22:52:54 +07:00
parent e2154b3edf
commit 78e5d58d08
10 changed files with 136 additions and 12 deletions
@@ -5,6 +5,7 @@ describe('direct-url', () => {
describe('isDirectMediaUrl', () => {
it('returns true for image extensions', () => {
expect(isDirectMediaUrl('https://example.com/photo.jpg')).toBe(true);
expect(isDirectMediaUrl('https://i.ibb.co/example/photo.jpg')).toBe(true);
expect(isDirectMediaUrl('https://example.com/photo.jpeg')).toBe(true);
expect(isDirectMediaUrl('https://example.com/photo.png')).toBe(true);
expect(isDirectMediaUrl('https://example.com/photo.gif')).toBe(true);
@@ -1,6 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { orchestrateElectronUpload } from '../upload-orchestrator';
import { uploadToCatbox } from '../../utils/catbox-utils';
import type { ProviderId } from '../types';
vi.mock('../../utils/catbox-utils', () => ({
uploadToCatbox: vi.fn(),
@@ -11,7 +12,7 @@ function createElectronApiMock() {
isElectron: true,
copyToClipboard: vi.fn(async () => ({ success: true })),
getPlatform: vi.fn(async () => ({ platform: 'darwin' as NodeJS.Platform, arch: 'x64', version: 'v20.0.0' })),
automateUploadMedia: vi.fn(async () => ({ url: 'https://i.imgur.com/abc.png', provider: 'imgur' as const })),
automateUploadMedia: vi.fn(async (options: { provider: ProviderId }) => ({ url: 'https://i.imgur.com/abc.png', provider: options.provider })),
getPathForFile: vi.fn((): string | null => '/tmp/image.png'),
};
}
@@ -48,6 +49,21 @@ describe('orchestrateElectronUpload', () => {
});
});
it('routes ImgBB through Electron automation', async () => {
const electronApi = createElectronApiMock();
electronApi.automateUploadMedia = vi.fn(async () => ({ url: 'https://i.ibb.co/example/image.png', provider: 'imgbb' as const }));
window.electronApi = electronApi;
const file = new File(['x'], 'x.png', { type: 'image/png' });
const url = await orchestrateElectronUpload(file, ['imgbb']);
expect(url).toBe('https://i.ibb.co/example/image.png');
expect(electronApi.automateUploadMedia).toHaveBeenCalledWith({
provider: 'imgbb',
filePath: '/tmp/image.png',
});
});
it('fails with provider attempt details if no file path can be resolved', async () => {
const electronApi = createElectronApiMock();
electronApi.getPathForFile = vi.fn((): string | null => null);
+11
View File
@@ -4,6 +4,8 @@ interface ProviderDefinition {
id: ProviderId;
label: string;
homepageUrl: string;
/** Image URLs used to detect whether this provider is reachable from the user's network. */
availabilityProbeUrls: readonly string[];
/** Runtimes where automated upload is supported (non-web = no interactive fallback) */
supportedRuntimes: readonly MediaHostingRuntime[];
}
@@ -14,12 +16,21 @@ export const MEDIA_HOSTING_PROVIDERS: readonly ProviderDefinition[] = [
id: 'catbox',
label: 'Catbox',
homepageUrl: 'https://catbox.moe',
availabilityProbeUrls: ['https://catbox.moe/pictures/logo.png', 'https://files.catbox.moe/8ten4y.png'],
supportedRuntimes: ['web', 'electron', 'android'],
},
{
id: 'imgur',
label: 'Imgur',
homepageUrl: 'https://imgur.com',
availabilityProbeUrls: ['https://s.imgur.com/images/favicon-32x32.png', 'https://i.imgur.com/YpB7qfa.jpg'],
supportedRuntimes: ['electron'],
},
{
id: 'imgbb',
label: 'ImgBB',
homepageUrl: 'https://imgbb.com',
availabilityProbeUrls: ['https://simgbb.com/images/logo.png', 'https://i.ibb.co/7Jsq00V5/spoiler.png'],
supportedRuntimes: ['electron', 'android'],
},
] as const;
+1 -1
View File
@@ -1,5 +1,5 @@
/** Supported media hosting provider identifiers */
export type ProviderId = 'catbox' | 'imgur';
export type ProviderId = 'catbox' | 'imgur' | 'imgbb';
/** User-facing upload mode */
export type UploadMode = 'random' | 'preferred' | 'none';
+2 -2
View File
@@ -58,11 +58,11 @@ function resolveElectronFilePath(file: File): string | null {
/**
* Uploads a file via a single provider. Catbox uses the web API;
* imgur uses Electron automation when available.
* imgur/imgbb use Electron automation when available.
*/
async function uploadViaProvider(provider: ProviderId, file: File): Promise<string> {
if (provider === 'catbox') return uploadToCatbox(file);
if (provider === 'imgur') {
if (provider === 'imgur' || provider === 'imgbb') {
const fn = typeof window !== 'undefined' && window.electronApi?.automateUploadMedia;
if (fn) {
const filePath = resolveElectronFilePath(file);