-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Skick <[email protected]>
- Loading branch information
1 parent
4eebfc4
commit c2a4ef5
Showing
12 changed files
with
905 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<div align="center"> | ||
<p> | ||
<a href="https://nodei.co/npm/@distube/bandlab"><img src="https://nodei.co/npm/@distube/bandlab.png?downloads=true&downloadRank=true&stars=true"></a> | ||
</p> | ||
<p> | ||
<img alt="npm peer dependency version" src="https://img.shields.io/npm/dependency-version/@distube/bandlab/peer/distube?style=flat-square"> | ||
<img alt="npm" src="https://img.shields.io/npm/dt/@distube/bandlab?logo=npm&style=flat-square"> | ||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/distubejs/extractor-plugins?logo=github&logoColor=white&style=flat-square"> | ||
<a href="https://discord.gg/feaDd9h"><img alt="Discord" src="https://img.shields.io/discord/732254550689316914?logo=discord&logoColor=white&style=flat-square"></a> | ||
</p> | ||
<p> | ||
<a href='https://ko-fi.com/skick' target='_blank'><img height='48' src='https://storage.ko-fi.com/cdn/kofi3.png' alt='Buy Me a Coffee at ko-fi.com' /></a> | ||
</p> | ||
</div> | ||
|
||
# @distube/bandlab | ||
|
||
A DisTube playable extractor plugin for supporting Bandlab. | ||
|
||
[_What is a playable extractor plugin?_](https://github.com/skick1234/DisTube/wiki/Projects-Hub#plugins) | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install @distube/bandlab@latest | ||
``` | ||
|
||
## Usage | ||
|
||
```ts | ||
import { DisTube } from "distube"; | ||
import { BandlabPlugin } from "@distube/bandlab"; | ||
|
||
const distube = new DisTube(client, { | ||
plugins: [new BandlabPlugin()], | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{ | ||
"name": "@distube/bandlab", | ||
"version": "0.0.1", | ||
"author": "Skick (https://github.com/skick1234)", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/distubejs/extractor-plugins.git" | ||
}, | ||
"main": "./dist/index.js", | ||
"devDependencies": { | ||
"@types/node": "^20.14.7", | ||
"discord.js": "^14.15.3", | ||
"distube": "^5.0.2", | ||
"eslint": "^8.57.0", | ||
"eslint-config-distube": "^1.7.0", | ||
"prettier": "^3.3.2", | ||
"tslib": "^2.6.3", | ||
"tsup": "^8.1.0", | ||
"typescript": "^5.5.2" | ||
}, | ||
"exports": "./dist/index.js", | ||
"bugs": { | ||
"url": "https://github.com/distubejs/extractor-plugins/issues" | ||
}, | ||
"description": "A DisTube playable extractor plugin using bandlab.", | ||
"directories": { | ||
"lib": "src" | ||
}, | ||
"files": [ | ||
"dist", | ||
"script" | ||
], | ||
"funding": "https://github.com/skick1234/DisTube?sponsor", | ||
"homepage": "https://github.com/distubejs/extractor-plugins/tree/main/packages/bandlab#readme", | ||
"keywords": [ | ||
"distube", | ||
"plugin", | ||
"discord", | ||
"music" | ||
], | ||
"license": "MIT", | ||
"nano-staged": { | ||
"*.ts": [ | ||
"prettier --write", | ||
"eslint" | ||
], | ||
"*.{json,yml,yaml}": [ | ||
"prettier --write" | ||
] | ||
}, | ||
"peerDependencies": { | ||
"distube": "5" | ||
}, | ||
"scripts": { | ||
"format": "prettier --write \"**/*.{js,ts,json,yml,yaml,md}\"", | ||
"lint": "eslint .", | ||
"build": "tsup", | ||
"type": "tsc --noEmit", | ||
"prepack": "pnpm run build" | ||
}, | ||
"typings": "./dist/index.d.ts", | ||
"dependencies": { | ||
"undici": "^6.19.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Pool } from "undici"; | ||
import type { AlbumResponse } from "./interfaces/Album"; | ||
import type { PlaylistResponse } from "./interfaces/Playlist"; | ||
import type { RevisionResponse } from "./interfaces/Revision"; | ||
import type { TrackResponse } from "./interfaces/Track"; | ||
|
||
const api = new Pool("https://www.bandlab.com"); | ||
|
||
const get = async (path: string): Promise<any> => { | ||
const { body } = await api.request({ | ||
path, | ||
method: "GET", | ||
}); | ||
|
||
const data: any = await body.json(); | ||
if (data.errorCode === 400) throw new Error(data.modelState[0]); | ||
return data; | ||
}; | ||
|
||
export const getTrack = async (id: string): Promise<TrackResponse | RevisionResponse> => get(`/api/v1.3/posts/${id}`); | ||
|
||
export const getAlbum = async (id: string): Promise<AlbumResponse> => get(`/api/v1.3/albums/${id}`); | ||
|
||
export const getPlaylist = async (id: string): Promise<PlaylistResponse> => get(`/api/v1.3/collections/${id}`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { getAlbum, getPlaylist, getTrack } from "./API"; | ||
import { DisTubeError, PlayableExtractorPlugin, Playlist, Song } from "distube"; | ||
import type { ResolveOptions } from "distube"; | ||
import type { RevisionResponse } from "./interfaces/Revision"; | ||
import type { TrackResponse } from "./interfaces/Track"; | ||
import type { AlbumResponse } from "./interfaces/Album"; | ||
import type { PlaylistResponse } from "./interfaces/Playlist"; | ||
|
||
const BASE_URL = "https://www.bandlab.com"; | ||
|
||
const REGEX = | ||
/https?:\/\/(?:www\.)?bandlab\.com\/(?:[\w\d_]+\/)?(collections|albums|track|post|songs)\/([a-f0-9-]+)(?:_|\?|$)/; | ||
|
||
const SUPPORTED_TYPES = ["collections", "albums", "track", "post", "songs"]; | ||
|
||
const parseURL = (url: string): { type?: string; id?: string } => { | ||
const [, type, id] = url.match(REGEX) ?? []; | ||
return { type, id }; | ||
}; | ||
|
||
export class BandlabPlugin extends PlayableExtractorPlugin { | ||
validate(url: string) { | ||
if (typeof url !== "string" || !url.includes("bandlab.com")) return false; | ||
const { type, id } = parseURL(url); | ||
if (!type || !id || !SUPPORTED_TYPES.includes(type)) return false; | ||
return true; | ||
} | ||
|
||
async resolve<T>(url: string, options: ResolveOptions<T>): Promise<Song<T> | Playlist<T>> { | ||
const { type, id } = parseURL(url); | ||
if (!type || !id) throw new DisTubeError("BANDLAB_PLUGIN_INVALID_URL", `Invalid Bandlab url: ${url}`); | ||
|
||
const api = type === "collections" ? getPlaylist(id) : type === "albums" ? getAlbum(id) : getTrack(id); | ||
const data: any = await api.catch(e => { | ||
throw new DisTubeError("BANDLAB_PLUGIN_API_ERROR", e.message); | ||
}); | ||
|
||
if (data.type === "Track") { | ||
return new BandlabTrack(this, data, options); | ||
} else if (data.type === "Revision") { | ||
return new BandlabRevision(this, data, options); | ||
} else { | ||
return new BandlabPlaylist(this, data, url, options); | ||
} | ||
} | ||
|
||
async getStreamURL(song: Song): Promise<string> { | ||
if (!song.url) { | ||
throw new DisTubeError("BANDLAB_PLUGIN_INVALID_SONG", "Cannot get stream url from invalid song."); | ||
} | ||
const data = await getTrack(song.id).catch(e => { | ||
throw new DisTubeError("BANDLAB_ERROR", `${e.message || e}`); | ||
}); | ||
return data.type === "Revision" | ||
? (data as RevisionResponse).revision.mixdown.file | ||
: (data as TrackResponse).track.sample.audioUrl; | ||
} | ||
|
||
getRelatedSongs() { | ||
return []; | ||
} | ||
} | ||
|
||
class BandlabTrack<T> extends Song<T> { | ||
constructor(plugin: BandlabPlugin, data: TrackResponse, options: ResolveOptions<T> = {}) { | ||
super( | ||
{ | ||
plugin, | ||
source: "bandlab", | ||
playFromSource: true, | ||
id: data.id.toString(), | ||
name: data.track.name, | ||
url: `${BASE_URL}/track/${data.id}`, | ||
thumbnail: data.track.picture.url, | ||
uploader: { | ||
name: data.creator.name, | ||
url: `${BASE_URL}/${data.creator.username}`, | ||
}, | ||
likes: data.counters.likes, | ||
views: data.counters.plays, | ||
ageRestricted: data.isExplicit, | ||
duration: data.track.sample.duration, | ||
}, | ||
options, | ||
); | ||
} | ||
} | ||
|
||
class BandlabRevision<T> extends Song<T> { | ||
constructor(plugin: BandlabPlugin, data: RevisionResponse, options: ResolveOptions<T> = {}) { | ||
super( | ||
{ | ||
plugin, | ||
source: "bandlab", | ||
playFromSource: true, | ||
id: data.id.toString(), | ||
name: data.revision.song.name, | ||
url: `${BASE_URL}/track/${data.id}`, | ||
thumbnail: data.revision.song.picture.url, | ||
uploader: { | ||
name: data.creator.name, | ||
url: `${BASE_URL}/${data.creator.username}`, | ||
}, | ||
likes: data.revision.counters.likes, | ||
views: data.revision.counters.plays, | ||
ageRestricted: data.isExplicit, | ||
duration: data.revision.mixdown.duration, | ||
}, | ||
options, | ||
); | ||
} | ||
} | ||
|
||
class BandlabPlaylist<T> extends Playlist<T> { | ||
constructor( | ||
plugin: BandlabPlugin, | ||
data: PlaylistResponse | AlbumResponse, | ||
url: string, | ||
options: ResolveOptions<T> = {}, | ||
) { | ||
super( | ||
{ | ||
source: "bandlab", | ||
id: data.id.toString(), | ||
name: data.name, | ||
url, | ||
thumbnail: data.picture.url, | ||
songs: | ||
data.posts[0].type === "Track" | ||
? data.posts.map((s: any) => new BandlabTrack(plugin, s, options)) | ||
: data.posts.map((s: any) => new BandlabRevision(plugin, s, options)), | ||
}, | ||
options, | ||
); | ||
} | ||
} |
Oops, something went wrong.