Song and Video data fetching works

This commit is contained in:
Zechariah 2021-12-26 03:22:59 +08:00
parent 7471f1e70c
commit 9479811d4c
8 changed files with 119 additions and 24 deletions

View File

@ -1,7 +1,6 @@
import AlbumParser from "./parsers/AlbumParser" import AlbumParser from "./parsers/AlbumParser"
import ArtistParser from "./parsers/ArtistParser" import ArtistParser from "./parsers/ArtistParser"
import axios, { AxiosInstance } from "axios" import axios, { AxiosInstance } from "axios"
import fs from "fs"
import PlaylistParser from "./parsers/PlaylistParser" import PlaylistParser from "./parsers/PlaylistParser"
import SearchParser from "./parsers/SearchParser" import SearchParser from "./parsers/SearchParser"
import SongParser from "./parsers/SongParser" import SongParser from "./parsers/SongParser"
@ -237,16 +236,28 @@ export default class YTMusic {
) )
} }
public async getSong(videoId: string) { /**
* Get all possible information of a Song
*
* @param videoId Video ID
* @returns Song Data
*/
public async getSong(videoId: string): Promise<YTMusic.SongFull> {
const data = await this.constructRequest("player", { videoId }) const data = await this.constructRequest("player", { videoId })
fs.writeFileSync("data.json", JSON.stringify(data)) return SongParser.parse(data)
} }
public async getVideo(videoId: string) { /**
* Get all possible information of a Video
*
* @param videoId Video ID
* @returns Video Data
*/
public async getVideo(videoId: string): Promise<YTMusic.VideoFull> {
const data = await this.constructRequest("player", { videoId }) const data = await this.constructRequest("player", { videoId })
fs.writeFileSync("data.json", JSON.stringify(data)) return VideoParser.parse(data)
} }
/** /**
@ -320,7 +331,7 @@ export default class YTMusic {
/** /**
* Get all possible information of a Playlist except the tracks * Get all possible information of a Playlist except the tracks
* *
* @param playlistId Playlist ID * @param playlistId Playlist ID
* @returns Playlist Data * @returns Playlist Data
*/ */
@ -333,7 +344,7 @@ export default class YTMusic {
/** /**
* Get all videos in a Playlist * Get all videos in a Playlist
* *
* @param playlistId Playlist ID * @param playlistId Playlist ID
* @returns Playlist's Videos * @returns Playlist's Videos
*/ */

View File

@ -1,10 +0,0 @@
import YTMusic from "./YTMusic"
const ytmusic = new YTMusic()
ytmusic.initialize().then(() => {
ytmusic.search("Lilac", "PLAYLIST").then(res => {
ytmusic.getPlaylist(res[0].playlistId).then(res => {
console.log(res)
})
})
})

View File

@ -2,6 +2,25 @@ import Parser from "./Parser"
import traverse from "../utils/traverse" import traverse from "../utils/traverse"
export default class SongParser { export default class SongParser {
public static parse(data: any): YTMusic.SongFull {
return {
type: "SONG",
videoId: traverse(data, "videoDetails", "videoId"),
name: traverse(data, "videoDetails", "title"),
artists: [
{
artistId: traverse(data, "videoDetails", "channelId"),
name: traverse(data, "author")
}
],
duration: +traverse(data, "videoDetails", "lengthSeconds"),
thumbnails: [traverse(data, "videoDetails", "thumbnails")].flat(),
description: traverse(data, "description"),
formats: traverse(data, "streamingData", "formats"),
adaptiveFormats: traverse(data, "streamingData", "adaptiveFormats")
}
}
public static parseSearchResult(item: any): YTMusic.SongDetailed { public static parseSearchResult(item: any): YTMusic.SongDetailed {
const flexColumns = traverse(item, "flexColumns") const flexColumns = traverse(item, "flexColumns")
const videoId = traverse(item, "playlistItemData", "videoId") const videoId = traverse(item, "playlistItemData", "videoId")

View File

@ -2,6 +2,28 @@ import Parser from "./Parser"
import traverse from "../utils/traverse" import traverse from "../utils/traverse"
export default class VideoParser { export default class VideoParser {
public static parse(data: any): YTMusic.VideoFull {
return {
type: "VIDEO",
videoId: traverse(data, "videoDetails", "videoId"),
name: traverse(data, "videoDetails", "title"),
artists: [
{
artistId: traverse(data, "videoDetails", "channelId"),
name: traverse(data, "author")
}
],
views: +traverse(data, "videoDetails", "viewCount"),
duration: +traverse(data, "videoDetails", "lengthSeconds"),
thumbnails: [traverse(data, "videoDetails", "thumbnails")].flat(),
description: traverse(data, "description"),
unlisted: traverse(data, "unlisted"),
familySafe: traverse(data, "familySafe"),
paid: traverse(data, "paid"),
tags: traverse(data, "tags")
}
}
public static parseSearchResult(item: any): YTMusic.VideoDetailed { public static parseSearchResult(item: any): YTMusic.VideoDetailed {
const flexColumns = traverse(item, "flexColumns") const flexColumns = traverse(item, "flexColumns")
const videoId = traverse(item, "playNavigationEndpoint", "videoId") const videoId = traverse(item, "playNavigationEndpoint", "videoId")

View File

@ -8,7 +8,9 @@ import {
PLAYLIST_DETAILED, PLAYLIST_DETAILED,
PLAYLIST_VIDEO, PLAYLIST_VIDEO,
SONG_DETAILED, SONG_DETAILED,
VIDEO_DETAILED SONG_FULL,
VIDEO_DETAILED,
VIDEO_FULL
} from "./interfaces" } from "./interfaces"
import { LIST, validate } from "validate-any" import { LIST, validate } from "validate-any"
@ -26,10 +28,10 @@ ytmusic.initialize().then(() =>
ytmusic.search(query) ytmusic.search(query)
]) ])
const [artist, artistSongs, artistAlbums, album, playlist, playlistVideos] = const [song, video, artist, artistSongs, artistAlbums, album, playlist, playlistVideos] =
await Promise.all([ await Promise.all([
// ytmusic.getSong(songs[0].videoId), ytmusic.getSong(songs[0].videoId!),
// ytmusic.getVideo(videos[0].videoId), ytmusic.getVideo(videos[0].videoId!),
ytmusic.getArtist(artists[0].artistId), ytmusic.getArtist(artists[0].artistId),
ytmusic.getArtistSongs(artists[0].artistId), ytmusic.getArtistSongs(artists[0].artistId),
ytmusic.getArtistAlbums(artists[0].artistId), ytmusic.getArtistAlbums(artists[0].artistId),
@ -54,8 +56,8 @@ ytmusic.initialize().then(() =>
VIDEO_DETAILED VIDEO_DETAILED
) )
], ],
// [song, SONG_DETAILED], [song, SONG_FULL],
// [video, VIDEO_DETAILED], [video, VIDEO_FULL],
[artist, ARTIST_FULL], [artist, ARTIST_FULL],
[artistSongs, LIST(SONG_DETAILED)], [artistSongs, LIST(SONG_DETAILED)],
[artistAlbums, LIST(ALBUM_DETAILED)], [artistAlbums, LIST(ALBUM_DETAILED)],

View File

@ -1,5 +1,5 @@
import ObjectValidator from "validate-any/build/validators/ObjectValidator" import ObjectValidator from "validate-any/build/validators/ObjectValidator"
import { LIST, NULL, NUMBER, OBJECT, OR, STRING } from "validate-any" import { BOOLEAN, LIST, NULL, NUMBER, OBJECT, OR, STRING } from "validate-any"
export const THUMBNAIL_FULL: ObjectValidator<YTMusic.ThumbnailFull> = OBJECT({ export const THUMBNAIL_FULL: ObjectValidator<YTMusic.ThumbnailFull> = OBJECT({
url: STRING(), url: STRING(),
@ -54,6 +54,33 @@ export const ALBUM_DETAILED: ObjectValidator<YTMusic.AlbumDetailed> = OBJECT({
thumbnails: LIST(THUMBNAIL_FULL) thumbnails: LIST(THUMBNAIL_FULL)
}) })
export const SONG_FULL: ObjectValidator<YTMusic.SongFull> = OBJECT({
type: STRING("SONG"),
videoId: OR(STRING(), NULL()),
name: STRING(),
artists: LIST(ARTIST_BASIC),
duration: NUMBER(),
thumbnails: LIST(THUMBNAIL_FULL),
description: STRING(),
formats: LIST(OBJECT()),
adaptiveFormats: LIST(OBJECT())
})
export const VIDEO_FULL: ObjectValidator<YTMusic.VideoFull> = OBJECT({
type: STRING("VIDEO"),
videoId: OR(STRING(), NULL()),
name: STRING(),
artists: LIST(ARTIST_BASIC),
views: NUMBER(),
duration: NUMBER(),
thumbnails: LIST(THUMBNAIL_FULL),
description: STRING(),
unlisted: BOOLEAN(),
familySafe: BOOLEAN(),
paid: BOOLEAN(),
tags: LIST(STRING())
})
export const ARTIST_FULL: ObjectValidator<YTMusic.ArtistFull> = OBJECT({ export const ARTIST_FULL: ObjectValidator<YTMusic.ArtistFull> = OBJECT({
artistId: STRING(), artistId: STRING(),
name: STRING(), name: STRING(),

10
src/tests/run.ts Normal file
View File

@ -0,0 +1,10 @@
import YTMusic from "../YTMusic"
const ytmusic = new YTMusic()
ytmusic.initialize().then(() => {
ytmusic.search("Lilac", "SONG").then(res => {
ytmusic.getSong(res.find(r => !!r.videoId)!.videoId!).then(res => {
console.log(res)
})
})
})

14
src/types.d.ts vendored
View File

@ -15,6 +15,12 @@ declare namespace YTMusic {
thumbnails: ThumbnailFull[] thumbnails: ThumbnailFull[]
} }
interface SongFull extends Omit<SongDetailed, "album"> {
description: string
formats: any[]
adaptiveFormats: any[]
}
interface VideoDetailed { interface VideoDetailed {
type: "VIDEO" type: "VIDEO"
videoId: string | null videoId: string | null
@ -25,6 +31,14 @@ declare namespace YTMusic {
thumbnails: ThumbnailFull[] thumbnails: ThumbnailFull[]
} }
interface VideoFull extends VideoDetailed {
description: string
unlisted: boolean
familySafe: boolean
paid: boolean
tags: string[]
}
interface ArtistBasic { interface ArtistBasic {
artistId: string | null artistId: string | null
name: string name: string