Add Stream API
This commit is contained in:
parent
e6a01cbe85
commit
7bb1899029
26
examples/stream/testAndRefresh.ts
Normal file
26
examples/stream/testAndRefresh.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import bcfetch from '../../';
|
||||
|
||||
const streamUrl = 'https://t4.bcbits.com/stream/a9fab205c6d82ffc4ca4f377d86bd8dd/mp3-128/3535427082?p=0&ts=1698526235&t=3c987d3b75dc4151c931288780eca682ae1d70ac&token=1698526235_35d7ab2f75c60cdb31c68df15cc5d5024368a535';
|
||||
|
||||
console.log(`Testing stream URL: ${streamUrl}`);
|
||||
|
||||
bcfetch.stream.test(streamUrl).then((result1) => {
|
||||
if (result1.ok) {
|
||||
console.log('Stream URL is valid.');
|
||||
return;
|
||||
}
|
||||
console.log(`Stream URL is invalid. Status code: ${result1.status}. Refreshing...`);
|
||||
bcfetch.stream.refresh(streamUrl).then((result2) => {
|
||||
console.log(`Refreshed stream URL: ${result2}`);
|
||||
if (result2) {
|
||||
console.log('Testing refreshed stream URL...');
|
||||
bcfetch.stream.test(result2).then((result3) => {
|
||||
if (result3.ok) {
|
||||
console.log('Refreshed stream URL is valid.');
|
||||
return;
|
||||
}
|
||||
console.log(`Refreshed stream URL is invalid. Status code: ${result3.status}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
5
examples/stream/testAndRefresh_output.txt
Normal file
5
examples/stream/testAndRefresh_output.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
Testing stream URL: https://t4.bcbits.com/stream/a9fab205c6d82ffc4ca4f377d86bd8dd/mp3-128/3535427082?p=0&ts=1698526235&t=3c987d3b75dc4151c931288780eca682ae1d70ac&token=1698526235_35d7ab2f75c60cdb31c68df15cc5d5024368a535
|
||||
Stream URL is invalid. Status code: 410. Refreshing...
|
||||
Refreshed stream URL: https://t4.bcbits.com/stream/a9fab205c6d82ffc4ca4f377d86bd8dd/mp3-128/3535427082?p=0&ts=1698685897&t=933f95e7071852f98c44ce1876d4a68a29cc229a&token=1698685897_e04500be84f7e94b43baed9170b689cfda72d565
|
||||
Testing refreshed stream URL...
|
||||
Refreshed stream URL is valid.
|
|
@ -13,6 +13,7 @@ export { default as TagAPI } from './lib/tag/TagAPI.js';
|
|||
export { default as TrackAPI } from './lib/track/TrackAPI.js';
|
||||
export { default as SearchAPI } from './lib/search/SearchAPI.js';
|
||||
export { default as AutocompleteAPI } from './lib/autocomplete/AutocompleteAPI.js';
|
||||
export { default as StreamAPI } from './lib/stream/StreamAPI.js';
|
||||
|
||||
export * from './lib/common/BaseAPI.js';
|
||||
export * from './lib/common/BaseAPIWithImageSupport.js';
|
||||
|
@ -28,6 +29,7 @@ export * from './lib/track/TrackAPI.js';
|
|||
export * from './lib/album/AlbumAPI.js';
|
||||
export * from './lib/search/SearchAPI.js';
|
||||
export * from './lib/autocomplete/AutocompleteAPI.js';
|
||||
export * from './lib/stream/StreamAPI.js';
|
||||
|
||||
export * from './lib/types/Album.js';
|
||||
export * from './lib/types/Article.js';
|
||||
|
|
|
@ -13,6 +13,7 @@ import TrackAPI, { LimiterTrackAPI } from './track/TrackAPI.js';
|
|||
import Cache, { CacheDataType } from './utils/Cache.js';
|
||||
import Fetcher from './utils/Fetcher.js';
|
||||
import Limiter from './utils/Limiter.js';
|
||||
import StreamAPI, { LimiterStreamAPI } from './stream/StreamAPI.js';
|
||||
|
||||
export interface BandcampFetchParams {
|
||||
cookie?: string | null;
|
||||
|
@ -37,6 +38,7 @@ export default class BandcampFetch {
|
|||
readonly fan: FanAPI;
|
||||
readonly search: SearchAPI;
|
||||
readonly autocomplete: AutocompleteAPI;
|
||||
readonly stream: StreamAPI;
|
||||
|
||||
readonly limiter: {
|
||||
readonly album: LimiterAlbumAPI;
|
||||
|
@ -50,6 +52,7 @@ export default class BandcampFetch {
|
|||
readonly fan: LimiterFanAPI;
|
||||
readonly search: LimiterSearchAPI;
|
||||
readonly autocomplete: LimiterAutocompleteAPI;
|
||||
readonly stream: StreamAPI;
|
||||
updateSettings: (options?: Bottleneck.ConstructorOptions) => void;
|
||||
};
|
||||
|
||||
|
@ -89,6 +92,7 @@ export default class BandcampFetch {
|
|||
this.fan = new FanAPI(baseAPIWithImageSupportParams);
|
||||
this.search = new SearchAPI(baseAPIWithImageSupportParams);
|
||||
this.autocomplete = new AutocompleteAPI(baseAPIParams);
|
||||
this.stream = new StreamAPI(baseAPIParams);
|
||||
|
||||
this.limiter = {
|
||||
album: new LimiterAlbumAPI(baseAPIWithImageSupportParams),
|
||||
|
@ -102,6 +106,7 @@ export default class BandcampFetch {
|
|||
fan: new LimiterFanAPI(baseAPIWithImageSupportParams),
|
||||
search: new LimiterSearchAPI(baseAPIWithImageSupportParams),
|
||||
autocomplete: new LimiterAutocompleteAPI(baseAPIParams),
|
||||
stream: new LimiterStreamAPI(baseAPIParams),
|
||||
updateSettings: this.#limiter.updateSettings.bind(this.#limiter)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Response } from 'node-fetch';
|
||||
import Cache from '../utils/Cache.js';
|
||||
import Fetcher, { FetchMethod } from '../utils/Fetcher.js';
|
||||
|
||||
|
@ -16,9 +17,10 @@ export default abstract class BaseAPI {
|
|||
this.#cache = params.cache;
|
||||
}
|
||||
|
||||
protected fetch(url: string, jsonResponse: false, method: FetchMethod.HEAD, payload?: undefined): Promise<Response>;
|
||||
protected fetch(url: string, jsonResponse: true, method?: FetchMethod, payload?: Record<string, any>): Promise<any>;
|
||||
protected fetch(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record<string, any>): Promise<string>;
|
||||
protected fetch(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record<string, any>) {
|
||||
protected fetch(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record<string, any>): Promise<any> {
|
||||
return this.#fetcher.fetch(url, jsonResponse, method, payload);
|
||||
}
|
||||
|
||||
|
|
44
src/lib/stream/StreamAPI.ts
Normal file
44
src/lib/stream/StreamAPI.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import BaseAPI, { BaseAPIParams } from '../common/BaseAPI.js';
|
||||
import { URLS } from '../utils/Constants.js';
|
||||
import { FetchMethod } from '../utils/Fetcher.js';
|
||||
import Limiter from '../utils/Limiter.js';
|
||||
|
||||
export interface StreamTestResult {
|
||||
ok: boolean;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export default class StreamAPI extends BaseAPI {
|
||||
|
||||
async test(url: string): Promise<StreamTestResult> {
|
||||
const res = await this.fetch(url, false, FetchMethod.HEAD);
|
||||
return {
|
||||
ok: res.ok,
|
||||
status: res.status
|
||||
};
|
||||
}
|
||||
|
||||
async refresh(url: string): Promise<string | null> {
|
||||
const refreshUrl = new URL(URLS.REFRESH_STREAM);
|
||||
refreshUrl.searchParams.set('url', url);
|
||||
const res = await this.fetch(refreshUrl.toString(), true);
|
||||
if (res && res.url && typeof res.url === 'string') {
|
||||
return res.url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class LimiterStreamAPI extends StreamAPI {
|
||||
|
||||
#limiter: Limiter;
|
||||
|
||||
constructor(params: BaseAPIParams & { limiter: Limiter }) {
|
||||
super(params);
|
||||
this.#limiter = params.limiter;
|
||||
}
|
||||
|
||||
async refresh(url: string): Promise<string | null> {
|
||||
return this.#limiter.schedule(() => super.refresh(url));
|
||||
}
|
||||
}
|
|
@ -17,5 +17,6 @@ export const URLS = {
|
|||
AUTOCOMPLETE: {
|
||||
TAG: `${API_URL}/fansignup/1/search_tag`,
|
||||
LOCATION: `${API_URL}/location/1/geoname_search`
|
||||
}
|
||||
},
|
||||
REFRESH_STREAM: `${API_URL}/stream/1/refresh`
|
||||
};
|
||||
|
|
|
@ -4,7 +4,8 @@ import Cache, { CacheDataType } from './Cache.js';
|
|||
|
||||
export enum FetchMethod {
|
||||
GET = 'GET',
|
||||
POST = 'POST'
|
||||
POST = 'POST',
|
||||
HEAD = 'HEAD'
|
||||
}
|
||||
|
||||
export interface FetcherParams {
|
||||
|
@ -34,6 +35,7 @@ export default class Fetcher {
|
|||
return this.#cookie;
|
||||
}
|
||||
|
||||
fetch(url: string, jsonResponse: false, method: FetchMethod.HEAD, payload?: undefined): Promise<Response>;
|
||||
fetch(url: string, jsonResponse: true, method?: FetchMethod, payload?: Record<string, any>): Promise<any>;
|
||||
fetch(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record<string, any>): Promise<string>;
|
||||
fetch(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record<string, any>) {
|
||||
|
@ -44,6 +46,11 @@ export default class Fetcher {
|
|||
if (method === undefined) {
|
||||
method = FetchMethod.GET;
|
||||
}
|
||||
|
||||
if (method === FetchMethod.HEAD) {
|
||||
return fetch(url, { method: 'HEAD' });
|
||||
}
|
||||
|
||||
let response;
|
||||
if (method === FetchMethod.GET) {
|
||||
const urlObj = new URL(url);
|
||||
|
|
Loading…
Reference in New Issue
Block a user