FileUpload: PixelDrain support and BypassDiscordUpload fix (#1041)

* [+] PixelDrain

* [F] Fix BypassDiscordUpload key mismatch
This commit is contained in:
Azalea 2026-05-01 15:17:18 -04:00 • committed by GitHub
parent 2fda567da1
commit 77cf82dc6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 107 additions and 4 deletions

View file

@ -74,7 +74,7 @@ function interceptUploadAddFiles(event: unknown): void {
if (payload.draftType !== DraftType.ChannelMessage) return;
if (!Boolean((settings.store as { interceptDiscordUpload?: boolean; }).interceptDiscordUpload) || !isConfigured()) return;
if (!settings.store.bypassDiscordUpload || !isConfigured()) return;
const files = [
...extractFilesFromValue(payload.files),
@ -283,7 +283,7 @@ export default definePlugin({
}
},
shouldBypassDiscordUploadSizeCheck(): boolean {
return Boolean((settings.store as { interceptDiscordUpload?: boolean; }).interceptDiscordUpload) && isConfigured();
return Boolean(settings.store.bypassDiscordUpload) && isConfigured();
},
renderUploadProgress() {
return <ProgressBar />;

View file

@ -418,6 +418,46 @@ export async function uploadToPixelVault(
}
}
export async function uploadToPixelDrain(
_: IpcMainInvokeEvent,
fileBuffer: ArrayBuffer,
filename: string,
apiKey?: string
): Promise<NativeUploadResult> {
try {
const headers: Record<string, string> = {};
if (apiKey?.trim()) {
headers.Authorization = `Basic ${Buffer.from(`:${apiKey.trim()}`).toString("base64")}`;
}
const response = await fetch(`https://pixeldrain.com/api/file/${encodeURIComponent(filename)}`, {
method: "PUT",
headers,
body: new Blob([fileBuffer])
});
const text = await response.text();
let data: { id?: string; message?: string; } | null = null;
try {
data = text ? JSON.parse(text) : null;
} catch {
data = null;
}
if (!response.ok) {
return { success: false, error: data?.message || `Upload failed: ${response.status} ${text}` };
}
if (!data?.id) {
return { success: false, error: data?.message || "No URL returned from upload" };
}
return { success: true, url: `https://pixeldrain.com/u/${data.id}` };
} catch (e) {
return { success: false, error: e instanceof Error ? e.message : "Unknown error" };
}
}
export async function fetchFile(
_: IpcMainInvokeEvent,

View file

@ -34,6 +34,7 @@ const serviceOptions = [
{ label: "temp.sh", value: ServiceType.TEMPSH },
{ label: "filebin.net", value: ServiceType.FILEBIN },
{ label: "PixelVault", value: ServiceType.PIXELVAULT },
{ label: "PixelDrain", value: ServiceType.PIXELDRAIN },
{ label: "ShareX Custom Uploader", value: ServiceType.SHAREX }
];
@ -207,6 +208,12 @@ export const settings = definePluginSettings({
default: "",
hidden: true
},
pixelDrainKey: {
type: OptionType.STRING,
description: "Optional PixelDrain API key",
default: "",
hidden: true
},
uploadTimeoutMs: {
type: OptionType.NUMBER,
description: "Upload timeout in milliseconds",
@ -366,6 +373,7 @@ export function SettingsComponent() {
const isLitterbox = store.serviceType === ServiceType.LITTERBOX;
const isGofile = store.serviceType === ServiceType.GOFILE;
const isPixelVault = store.serviceType === ServiceType.PIXELVAULT;
const isPixelDrain = store.serviceType === ServiceType.PIXELDRAIN;
const isShareX = store.serviceType === ServiceType.SHAREX;
const validateShareXConfig = () => {
@ -576,6 +584,16 @@ export function SettingsComponent() {
/>
)}
{isPixelDrain && (
<SettingTextInput
name="PixelDrain API Key"
description="Optional PixelDrain API key for authenticated uploads. Leave empty for anonymous uploads."
value={store.pixelDrainKey}
onChange={v => store.pixelDrainKey = v}
placeholder="Your PixelDrain API key"
/>
)}
{isShareX && (
<>
<SettingsSection

View file

@ -18,7 +18,8 @@ export enum ServiceType {
BUZZHEAVIER = "buzzheavier",
TEMPSH = "tempsh",
FILEBIN = "filebin",
PIXELVAULT = "pixelvault"
PIXELVAULT = "pixelvault",
PIXELDRAIN = "pixeldrain"
}
export const serviceLabels: Record<ServiceType, string> = {
@ -35,7 +36,8 @@ export const serviceLabels: Record<ServiceType, string> = {
[ServiceType.BUZZHEAVIER]: "buzzheavier.com",
[ServiceType.TEMPSH]: "temp.sh",
[ServiceType.FILEBIN]: "filebin.net",
[ServiceType.PIXELVAULT]: "PixelVault"
[ServiceType.PIXELVAULT]: "PixelVault",
[ServiceType.PIXELDRAIN]: "PixelDrain"
};
export const fallbackServiceOrder: ServiceType[] = [
@ -52,6 +54,7 @@ export const fallbackServiceOrder: ServiceType[] = [
ServiceType.TEMPSH,
ServiceType.FILEBIN,
ServiceType.PIXELVAULT,
ServiceType.PIXELDRAIN,
ServiceType.SHAREX
];

View file

@ -374,6 +374,7 @@ export function isConfigured(): boolean {
case ServiceType.BUZZHEAVIER:
case ServiceType.TEMPSH:
case ServiceType.FILEBIN:
case ServiceType.PIXELDRAIN:
return true;
case ServiceType.PIXELVAULT:
return Boolean((settings.store as { pixelVaultKey?: string; }).pixelVaultKey);
@ -714,6 +715,45 @@ async function uploadToPixelVault(fileBlob: Blob, filename: string): Promise<str
return url;
}
async function uploadToPixelDrain(fileBlob: Blob, filename: string): Promise<string> {
const { pixelDrainKey } = settings.store as { pixelDrainKey?: string; };
if (Native) {
const result = await Native.uploadToPixelDrain(await fileBlob.arrayBuffer(), filename, pixelDrainKey?.trim() || undefined);
if (!result.success || !result.url) throw new Error(result.error || "No URL returned from upload");
return result.url;
}
const headers: Record<string, string> = {};
if (pixelDrainKey?.trim()) {
headers.Authorization = `Basic ${btoa(`:${pixelDrainKey.trim()}`)}`;
}
const response = await fetchWithTimeout(`https://pixeldrain.com/api/file/${encodeURIComponent(filename)}`, {
method: "PUT",
headers,
body: fileBlob
});
const text = await response.text();
let data: { id?: string; success?: boolean; message?: string; } | null = null;
try {
data = text ? JSON.parse(text) : null;
} catch {
data = null;
}
if (!response.ok) {
throw new Error(data?.message || `Upload failed: ${response.status} ${text}`);
}
if (!data?.id) {
throw new Error(data?.message || "No URL returned from upload");
}
return `https://pixeldrain.com/u/${data.id}`;
}
async function uploadToService(serviceType: ServiceType, fileBlob: Blob, filename: string): Promise<string> {
switch (serviceType) {
case ServiceType.ZIPLINE:
@ -744,6 +784,8 @@ async function uploadToService(serviceType: ServiceType, fileBlob: Blob, filenam
return uploadToFilebin(fileBlob, filename);
case ServiceType.PIXELVAULT:
return uploadToPixelVault(fileBlob, filename);
case ServiceType.PIXELDRAIN:
return uploadToPixelDrain(fileBlob, filename);
default:
throw new Error("Unknown service type");
}