From bfdceea12d7dad93394f8f0adfb99105bd4ef176 Mon Sep 17 00:00:00 2001 From: zS1L3NT Mac Date: Fri, 22 Dec 2023 03:23:42 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20use=20arktype=20instead=20of=20zod?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lockb | Bin 142893 -> 142481 bytes package.json | 6 +- src/@types/types.ts | 141 ++++++++++++++++++++++++++++++++++ src/YTMusic.ts | 45 ++++++----- src/index.ts | 2 +- src/parsers/AlbumParser.ts | 2 +- src/parsers/ArtistParser.ts | 2 +- src/parsers/PlaylistParser.ts | 2 +- src/parsers/SearchParser.ts | 2 +- src/parsers/SongParser.ts | 10 +-- src/parsers/VideoParser.ts | 8 +- src/schemas.ts | 141 ---------------------------------- src/tests/traversing.spec.ts | 45 +++++------ src/utils/checkType.ts | 15 ++-- 14 files changed, 203 insertions(+), 218 deletions(-) create mode 100644 src/@types/types.ts delete mode 100644 src/schemas.ts diff --git a/bun.lockb b/bun.lockb index 54d036cad1c2d5eca869e857b32e89482d6982a8..6c964b5bcbb50d65f280751ee2d11b41d51af29e 100755 GIT binary patch delta 25971 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 YZfXVke?$aiCk;tSw{FR7sNco@AA#%r1^@s6 delta 26357 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 a{-|=ep[]> { + public async search(query: string): Promise<(typeof SearchResult.infer)[]> { const searchData = await this.constructRequest("search", { query, params: null, @@ -234,7 +233,7 @@ export default class YTMusic { return traverseList(searchData, "musicResponsiveListItemRenderer") .map(SearchParser.parse) - .filter(Boolean) as z.infer[] + .filter(Boolean) as (typeof SearchResult.infer)[] } /** @@ -242,7 +241,7 @@ export default class YTMusic { * * @param query Query string */ - public async searchSongs(query: string): Promise[]> { + public async searchSongs(query: string): Promise<(typeof SongDetailed.infer)[]> { const searchData = await this.constructRequest("search", { query, params: "Eg-KAQwIARAAGAAgACgAMABqChAEEAMQCRAFEAo%3D", @@ -258,7 +257,7 @@ export default class YTMusic { * * @param query Query string */ - public async searchVideos(query: string): Promise[]> { + public async searchVideos(query: string): Promise<(typeof VideoDetailed.infer)[]> { const searchData = await this.constructRequest("search", { query, params: "Eg-KAQwIABABGAAgACgAMABqChAEEAMQCRAFEAo%3D", @@ -274,7 +273,7 @@ export default class YTMusic { * * @param query Query string */ - public async searchArtists(query: string): Promise[]> { + public async searchArtists(query: string): Promise<(typeof ArtistDetailed.infer)[]> { const searchData = await this.constructRequest("search", { query, params: "Eg-KAQwIABAAGAAgASgAMABqChAEEAMQCRAFEAo%3D", @@ -290,7 +289,7 @@ export default class YTMusic { * * @param query Query string */ - public async searchAlbums(query: string): Promise[]> { + public async searchAlbums(query: string): Promise<(typeof AlbumDetailed.infer)[]> { const searchData = await this.constructRequest("search", { query, params: "Eg-KAQwIABAAGAEgACgAMABqChAEEAMQCRAFEAo%3D", @@ -306,7 +305,7 @@ export default class YTMusic { * * @param query Query string */ - public async searchPlaylists(query: string): Promise[]> { + public async searchPlaylists(query: string): Promise<(typeof PlaylistDetailed.infer)[]> { const searchData = await this.constructRequest("search", { query, params: "Eg-KAQwIABAAGAAgACgBMABqChAEEAMQCRAFEAo%3D", @@ -323,7 +322,7 @@ export default class YTMusic { * @param videoId Video ID * @returns Song Data */ - public async getSong(videoId: string): Promise> { + public async getSong(videoId: string): Promise { if (!videoId.match(/^[a-zA-Z0-9-_]{11}$/)) throw new Error("Invalid videoId") const data = await this.constructRequest("player", { videoId }) @@ -338,7 +337,7 @@ export default class YTMusic { * @param videoId Video ID * @returns Video Data */ - public async getVideo(videoId: string): Promise> { + public async getVideo(videoId: string): Promise { if (!videoId.match(/^[a-zA-Z0-9-_]{11}$/)) throw new Error("Invalid videoId") const data = await this.constructRequest("player", { videoId }) @@ -353,7 +352,7 @@ export default class YTMusic { * @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, }) @@ -367,7 +366,7 @@ export default class YTMusic { * @param artistId Artist ID * @returns Artist's Songs */ - public async getArtistSongs(artistId: string): Promise[]> { + public async getArtistSongs(artistId: string): Promise<(typeof SongDetailed.infer)[]> { const artistData = await this.constructRequest("browse", { browseId: artistId, }) @@ -397,7 +396,7 @@ export default class YTMusic { * @param artistId Artist ID * @returns Artist's Albums */ - public async getArtistAlbums(artistId: string): Promise[]> { + public async getArtistAlbums(artistId: string): Promise<(typeof AlbumDetailed.infer)[]> { const artistData = await this.constructRequest("browse", { browseId: artistId, }) @@ -420,7 +419,7 @@ export default class YTMusic { * @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, }) @@ -434,7 +433,7 @@ export default class YTMusic { * @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, @@ -449,7 +448,7 @@ export default class YTMusic { * @param playlistId Playlist ID * @returns Playlist's Videos */ - public async getPlaylistVideos(playlistId: string): Promise[]> { + public async getPlaylistVideos(playlistId: string): Promise<(typeof VideoDetailed.infer)[]> { 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 4923625..c3ddccb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,6 @@ export type { ThumbnailFull, VideoDetailed, VideoFull, -} from "./schemas" +} from "./@types/types" export default YTMusic diff --git a/src/parsers/AlbumParser.ts b/src/parsers/AlbumParser.ts index 469c15a..08d8ca5 100644 --- a/src/parsers/AlbumParser.ts +++ b/src/parsers/AlbumParser.ts @@ -1,4 +1,4 @@ -import { AlbumBasic, AlbumDetailed, AlbumFull, ArtistBasic } from "../schemas" +import { AlbumBasic, AlbumDetailed, AlbumFull, ArtistBasic } from "../@types/types" import checkType from "../utils/checkType" import traverseList from "../utils/traverseList" import traverseString from "../utils/traverseString" diff --git a/src/parsers/ArtistParser.ts b/src/parsers/ArtistParser.ts index 34546cb..77a78fb 100644 --- a/src/parsers/ArtistParser.ts +++ b/src/parsers/ArtistParser.ts @@ -1,4 +1,4 @@ -import { ArtistBasic, ArtistDetailed, ArtistFull } from "../schemas" +import { ArtistBasic, ArtistDetailed, ArtistFull } from "../@types/types" import checkType from "../utils/checkType" import traverseList from "../utils/traverseList" import traverseString from "../utils/traverseString" diff --git a/src/parsers/PlaylistParser.ts b/src/parsers/PlaylistParser.ts index 131aea6..11df24b 100644 --- a/src/parsers/PlaylistParser.ts +++ b/src/parsers/PlaylistParser.ts @@ -1,4 +1,4 @@ -import { PlaylistDetailed, PlaylistFull } from "../schemas" +import { PlaylistDetailed, PlaylistFull } from "../@types/types" import checkType from "../utils/checkType" import traverseList from "../utils/traverseList" import traverseString from "../utils/traverseString" diff --git a/src/parsers/SearchParser.ts b/src/parsers/SearchParser.ts index 7a59fe9..57dd535 100644 --- a/src/parsers/SearchParser.ts +++ b/src/parsers/SearchParser.ts @@ -1,4 +1,4 @@ -import { SearchResult } from "../schemas" +import { SearchResult } from "../@types/types" import traverseList from "../utils/traverseList" import AlbumParser from "./AlbumParser" import ArtistParser from "./ArtistParser" diff --git a/src/parsers/SongParser.ts b/src/parsers/SongParser.ts index 425a9e3..daed359 100644 --- a/src/parsers/SongParser.ts +++ b/src/parsers/SongParser.ts @@ -1,4 +1,4 @@ -import { AlbumBasic, ArtistBasic, SongDetailed, SongFull, ThumbnailFull } from "../schemas" +import { AlbumBasic, ArtistBasic, SongDetailed, SongFull, ThumbnailFull } from "../@types/types" import checkType from "../utils/checkType" import traverseList from "../utils/traverseList" import traverseString from "../utils/traverseString" @@ -81,10 +81,7 @@ export default class SongParser { ) } - public static parseArtistTopSong( - item: any, - artistBasic: ArtistBasic, - ): Omit { + public static parseArtistTopSong(item: any, artistBasic: ArtistBasic): SongDetailed { const flexColumns = traverseList(item, "flexColumns") const videoId = traverseString(item, "playlistItemData", "videoId")() @@ -98,9 +95,10 @@ export default class SongParser { albumId: traverseString(flexColumns[2], "browseId")(), name: traverseString(flexColumns[2], "runs", "text")(), }, + duration: null, thumbnails: traverseList(item, "thumbnails"), }, - SongDetailed.omit({ duration: true }), + SongDetailed, ) } diff --git a/src/parsers/VideoParser.ts b/src/parsers/VideoParser.ts index 233c18a..01a5145 100644 --- a/src/parsers/VideoParser.ts +++ b/src/parsers/VideoParser.ts @@ -1,4 +1,4 @@ -import { ArtistBasic, VideoDetailed, VideoFull } from "../schemas" +import { ArtistBasic, VideoDetailed, VideoFull } from "../@types/types" import checkType from "../utils/checkType" import traverse from "../utils/traverse" import traverseList from "../utils/traverseList" @@ -46,15 +46,13 @@ export default class VideoParser { } } - public static parseArtistTopVideo( - item: any, - artistBasic: ArtistBasic, - ): Omit { + public static parseArtistTopVideo(item: any, artistBasic: ArtistBasic): VideoDetailed { return { type: "VIDEO", videoId: traverseString(item, "videoId")(), name: traverseString(item, "runs", "text")(), artists: [artistBasic], + duration: null, thumbnails: traverseList(item, "thumbnails"), } } diff --git a/src/schemas.ts b/src/schemas.ts deleted file mode 100644 index 4c4581d..0000000 --- a/src/schemas.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { z } from "zod" - -export type ThumbnailFull = z.infer -export const ThumbnailFull = z.object({ - url: z.string(), - width: z.number(), - height: z.number(), -}) - -export type ArtistBasic = z.infer -export const ArtistBasic = z.object({ - artistId: z.string(), - name: z.string(), -}) - -export type AlbumBasic = z.infer -export const AlbumBasic = z.object({ - albumId: z.string(), - name: z.string(), -}) - -export type SongDetailed = z.infer -export const SongDetailed = z.object({ - type: z.literal("SONG"), - videoId: z.string(), - name: z.string(), - artists: z.array(ArtistBasic), - album: AlbumBasic, - duration: z.number(), - thumbnails: z.array(ThumbnailFull), -}) - -export type VideoDetailed = z.infer -export const VideoDetailed = z.object({ - type: z.literal("VIDEO"), - videoId: z.string(), - name: z.string(), - artists: z.array(ArtistBasic), - duration: z.number(), - thumbnails: z.array(ThumbnailFull), -}) - -export type ArtistDetailed = z.infer -export const ArtistDetailed = z.object({ - artistId: z.string(), - name: z.string(), - type: z.literal("ARTIST"), - thumbnails: z.array(ThumbnailFull), -}) - -export type AlbumDetailed = z.infer -export const AlbumDetailed = z.object({ - type: z.literal("ALBUM"), - albumId: z.string(), - playlistId: z.string(), - name: z.string(), - artists: z.array(ArtistBasic), - year: z.number().nullable(), - thumbnails: z.array(ThumbnailFull), -}) - -export type PlaylistDetailed = z.infer -export const PlaylistDetailed = z.object({ - type: z.literal("PLAYLIST"), - playlistId: z.string(), - name: z.string(), - artist: ArtistBasic, - thumbnails: z.array(ThumbnailFull), -}) - -export type SongFull = z.infer -export const SongFull = z.object({ - type: z.literal("SONG"), - videoId: z.string(), - name: z.string(), - artists: z.array(ArtistBasic), - duration: z.number(), - thumbnails: z.array(ThumbnailFull), - description: z.string(), - formats: z.array(z.any()), - adaptiveFormats: z.array(z.any()), -}) - -export type VideoFull = z.infer -export const VideoFull = z.object({ - type: z.literal("VIDEO"), - videoId: z.string(), - name: z.string(), - artists: z.array(ArtistBasic), - duration: z.number(), - thumbnails: z.array(ThumbnailFull), - description: z.string(), - unlisted: z.boolean(), - familySafe: z.boolean(), - paid: z.boolean(), - tags: z.array(z.string()), -}) - -export type ArtistFull = z.infer -export const ArtistFull = z.object({ - artistId: z.string(), - name: z.string(), - type: z.literal("ARTIST"), - thumbnails: z.array(ThumbnailFull), - description: z.string(), - topSongs: z.array(SongDetailed.omit({ duration: true })), - topAlbums: z.array(AlbumDetailed), - topSingles: z.array(AlbumDetailed), - topVideos: z.array(VideoDetailed.omit({ duration: true })), - featuredOn: z.array(PlaylistDetailed), - similarArtists: z.array(ArtistDetailed), -}) - -export type AlbumFull = z.infer -export const AlbumFull = z.object({ - type: z.literal("ALBUM"), - albumId: z.string(), - playlistId: z.string(), - name: z.string(), - artists: z.array(ArtistBasic), - year: z.number().nullable(), - thumbnails: z.array(ThumbnailFull), - description: z.string(), - songs: z.array(SongDetailed), -}) - -export type PlaylistFull = z.infer -export const PlaylistFull = z.object({ - type: z.literal("PLAYLIST"), - playlistId: z.string(), - name: z.string(), - artist: ArtistBasic, - videoCount: z.number(), - thumbnails: z.array(ThumbnailFull), -}) - -export type SearchResult = z.infer -export const SearchResult = SongDetailed.or(VideoDetailed) - .or(AlbumDetailed) - .or(ArtistDetailed) - .or(PlaylistDetailed) diff --git a/src/tests/traversing.spec.ts b/src/tests/traversing.spec.ts index 9f05b0a..27aa0b4 100644 --- a/src/tests/traversing.spec.ts +++ b/src/tests/traversing.spec.ts @@ -1,6 +1,6 @@ +import { arrayOf, Problem, Type, type } from "arktype" import { equal } from "assert" import { afterAll, beforeAll, describe, it } from "bun:test" -import { z } from "zod" import { AlbumDetailed, @@ -9,21 +9,22 @@ import { ArtistFull, PlaylistDetailed, PlaylistFull, + SearchResult, SongDetailed, SongFull, VideoDetailed, VideoFull, -} from "../schemas" +} from "../@types/types" import YTMusic from "../YTMusic" -const errors = []>[] +const errors: Problem[] = [] const queries = ["Lilac", "Weekend", "Eill", "Eminem", "Lisa Hannigan"] -const expect = (data: any, schema: z.Schema) => { - const result = schema.safeParse(data) - if (!result.success && "error" in result) { - errors.push(result.error) +const expect = (data: any, type: Type) => { + const result = type(data) + if (!result.data && "problems" in result) { + errors.push(...result.problems!) } - equal(result.success, true) + equal(!!result.data, true) } const ytmusic = new YTMusic() @@ -33,45 +34,37 @@ queries.forEach(query => { describe("Query: " + query, () => { it("Search suggestions", async () => { const suggestions = await ytmusic.getSearchSuggestions(query) - expect(suggestions, z.array(z.string())) + expect(suggestions, type("string[]")) }) it("Search Songs", async () => { const songs = await ytmusic.searchSongs(query) - expect(songs, z.array(SongDetailed)) + expect(songs, arrayOf(SongDetailed)) }) it("Search Videos", async () => { const videos = await ytmusic.searchVideos(query) - expect(videos, z.array(VideoDetailed)) + expect(videos, arrayOf(VideoDetailed)) }) it("Search Artists", async () => { const artists = await ytmusic.searchArtists(query) - expect(artists, z.array(ArtistDetailed)) + expect(artists, arrayOf(ArtistDetailed)) }) it("Search Albums", async () => { const albums = await ytmusic.searchAlbums(query) - expect(albums, z.array(AlbumDetailed)) + expect(albums, arrayOf(AlbumDetailed)) }) it("Search Playlists", async () => { const playlists = await ytmusic.searchPlaylists(query) - expect(playlists, z.array(PlaylistDetailed)) + expect(playlists, arrayOf(PlaylistDetailed)) }) it("Search All", async () => { const results = await ytmusic.search(query) - expect( - results, - z.array( - AlbumDetailed.or(ArtistDetailed) - .or(PlaylistDetailed) - .or(SongDetailed) - .or(VideoDetailed), - ), - ) + expect(results, arrayOf(SearchResult)) }) it("Get details of the first song result", async () => { @@ -95,13 +88,13 @@ queries.forEach(query => { it("Get the songs of the first artist result", async () => { const artists = await ytmusic.searchArtists(query) const songs = await ytmusic.getArtistSongs(artists[0]!.artistId) - expect(songs, z.array(SongDetailed)) + expect(songs, arrayOf(SongDetailed)) }) it("Get the albums of the first artist result", async () => { const artists = await ytmusic.searchArtists(query) const albums = await ytmusic.getArtistAlbums(artists[0]!.artistId) - expect(albums, z.array(AlbumDetailed)) + expect(albums, arrayOf(AlbumDetailed)) }) it("Get details of the first album result", async () => { @@ -119,7 +112,7 @@ queries.forEach(query => { it("Get the videos of the first playlist result", async () => { const playlists = await ytmusic.searchPlaylists(query) const videos = await ytmusic.getPlaylistVideos(playlists[0]!.playlistId) - expect(videos, z.array(VideoDetailed)) + expect(videos, arrayOf(VideoDetailed)) }) }) }) diff --git a/src/utils/checkType.ts b/src/utils/checkType.ts index 267aa23..916bbcf 100644 --- a/src/utils/checkType.ts +++ b/src/utils/checkType.ts @@ -1,17 +1,16 @@ -import { z } from "zod" -import zodtojson from "zod-to-json-schema" +import { Type } from "arktype" -export default (data: z.infer, schema: T): z.infer => { - const result = schema.safeParse(data) - if (result.success) { - return data +export default (data: T, type: Type): T => { + const result = type(data) + if (result.data) { + return result.data as T } else { if ("error" in result) { console.error( - "Invalid data schema, please report to https://github.com/zS1L3NT/ts-npm-ytmusic-api/issues/new/choose", + "Invalid data type, please report to https://github.com/zS1L3NT/ts-npm-ytmusic-api/issues/new/choose", JSON.stringify( { - schema: zodtojson(schema), + type: type.definition, data, error: result.error, },