FanAPI: fetch by logged-in user if target empty

This commit is contained in:
patrickkfkan 2023-10-29 01:30:55 +08:00
parent 7b537aade1
commit 7815c68ea8
2 changed files with 58 additions and 8 deletions

View File

@ -17,12 +17,12 @@ import BaseAPIWithImageSupport, { BaseAPIWithImageSupportParams } from '../commo
export { FanPageItemsResult, FanContinuationItemsResult };
export interface FanAPIGetInfoParams {
username: string;
username?: string;
imageFormat: string | number | ImageFormat;
}
export interface FanAPIGetItemsParams {
target: string | FanItemsContinuation;
target?: string | FanItemsContinuation;
imageFormat?: string | number | ImageFormat;
}
@ -39,6 +39,13 @@ export interface FanAPIGetItemsFullParams<T> extends FanAPIGetItemsParams {
export default class FanAPI extends BaseAPIWithImageSupport {
async getInfo(params: FanAPIGetInfoParams): Promise<Fan> {
if (!params.username) {
const username = await this.getLoggedInFanUsername();
return this.getInfo({
...params,
username
});
}
const imageConstants = await this.imageAPI.getConstants();
const fanPageUrl = FanAPI.getFanPageUrl(params.username);
const opts = {
@ -94,6 +101,15 @@ export default class FanAPI extends BaseAPIWithImageSupport {
*/
protected async getItems<T>(params: FanAPIGetItemsFullParams<T>): Promise<FanPageItemsResult<T> | FanContinuationItemsResult<T>> {
const { target, imageFormat, defaultImageFormat, continuationUrl } = params;
if (!target) {
const username = await this.getLoggedInFanUsername();
return this.getItems({
...params,
target: username
});
}
const imageConstants = await this.imageAPI.getConstants();
const opts = {
imageBaseUrl: imageConstants.baseUrl,
@ -101,7 +117,7 @@ export default class FanAPI extends BaseAPIWithImageSupport {
};
if (!FanAPI.isContinuation(target)) {
const fanPageUrl = FanAPI.getFanPageUrl(target as string);
const fanPageUrl = FanAPI.getFanPageUrl(target);
const html = await this.fetch(fanPageUrl);
return params.parsePageFn(html, opts);
}
@ -111,14 +127,13 @@ export default class FanAPI extends BaseAPIWithImageSupport {
throw new FetchError('Unable to fetch fan contents: target is continuation token but continuation URL is missing.');
}
const continuation = target as FanItemsContinuation;
const payload = {
fan_id: continuation.fanId,
older_than_token: continuation.token,
fan_id: target.fanId,
older_than_token: target.token,
count: 20
};
const json = await this.fetch(continuationUrl, true, FetchMethod.POST, payload);
return params.parseContinuationFn(json, continuation, opts);
return params.parseContinuationFn(json, target, opts);
}
/**
@ -131,9 +146,17 @@ export default class FanAPI extends BaseAPIWithImageSupport {
/**
* @internal
*/
protected static isContinuation(target: any) {
protected static isContinuation(target: any): target is FanItemsContinuation {
return typeof target === 'object' && target.fanId && target.token;
}
/**
* @internal
*/
protected async getLoggedInFanUsername() {
const html = await this.fetch(URLS.SITE_URL);
return FanInfoParser.parseLoggedInFanUsername(html);
}
}
export class LimiterFanAPI extends FanAPI {

View File

@ -49,4 +49,31 @@ export default class FanInfoParser {
return result;
}
static parseLoggedInFanUsername(html: string) {
const $ = cheerioLoad(html);
const blob = decode($('#pagedata[data-blob]').attr('data-blob'));
let parsed;
try {
parsed = JSON.parse(blob);
}
catch (error: any) {
throw new ParseError('Failed to parse logged-in fan username: JSON error in data-blob.', html, error);
}
const identitiesData = parsed.identities || {};
const username = identitiesData.fan?.username;
if (!username || typeof username !== 'string') {
let reason;
if (identitiesData.fan === null) {
reason = 'check if valid cookie is set';
}
else {
reason = 'invalid data';
}
throw new ParseError(`Failed to parse logged-in fan username: ${reason}.`, html);
}
return username;
}
}