83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import bytes from 'bytes';
|
|
import * as cheerio from 'cheerio';
|
|
import { Context, Format, Meta, UrlResult } from '../types';
|
|
import { Extractor } from './Extractor';
|
|
|
|
export class HubCloud extends Extractor {
|
|
public readonly id = 'hubcloud';
|
|
|
|
public readonly label = 'HubCloud';
|
|
|
|
public override readonly ttl: number = 259200000; // 3d
|
|
|
|
public supports(_ctx: Context, url: URL): boolean {
|
|
return null !== url.host.match(/hubcloud/);
|
|
}
|
|
|
|
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
|
|
const headers = { Referer: meta.referer ?? url.href };
|
|
|
|
const redirectHtml = await this.fetcher.text(ctx, url, { headers });
|
|
const redirectUrlMatch = redirectHtml.match(/var url ?= ?'(.*?)'/) as string[];
|
|
|
|
const linksHtml = await this.fetcher.text(ctx, new URL(redirectUrlMatch[1] as string), { headers: { Referer: url.href } });
|
|
const $ = cheerio.load(linksHtml);
|
|
|
|
const urlResults = [
|
|
...$('a')
|
|
.filter((_i, el) => {
|
|
const text = $(el).text();
|
|
|
|
return text.includes('FSL') || text.includes('Download File');
|
|
})
|
|
.map((_i, el) => {
|
|
const url = new URL($(el).attr('href') as string);
|
|
return {
|
|
url,
|
|
format: Format.unknown,
|
|
label: `${this.label} (FSL)`,
|
|
sourceId: `${this.id}_fsl`,
|
|
ttl: this.ttl,
|
|
meta: {
|
|
...meta,
|
|
bytes: bytes.parse($('#size').text()) as number,
|
|
title: $('title').text().trim(),
|
|
},
|
|
};
|
|
}).toArray(),
|
|
...$('a')
|
|
.filter((_i, el) => $(el).text().includes('PixelServer'))
|
|
.map((_i, el) => {
|
|
const url = new URL(($(el).attr('href') as string).replace('/u/', '/api/file/'));
|
|
return {
|
|
url,
|
|
format: Format.unknown,
|
|
label: `${this.label} (PixelServer)`,
|
|
sourceId: `${this.id}_pixelserver`,
|
|
ttl: this.ttl,
|
|
meta: {
|
|
...meta,
|
|
bytes: bytes.parse($('#size').text()) as number,
|
|
title: $('title').text().trim(),
|
|
},
|
|
};
|
|
}).toArray(),
|
|
];
|
|
|
|
return this.asyncFilter(urlResults, async ({ url }) => {
|
|
try {
|
|
await this.fetcher.head(ctx, url);
|
|
} catch {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
};
|
|
|
|
private async asyncFilter<T>(array: T[], asyncCallback: (item: T) => Promise<boolean>): Promise<T[]> {
|
|
const results = await Promise.all(array.map(asyncCallback));
|
|
|
|
return array.filter((_, index) => results[index]);
|
|
}
|
|
}
|