From 08ea5a47b6df70e1836ca8f8fd53cda594a7e046 Mon Sep 17 00:00:00 2001 From: Zechariah Date: Fri, 31 Dec 2021 00:54:47 +0800 Subject: [PATCH] Not exporting namespace anymore --- package.json | 2 +- src/{Api.ts => YTMusic.ts} | 45 ++++---- src/index.ts | 186 +++++++++++++++++----------------- src/parsers/AlbumParser.ts | 16 +-- src/parsers/ArtistParser.ts | 6 +- src/parsers/PlaylistParser.ts | 6 +- src/parsers/SearchParser.ts | 4 +- src/parsers/SongParser.ts | 20 ++-- src/parsers/VideoParser.ts | 8 +- src/tests/all.ts | 4 +- src/tests/interfaces.ts | 41 +++++--- src/tests/run.ts | 4 +- 12 files changed, 177 insertions(+), 165 deletions(-) rename src/{Api.ts => YTMusic.ts} (91%) diff --git a/package.json b/package.json index cd055b8..e1a8ca4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ytmusic-api", - "version": "1.0.2", + "version": "1.0.3", "description": "YouTube Music API", "main": "build/index.js", "types": "build/index.d.ts", diff --git a/src/Api.ts b/src/YTMusic.ts similarity index 91% rename from src/Api.ts rename to src/YTMusic.ts index 5d465d4..6eb5f13 100644 --- a/src/Api.ts +++ b/src/YTMusic.ts @@ -6,10 +6,21 @@ import SearchParser from "./parsers/SearchParser" import SongParser from "./parsers/SongParser" import traverse from "./utils/traverse" import VideoParser from "./parsers/VideoParser" -import YTMusic from "." +import { + AlbumDetailed, + AlbumFull, + ArtistDetailed, + ArtistFull, + PlaylistFull, + SearchResult, + SongDetailed, + SongFull, + VideoDetailed, + VideoFull +} from "." import { Cookie, CookieJar } from "tough-cookie" -export default class Api { +export default class YTMusic { private cookiejar: CookieJar private config?: Record private client: AxiosInstance @@ -206,12 +217,12 @@ export default class Api { * @param query Query string * @param category Type of search results to receive */ - public async search(query: string, category: "SONG"): Promise - public async search(query: string, category: "VIDEO"): Promise - public async search(query: string, category: "ARTIST"): Promise - public async search(query: string, category: "ALBUM"): Promise - public async search(query: string, category: "PLAYLIST"): Promise - public async search(query: string): Promise + public async search(query: string, category: "SONG"): Promise + public async search(query: string, category: "VIDEO"): Promise + public async search(query: string, category: "ARTIST"): Promise + public async search(query: string, category: "ALBUM"): Promise + public async search(query: string, category: "PLAYLIST"): Promise + public async search(query: string): Promise public async search(query: string, category?: string) { const searchData = await this.constructRequest("search", { query, @@ -242,7 +253,7 @@ export default class Api { * @param videoId Video ID * @returns Song Data */ - public async getSong(videoId: string): Promise { + public async getSong(videoId: string): Promise { const data = await this.constructRequest("player", { videoId }) return SongParser.parse(data) @@ -254,7 +265,7 @@ export default class Api { * @param videoId Video ID * @returns Video Data */ - public async getVideo(videoId: string): Promise { + public async getVideo(videoId: string): Promise { const data = await this.constructRequest("player", { videoId }) return VideoParser.parse(data) @@ -266,7 +277,7 @@ export default class Api { * @param artistId Artist ID * @returns Artist Data */ - public async getArtist(artistId: string): Promise { + public async getArtist(artistId: string): Promise { const data = await this.constructRequest("browse", { browseId: artistId }) return ArtistParser.parse(data, artistId) @@ -278,7 +289,7 @@ export default class Api { * @param artistId Artist ID * @returns Artist's Songs */ - public async getArtistSongs(artistId: string): Promise { + public async getArtistSongs(artistId: string): Promise { const artistData = await this.constructRequest("browse", { browseId: artistId }) const browseToken = traverse(artistData, "musicShelfRenderer", "title", "browseId") @@ -302,7 +313,7 @@ export default class Api { * @param artistId Artist ID * @returns Artist's Albums */ - public async getArtistAlbums(artistId: string): Promise { + public async getArtistAlbums(artistId: string): Promise { const artistData = await this.constructRequest("browse", { browseId: artistId }) const artistAlbumsData = traverse(artistData, "musicCarouselShelfRenderer")[0] const browseBody = traverse(artistAlbumsData, "moreContentButton", "browseEndpoint") @@ -323,7 +334,7 @@ export default class Api { * @param albumId Album ID * @returns Album Data */ - public async getAlbum(albumId: string): Promise { + public async getAlbum(albumId: string): Promise { const data = await this.constructRequest("browse", { browseId: albumId }) return AlbumParser.parse(data, albumId) @@ -335,7 +346,7 @@ export default class Api { * @param playlistId Playlist ID * @returns Playlist Data */ - public async getPlaylist(playlistId: string): Promise { + public async getPlaylist(playlistId: string): Promise { if (playlistId.startsWith("PL")) playlistId = "VL" + playlistId const data = await this.constructRequest("browse", { browseId: playlistId }) @@ -348,9 +359,7 @@ export default class Api { * @param playlistId Playlist ID * @returns Playlist's Videos */ - public async getPlaylistVideos( - playlistId: string - ): Promise[]> { + public async getPlaylistVideos(playlistId: string): Promise[]> { if (playlistId.startsWith("PL")) playlistId = "VL" + playlistId const playlistData = await this.constructRequest("browse", { browseId: playlistId }) diff --git a/src/index.ts b/src/index.ts index 8d1cac2..4e3180d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,99 +1,95 @@ -import Api from "./Api" +import YTMusic from "./YTMusic" -declare namespace YTMusic { - export { Api } - - export interface ThumbnailFull { - url: string - width: number - height: number - } - - export interface SongDetailed { - type: "SONG" - videoId: string | null - name: string - artists: ArtistBasic[] - album: AlbumBasic - duration: number - thumbnails: ThumbnailFull[] - } - - export interface SongFull extends Omit { - description: string - formats: any[] - adaptiveFormats: any[] - } - - export interface VideoDetailed { - type: "VIDEO" - videoId: string | null - name: string - artists: ArtistBasic[] - views: number - duration: number - thumbnails: ThumbnailFull[] - } - - export interface VideoFull extends VideoDetailed { - description: string - unlisted: boolean - familySafe: boolean - paid: boolean - tags: string[] - } - - export interface ArtistBasic { - artistId: string | null - name: string - } - - export interface ArtistDetailed extends ArtistBasic { - type: "ARTIST" - artistId: string - thumbnails: ThumbnailFull[] - } - - export interface ArtistFull extends ArtistDetailed { - description: string | null - subscribers: number - topSongs: Omit[] - topAlbums: AlbumDetailed[] - } - - export interface AlbumBasic { - albumId: string - name: string - } - - export interface AlbumDetailed extends AlbumBasic { - type: "ALBUM" - playlistId: string - artists: ArtistBasic[] - year: number - thumbnails: ThumbnailFull[] - } - - export interface AlbumFull extends AlbumDetailed { - description: string | null - songs: SongDetailed[] - } - - export interface PlaylistFull { - type: "PLAYLIST" - playlistId: string - name: string - artist: ArtistBasic - videoCount: number - thumbnails: ThumbnailFull[] - } - - export type SearchResult = - | SongDetailed - | VideoDetailed - | AlbumDetailed - | ArtistDetailed - | PlaylistFull +export interface ThumbnailFull { + url: string + width: number + height: number } +export interface SongDetailed { + type: "SONG" + videoId: string | null + name: string + artists: ArtistBasic[] + album: AlbumBasic + duration: number + thumbnails: ThumbnailFull[] +} + +export interface SongFull extends Omit { + description: string + formats: any[] + adaptiveFormats: any[] +} + +export interface VideoDetailed { + type: "VIDEO" + videoId: string | null + name: string + artists: ArtistBasic[] + views: number + duration: number + thumbnails: ThumbnailFull[] +} + +export interface VideoFull extends VideoDetailed { + description: string + unlisted: boolean + familySafe: boolean + paid: boolean + tags: string[] +} + +export interface ArtistBasic { + artistId: string | null + name: string +} + +export interface ArtistDetailed extends ArtistBasic { + type: "ARTIST" + artistId: string + thumbnails: ThumbnailFull[] +} + +export interface ArtistFull extends ArtistDetailed { + description: string | null + subscribers: number + topSongs: Omit[] + topAlbums: AlbumDetailed[] +} + +export interface AlbumBasic { + albumId: string + name: string +} + +export interface AlbumDetailed extends AlbumBasic { + type: "ALBUM" + playlistId: string + artists: ArtistBasic[] + year: number + thumbnails: ThumbnailFull[] +} + +export interface AlbumFull extends AlbumDetailed { + description: string | null + songs: SongDetailed[] +} + +export interface PlaylistFull { + type: "PLAYLIST" + playlistId: string + name: string + artist: ArtistBasic + videoCount: number + thumbnails: ThumbnailFull[] +} + +export type SearchResult = + | SongDetailed + | VideoDetailed + | AlbumDetailed + | ArtistDetailed + | PlaylistFull + export default YTMusic diff --git a/src/parsers/AlbumParser.ts b/src/parsers/AlbumParser.ts index 83032d7..ffde74d 100644 --- a/src/parsers/AlbumParser.ts +++ b/src/parsers/AlbumParser.ts @@ -1,9 +1,9 @@ import SongParser from "./SongParser" import traverse from "../utils/traverse" -import YTMusic from ".." +import { AlbumDetailed, AlbumFull, ArtistBasic } from ".." export default class AlbumParser { - public static parse(data: any, albumId: string): YTMusic.AlbumFull { + public static parse(data: any, albumId: string): AlbumFull { const albumBasic = { albumId, name: traverse(data, "header", "title", "text").at(0) @@ -30,7 +30,7 @@ export default class AlbumParser { } } - public static parseSearchResult(item: any): YTMusic.AlbumDetailed { + public static parseSearchResult(item: any): AlbumDetailed { const flexColumns = traverse(item, "flexColumns") return { @@ -46,10 +46,7 @@ export default class AlbumParser { } } - public static parseArtistAlbum( - item: any, - artistBasic: YTMusic.ArtistBasic - ): YTMusic.AlbumDetailed { + public static parseArtistAlbum(item: any, artistBasic: ArtistBasic): AlbumDetailed { return { type: "ALBUM", albumId: [traverse(item, "browseId")].flat().at(-1), @@ -61,10 +58,7 @@ export default class AlbumParser { } } - public static parseArtistTopAlbums( - item: any, - artistBasic: YTMusic.ArtistBasic - ): YTMusic.AlbumDetailed { + public static parseArtistTopAlbums(item: any, artistBasic: ArtistBasic): AlbumDetailed { return { type: "ALBUM", albumId: traverse(item, "browseId").at(-1), diff --git a/src/parsers/ArtistParser.ts b/src/parsers/ArtistParser.ts index 475680b..b2482c0 100644 --- a/src/parsers/ArtistParser.ts +++ b/src/parsers/ArtistParser.ts @@ -2,10 +2,10 @@ import AlbumParser from "./AlbumParser" import Parser from "./Parser" import SongParser from "./SongParser" import traverse from "../utils/traverse" -import YTMusic from ".." +import { ArtistDetailed, ArtistFull } from ".." export default class ArtistParser { - public static parse(data: any, artistId: string): YTMusic.ArtistFull { + public static parse(data: any, artistId: string): ArtistFull { const artistBasic = { artistId, name: traverse(data, "header", "title", "text").at(0) @@ -29,7 +29,7 @@ export default class ArtistParser { } } - public static parseSearchResult(item: any): YTMusic.ArtistDetailed { + public static parseSearchResult(item: any): ArtistDetailed { const flexColumns = traverse(item, "flexColumns") return { diff --git a/src/parsers/PlaylistParser.ts b/src/parsers/PlaylistParser.ts index ab26e90..ebd1cfd 100644 --- a/src/parsers/PlaylistParser.ts +++ b/src/parsers/PlaylistParser.ts @@ -1,8 +1,8 @@ import traverse from "../utils/traverse" -import YTMusic from ".." +import { PlaylistFull } from ".." export default class PlaylistParser { - public static parse(data: any, playlistId: string): YTMusic.PlaylistFull { + public static parse(data: any, playlistId: string): PlaylistFull { return { type: "PLAYLIST", playlistId, @@ -20,7 +20,7 @@ export default class PlaylistParser { } } - public static parseSearchResult(item: any): YTMusic.PlaylistFull { + public static parseSearchResult(item: any): PlaylistFull { const flexColumns = traverse(item, "flexColumns") const artistId = traverse(flexColumns[1], "browseId") diff --git a/src/parsers/SearchParser.ts b/src/parsers/SearchParser.ts index 8ae6e1e..8d04320 100644 --- a/src/parsers/SearchParser.ts +++ b/src/parsers/SearchParser.ts @@ -4,10 +4,10 @@ import PlaylistParser from "./PlaylistParser" import SongParser from "./SongParser" import traverse from "../utils/traverse" import VideoParser from "./VideoParser" -import YTMusic from ".." +import { SearchResult } from ".." export default class SearchParser { - public static parse(item: any): YTMusic.SearchResult { + public static parse(item: any): SearchResult { const flexColumns = traverse(item, "flexColumns") const type = traverse(flexColumns[1], "runs", "text").at(0) as | "Song" diff --git a/src/parsers/SongParser.ts b/src/parsers/SongParser.ts index e7e2b86..485a2ca 100644 --- a/src/parsers/SongParser.ts +++ b/src/parsers/SongParser.ts @@ -1,9 +1,9 @@ import Parser from "./Parser" import traverse from "../utils/traverse" -import YTMusic from ".." +import { AlbumBasic, ArtistBasic, SongDetailed, SongFull, ThumbnailFull } from ".." export default class SongParser { - public static parse(data: any): YTMusic.SongFull { + public static parse(data: any): SongFull { return { type: "SONG", videoId: traverse(data, "videoDetails", "videoId"), @@ -22,7 +22,7 @@ export default class SongParser { } } - public static parseSearchResult(item: any): YTMusic.SongDetailed { + public static parseSearchResult(item: any): SongDetailed { const flexColumns = traverse(item, "flexColumns") const videoId = traverse(item, "playlistItemData", "videoId") @@ -43,7 +43,7 @@ export default class SongParser { } } - public static parseArtistSong(item: any): YTMusic.SongDetailed { + public static parseArtistSong(item: any): SongDetailed { const flexColumns = traverse(item, "flexColumns") const videoId = traverse(item, "playlistItemData", "videoId") @@ -69,8 +69,8 @@ export default class SongParser { public static parseArtistTopSong( item: any, - artistBasic: YTMusic.ArtistBasic - ): Omit { + artistBasic: ArtistBasic + ): Omit { const flexColumns = traverse(item, "flexColumns") const videoId = traverse(item, "playlistItemData", "videoId") @@ -89,10 +89,10 @@ export default class SongParser { public static parseAlbumSong( item: any, - artists: YTMusic.ArtistBasic[], - albumBasic: YTMusic.AlbumBasic, - thumbnails: YTMusic.ThumbnailFull[] - ): YTMusic.SongDetailed { + artists: ArtistBasic[], + albumBasic: AlbumBasic, + thumbnails: ThumbnailFull[] + ): SongDetailed { const flexColumns = traverse(item, "flexColumns") const videoId = traverse(item, "playlistItemData", "videoId") diff --git a/src/parsers/VideoParser.ts b/src/parsers/VideoParser.ts index 1f9f9c4..699e0b2 100644 --- a/src/parsers/VideoParser.ts +++ b/src/parsers/VideoParser.ts @@ -1,9 +1,9 @@ import Parser from "./Parser" import traverse from "../utils/traverse" -import YTMusic from ".." +import { VideoDetailed, VideoFull } from ".." export default class VideoParser { - public static parse(data: any): YTMusic.VideoFull { + public static parse(data: any): VideoFull { return { type: "VIDEO", videoId: traverse(data, "videoDetails", "videoId"), @@ -25,7 +25,7 @@ export default class VideoParser { } } - public static parseSearchResult(item: any): YTMusic.VideoDetailed { + public static parseSearchResult(item: any): VideoDetailed { const flexColumns = traverse(item, "flexColumns") const videoId = traverse(item, "playNavigationEndpoint", "videoId") @@ -43,7 +43,7 @@ export default class VideoParser { } } - public static parsePlaylistVideo(item: any): Omit { + public static parsePlaylistVideo(item: any): Omit { const flexColumns = traverse(item, "flexColumns") const videoId = traverse(item, "playNavigationEndpoint", "videoId") diff --git a/src/tests/all.ts b/src/tests/all.ts index f03d17d..1f3133b 100644 --- a/src/tests/all.ts +++ b/src/tests/all.ts @@ -1,5 +1,5 @@ import Validator from "validate-any/build/classes/Validator" -import YTMusicApi from "../Api" +import YTMusic from "../YTMusic" import { ALBUM_DETAILED, ALBUM_FULL, @@ -15,7 +15,7 @@ import { import { LIST, validate } from "validate-any" const queries = ["Lilac", "Weekend", "Yours Raiden", "Eminem", "Lisa Hannigan"] -const ytmusic = new YTMusicApi() +const ytmusic = new YTMusic() ytmusic.initialize().then(() => queries.forEach(async query => { diff --git a/src/tests/interfaces.ts b/src/tests/interfaces.ts index a049675..46ef1ba 100644 --- a/src/tests/interfaces.ts +++ b/src/tests/interfaces.ts @@ -1,24 +1,37 @@ import ObjectValidator from "validate-any/build/validators/ObjectValidator" -import YTMusic from "../index" +import { + AlbumBasic, + AlbumDetailed, + AlbumFull, + ArtistBasic, + ArtistDetailed, + ArtistFull, + PlaylistFull, + SongDetailed, + SongFull, + ThumbnailFull, + VideoDetailed, + VideoFull +} from "../index" import { BOOLEAN, LIST, NULL, NUMBER, OBJECT, OR, STRING } from "validate-any" -export const THUMBNAIL_FULL: ObjectValidator = OBJECT({ +export const THUMBNAIL_FULL: ObjectValidator = OBJECT({ url: STRING(), width: NUMBER(), height: NUMBER() }) -export const ARTIST_BASIC: ObjectValidator = OBJECT({ +export const ARTIST_BASIC: ObjectValidator = OBJECT({ artistId: OR(STRING(), NULL()), name: STRING() }) -export const ALBUM_BASIC: ObjectValidator = OBJECT({ +export const ALBUM_BASIC: ObjectValidator = OBJECT({ albumId: STRING(), name: STRING() }) -export const SONG_DETAILED: ObjectValidator = OBJECT({ +export const SONG_DETAILED: ObjectValidator = OBJECT({ type: STRING("SONG"), videoId: OR(STRING(), NULL()), name: STRING(), @@ -28,7 +41,7 @@ export const SONG_DETAILED: ObjectValidator = OBJECT({ thumbnails: LIST(THUMBNAIL_FULL) }) -export const VIDEO_DETAILED: ObjectValidator = OBJECT({ +export const VIDEO_DETAILED: ObjectValidator = OBJECT({ type: STRING("VIDEO"), videoId: OR(STRING(), NULL()), name: STRING(), @@ -38,14 +51,14 @@ export const VIDEO_DETAILED: ObjectValidator = OBJECT({ thumbnails: LIST(THUMBNAIL_FULL) }) -export const ARTIST_DETAILED: ObjectValidator = OBJECT({ +export const ARTIST_DETAILED: ObjectValidator = OBJECT({ artistId: STRING(), name: STRING(), type: STRING("ARTIST"), thumbnails: LIST(THUMBNAIL_FULL) }) -export const ALBUM_DETAILED: ObjectValidator = OBJECT({ +export const ALBUM_DETAILED: ObjectValidator = OBJECT({ type: STRING("ALBUM"), albumId: STRING(), playlistId: STRING(), @@ -55,7 +68,7 @@ export const ALBUM_DETAILED: ObjectValidator = OBJECT({ thumbnails: LIST(THUMBNAIL_FULL) }) -export const SONG_FULL: ObjectValidator = OBJECT({ +export const SONG_FULL: ObjectValidator = OBJECT({ type: STRING("SONG"), videoId: OR(STRING(), NULL()), name: STRING(), @@ -67,7 +80,7 @@ export const SONG_FULL: ObjectValidator = OBJECT({ adaptiveFormats: LIST(OBJECT()) }) -export const VIDEO_FULL: ObjectValidator = OBJECT({ +export const VIDEO_FULL: ObjectValidator = OBJECT({ type: STRING("VIDEO"), videoId: OR(STRING(), NULL()), name: STRING(), @@ -82,7 +95,7 @@ export const VIDEO_FULL: ObjectValidator = OBJECT({ tags: LIST(STRING()) }) -export const ARTIST_FULL: ObjectValidator = OBJECT({ +export const ARTIST_FULL: ObjectValidator = OBJECT({ artistId: STRING(), name: STRING(), type: STRING("ARTIST"), @@ -102,7 +115,7 @@ export const ARTIST_FULL: ObjectValidator = OBJECT({ topAlbums: LIST(ALBUM_DETAILED) }) -export const ALBUM_FULL: ObjectValidator = OBJECT({ +export const ALBUM_FULL: ObjectValidator = OBJECT({ type: STRING("ALBUM"), albumId: STRING(), playlistId: STRING(), @@ -114,7 +127,7 @@ export const ALBUM_FULL: ObjectValidator = OBJECT({ songs: LIST(SONG_DETAILED) }) -export const PLAYLIST_DETAILED: ObjectValidator = OBJECT({ +export const PLAYLIST_DETAILED: ObjectValidator = OBJECT({ type: STRING("PLAYLIST"), playlistId: STRING(), name: STRING(), @@ -123,7 +136,7 @@ export const PLAYLIST_DETAILED: ObjectValidator = OBJECT({ thumbnails: LIST(THUMBNAIL_FULL) }) -export const PLAYLIST_VIDEO: ObjectValidator> = OBJECT({ +export const PLAYLIST_VIDEO: ObjectValidator> = OBJECT({ type: STRING("VIDEO"), videoId: OR(STRING(), NULL()), name: STRING(), diff --git a/src/tests/run.ts b/src/tests/run.ts index 000d78a..9c016b1 100644 --- a/src/tests/run.ts +++ b/src/tests/run.ts @@ -1,6 +1,6 @@ -import YTMusicApi from "../Api" +import YTMusic from "../YTMusic" -const ytmusic = new YTMusicApi() +const ytmusic = new YTMusic() ytmusic.initialize().then(() => { ytmusic.search("Lilac", "SONG").then(res => { ytmusic.getSong(res.find(r => !!r.videoId)!.videoId!).then(res => {