Made tests for search, all works
This commit is contained in:
parent
fe73e1d5ab
commit
779035391c
|
|
@ -23,6 +23,16 @@
|
||||||
"skipFiles": ["<node_internals>/**"],
|
"skipFiles": ["<node_internals>/**"],
|
||||||
"runtimeExecutable": "node",
|
"runtimeExecutable": "node",
|
||||||
"runtimeArgs": ["--require", "ts-node/register/files"]
|
"runtimeArgs": ["--require", "ts-node/register/files"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "tests/testing.ts",
|
||||||
|
"program": "${workspaceFolder}/src/tests/testing.ts",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
"runtimeExecutable": "node",
|
||||||
|
"runtimeArgs": ["--require", "ts-node/register/files"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"http-debug": "^0.1.2",
|
"http-debug": "^0.1.2",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript": "^4.4.3",
|
"typescript": "^4.4.3",
|
||||||
|
"validate-any": "^1.1.1",
|
||||||
"youtube-music-api": "^1.0.6"
|
"youtube-music-api": "^1.0.6"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import ArtistParser from "./utils/ArtistParser"
|
import AlbumParser from "./parsers/AlbumParser"
|
||||||
|
import ArtistParser from "./parsers/ArtistParser"
|
||||||
import axios, { AxiosInstance } from "axios"
|
import axios, { AxiosInstance } from "axios"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import SearchParser from "./utils/SearchParser"
|
import PlaylistParser from "./parsers/PlaylistParser"
|
||||||
|
import SearchParser from "./parsers/SearchParser"
|
||||||
|
import SongParser from "./parsers/SongParser"
|
||||||
import traverse from "./utils/traverse"
|
import traverse from "./utils/traverse"
|
||||||
|
import VideoParser from "./parsers/VideoParser"
|
||||||
import { Cookie, CookieJar } from "tough-cookie"
|
import { Cookie, CookieJar } from "tough-cookie"
|
||||||
|
|
||||||
export default class YTMusic {
|
export default class YTMusic {
|
||||||
|
|
@ -223,7 +227,7 @@ export default class YTMusic {
|
||||||
public async search(query: string): Promise<YTMusic.SearchResult[]>
|
public async search(query: string): Promise<YTMusic.SearchResult[]>
|
||||||
public async search(query: string, category?: string) {
|
public async search(query: string, category?: string) {
|
||||||
const searchData = await this.constructRequest("search", {
|
const searchData = await this.constructRequest("search", {
|
||||||
query: query,
|
query,
|
||||||
params:
|
params:
|
||||||
{
|
{
|
||||||
SONG: "Eg-KAQwIARAAGAAgACgAMABqChAEEAMQCRAFEAo%3D",
|
SONG: "Eg-KAQwIARAAGAAgACgAMABqChAEEAMQCRAFEAo%3D",
|
||||||
|
|
@ -234,16 +238,15 @@ export default class YTMusic {
|
||||||
}[category!] || null
|
}[category!] || null
|
||||||
})
|
})
|
||||||
|
|
||||||
const searchParser = new SearchParser(searchData)
|
return traverse(searchData, "musicResponsiveListItemRenderer").map(
|
||||||
return (
|
|
||||||
{
|
{
|
||||||
SONG: searchParser.parseSongs,
|
SONG: SongParser.parseSearch,
|
||||||
VIDEO: searchParser.parseVideos,
|
VIDEO: VideoParser.parseSearch,
|
||||||
ARTIST: searchParser.parseArtists,
|
ARTIST: ArtistParser.parseSearch,
|
||||||
ALBUM: searchParser.parseAlbums,
|
ALBUM: AlbumParser.parseSearch,
|
||||||
PLAYLIST: searchParser.parsePlaylists
|
PLAYLIST: PlaylistParser.parseSearch
|
||||||
}[category!] || searchParser.parse
|
}[category!] || SearchParser.parse
|
||||||
).call(searchParser)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSong(videoId: string) {
|
public async getSong(videoId: string) {
|
||||||
|
|
@ -300,18 +303,4 @@ export default class YTMusic {
|
||||||
|
|
||||||
fs.writeFileSync("data.json", JSON.stringify(data))
|
fs.writeFileSync("data.json", JSON.stringify(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ytmusicapi = new YTMusic()
|
|
||||||
ytmusicapi.initialize().then(async () => {
|
|
||||||
console.log("Initialized")
|
|
||||||
|
|
||||||
const artistDetailed = (await ytmusicapi.search("Roundworm", "ARTIST"))[0]
|
|
||||||
const artistFull = await ytmusicapi.getArtist(artistDetailed.artistId)
|
|
||||||
console.log(JSON.stringify(artistFull, null, 4))
|
|
||||||
|
|
||||||
// const artistDetailed = (await ytmusicapi.search("IU Lilac", "ARTIST"))[0]
|
|
||||||
// const albumDetailed = (await ytmusicapi.getArtistAlbums(artistDetailed.artistId))[0]
|
|
||||||
// const albumFull = await ytmusicapi.getAlbum(albumDetailed.albumId)
|
|
||||||
// console.log(JSON.stringify(albumFull))
|
|
||||||
})
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import YTMusic from "./YTMusic"
|
||||||
|
|
||||||
|
const ytmusic = new YTMusic()
|
||||||
|
ytmusic.initialize().then(() => {
|
||||||
|
ytmusic.search("Yours Raiden", "ALBUM").then(res => {
|
||||||
|
console.log(JSON.stringify(res, null, 4))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import traverse from "../utils/traverse"
|
||||||
|
import fs from "fs"
|
||||||
|
|
||||||
|
export default class AlbumParser {
|
||||||
|
public static parseSearch(item: any): YTMusic.AlbumDetailed {
|
||||||
|
const flexColumns = traverse(item, "flexColumns")
|
||||||
|
const thumbnails = traverse(item, "thumbnails")
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "ALBUM",
|
||||||
|
albumId: [traverse(item, "browseId")].flat().at(-1),
|
||||||
|
playlistId: traverse(item, "overlay", "playlistId"),
|
||||||
|
artists: traverse(flexColumns[1], "runs")
|
||||||
|
.filter((run: any) => "navigationEndpoint" in run)
|
||||||
|
.map((run: any) => ({ artistId: traverse(run, "browseId"), name: run.text })),
|
||||||
|
name: traverse(flexColumns[0], "runs", "text"),
|
||||||
|
year: +traverse(flexColumns[1], "runs", "text").at(-1),
|
||||||
|
thumbnails: [thumbnails].flat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Parse from "./Parser"
|
import Parse from "./Parser"
|
||||||
import traverse from "./traverse"
|
import traverse from "../utils/traverse"
|
||||||
|
|
||||||
export default class ArtistParser {
|
export default class ArtistParser {
|
||||||
private data: any
|
private data: any
|
||||||
|
|
@ -8,8 +8,20 @@ export default class ArtistParser {
|
||||||
this.data = data
|
this.data = data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static parseSearch(item: any): YTMusic.ArtistDetailed {
|
||||||
|
const flexColumns = traverse(item, "flexColumns")
|
||||||
|
const thumbnails = traverse(item, "thumbnails")
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "ARTIST",
|
||||||
|
artistId: traverse(item, "browseId"),
|
||||||
|
name: traverse(flexColumns[0], "runs", "text"),
|
||||||
|
thumbnails: [thumbnails].flat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public parse(artistId: string): YTMusic.ArtistFull {
|
public parse(artistId: string): YTMusic.ArtistFull {
|
||||||
const artistBasic: YTMusic.ArtistBasic = {
|
const artistBasic = {
|
||||||
artistId,
|
artistId,
|
||||||
name: traverse(this.data, "header", "title", "text").at(0)
|
name: traverse(this.data, "header", "title", "text").at(0)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import traverse from "../utils/traverse"
|
||||||
|
|
||||||
|
export default class PlaylistParser {
|
||||||
|
public static parseSearch(item: any, specific: boolean): YTMusic.PlaylistDetailed {
|
||||||
|
const flexColumns = traverse(item, "flexColumns")
|
||||||
|
const thumbnails = traverse(item, "thumbnails")
|
||||||
|
const artistId = traverse(flexColumns[1], "browseId")
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "PLAYLIST",
|
||||||
|
playlistId: traverse(item, "overlay", "playlistId"),
|
||||||
|
name: traverse(flexColumns[0], "runs", "text"),
|
||||||
|
artist: {
|
||||||
|
artistId: artistId instanceof Array ? undefined : artistId,
|
||||||
|
name: traverse(flexColumns[1], "runs", "text").at(specific ? 0 : 2)
|
||||||
|
},
|
||||||
|
trackCount: +traverse(flexColumns[1], "runs", "text").at(-1).split(" ").at(0),
|
||||||
|
thumbnails: [thumbnails].flat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import AlbumParser from "./AlbumParser"
|
||||||
|
import ArtistParser from "./ArtistParser"
|
||||||
|
import PlaylistParser from "./PlaylistParser"
|
||||||
|
import SongParser from "./SongParser"
|
||||||
|
import traverse from "../utils/traverse"
|
||||||
|
import VideoParser from "./VideoParser"
|
||||||
|
|
||||||
|
export default class SearchParser {
|
||||||
|
public static parse(item: any): YTMusic.SearchResult {
|
||||||
|
const flexColumns = traverse(item, "flexColumns")
|
||||||
|
const type = traverse(flexColumns[1], "runs", "text").at(0) as
|
||||||
|
| "Song"
|
||||||
|
| "Video"
|
||||||
|
| "Artist"
|
||||||
|
| "EP"
|
||||||
|
| "Single"
|
||||||
|
| "Album"
|
||||||
|
| "Playlist"
|
||||||
|
|
||||||
|
return {
|
||||||
|
Song: () => SongParser.parseSearch(item),
|
||||||
|
Video: () => VideoParser.parseSearch(item, true),
|
||||||
|
Artist: () => ArtistParser.parseSearch(item),
|
||||||
|
EP: () => AlbumParser.parseSearch(item),
|
||||||
|
Single: () => AlbumParser.parseSearch(item),
|
||||||
|
Album: () => AlbumParser.parseSearch(item),
|
||||||
|
Playlist: () => PlaylistParser.parseSearch(item, true)
|
||||||
|
}[type]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import Parser from "./Parser"
|
||||||
|
import traverse from "../utils/traverse"
|
||||||
|
|
||||||
|
export default class SongParser {
|
||||||
|
public static parseSearch(item: any): YTMusic.SongDetailed {
|
||||||
|
const flexColumns = traverse(item, "flexColumns")
|
||||||
|
const thumbnails = traverse(item, "thumbnails")
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "SONG",
|
||||||
|
videoId: traverse(item, "playlistItemData", "videoId"),
|
||||||
|
name: traverse(flexColumns[0], "runs", "text"),
|
||||||
|
artists: traverse(flexColumns[1], "runs")
|
||||||
|
.map((run: any) =>
|
||||||
|
"navigationEndpoint" in run
|
||||||
|
? { name: run.text, artistId: traverse(run, "browseId") }
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
.slice(0, -3)
|
||||||
|
.filter(Boolean),
|
||||||
|
album: {
|
||||||
|
albumId: traverse(item, "browseId").at(-1),
|
||||||
|
name: traverse(flexColumns[1], "runs", "text").at(-3)
|
||||||
|
},
|
||||||
|
duration: Parser.parseDuration(traverse(flexColumns[1], "runs", "text").at(-1)),
|
||||||
|
thumbnails: thumbnails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Parser from "./Parser"
|
||||||
|
import traverse from "../utils/traverse"
|
||||||
|
|
||||||
|
export default class VideoParser {
|
||||||
|
public static parseSearch(item: any, specific: boolean): YTMusic.VideoDetailed {
|
||||||
|
const flexColumns = traverse(item, "flexColumns")
|
||||||
|
const thumbnails = traverse(item, "thumbnails")
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "VIDEO",
|
||||||
|
videoId: traverse(item, "playNavigationEndpoint", "videoId"),
|
||||||
|
name: traverse(flexColumns[0], "runs", "text"),
|
||||||
|
artists: traverse(flexColumns[1], "runs")
|
||||||
|
.filter((run: any) => "navigationEndpoint" in run)
|
||||||
|
.map((run: any) => ({ artistId: traverse(run, "browseId"), name: run.text })),
|
||||||
|
views: Parser.parseNumber(traverse(flexColumns[1], "runs", "text").at(-3).slice(0, -6)),
|
||||||
|
duration: Parser.parseDuration(traverse(flexColumns[1], "text").at(-1)),
|
||||||
|
thumbnails: [thumbnails].flat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import ObjectValidator from "validate-any/build/validators/ObjectValidator"
|
||||||
|
import { LIST, NUMBER, OBJECT, OR, STRING, UNDEFINED } from "validate-any"
|
||||||
|
|
||||||
|
export const THUMBNAIL_FULL: ObjectValidator<YTMusic.ThumbnailFull> = OBJECT({
|
||||||
|
url: STRING(),
|
||||||
|
width: NUMBER(),
|
||||||
|
height: NUMBER()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ARTIST_BASIC: ObjectValidator<YTMusic.ArtistBasic> = OBJECT({
|
||||||
|
artistId: OR(STRING(), UNDEFINED()),
|
||||||
|
name: STRING()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ALBUM_BASIC: ObjectValidator<YTMusic.AlbumBasic> = OBJECT({
|
||||||
|
albumId: STRING(),
|
||||||
|
name: STRING()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SONG_DETAILED: ObjectValidator<YTMusic.SongDetailed> = OBJECT({
|
||||||
|
type: STRING("SONG"),
|
||||||
|
videoId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
artists: LIST(ARTIST_BASIC),
|
||||||
|
album: ALBUM_BASIC,
|
||||||
|
duration: NUMBER(),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const VIDEO_DETAILED: ObjectValidator<YTMusic.VideoDetailed> = OBJECT({
|
||||||
|
type: STRING("VIDEO"),
|
||||||
|
videoId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
artists: LIST(ARTIST_BASIC),
|
||||||
|
views: NUMBER(),
|
||||||
|
duration: NUMBER(),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ARTIST_DETAILED: ObjectValidator<YTMusic.ArtistDetailed> = OBJECT({
|
||||||
|
artistId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
type: STRING("ARTIST"),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ALBUM_DETAILED: ObjectValidator<YTMusic.AlbumDetailed> = OBJECT({
|
||||||
|
type: STRING("ALBUM"),
|
||||||
|
albumId: STRING(),
|
||||||
|
playlistId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
artists: LIST(ARTIST_BASIC),
|
||||||
|
year: NUMBER(),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ARTIST_FULL: ObjectValidator<YTMusic.ArtistFull> = OBJECT({
|
||||||
|
artistId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
type: STRING("ARTIST"),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL),
|
||||||
|
description: STRING(),
|
||||||
|
subscribers: NUMBER(),
|
||||||
|
topTracks: LIST(
|
||||||
|
OBJECT({
|
||||||
|
type: STRING("SONG"),
|
||||||
|
videoId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
artists: LIST(ARTIST_BASIC),
|
||||||
|
album: ALBUM_BASIC,
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL)
|
||||||
|
})
|
||||||
|
),
|
||||||
|
topAlbums: LIST(ALBUM_DETAILED)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ALBUM_FULL: ObjectValidator<YTMusic.AlbumFull> = OBJECT({
|
||||||
|
type: STRING("ALBUM"),
|
||||||
|
albumId: STRING(),
|
||||||
|
playlistId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
artists: LIST(ARTIST_BASIC),
|
||||||
|
year: NUMBER(),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL),
|
||||||
|
description: STRING(),
|
||||||
|
tracks: LIST(SONG_DETAILED)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const PLAYLIST_DETAILED: ObjectValidator<YTMusic.PlaylistDetailed> = OBJECT({
|
||||||
|
type: STRING("PLAYLIST"),
|
||||||
|
playlistId: STRING(),
|
||||||
|
name: STRING(),
|
||||||
|
artist: ARTIST_BASIC,
|
||||||
|
trackCount: NUMBER(),
|
||||||
|
thumbnails: LIST(THUMBNAIL_FULL)
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import Validator from "validate-any/build/classes/Validator"
|
||||||
|
import YTMusic from "../YTMusic"
|
||||||
|
import {
|
||||||
|
ALBUM_DETAILED,
|
||||||
|
ARTIST_DETAILED,
|
||||||
|
PLAYLIST_DETAILED,
|
||||||
|
SONG_DETAILED,
|
||||||
|
VIDEO_DETAILED
|
||||||
|
} from "./interfaces"
|
||||||
|
import { LIST, validate } from "validate-any"
|
||||||
|
|
||||||
|
const ytmusic = new YTMusic()
|
||||||
|
const tests: (query: string) => [() => Promise<any>, Validator<any>][] = query => [
|
||||||
|
[() => ytmusic.search(query, "SONG"), LIST(SONG_DETAILED)],
|
||||||
|
[() => ytmusic.search(query, "VIDEO"), LIST(VIDEO_DETAILED)],
|
||||||
|
[() => ytmusic.search(query, "ARTIST"), LIST(ARTIST_DETAILED)],
|
||||||
|
[() => ytmusic.search(query, "ALBUM"), LIST(ALBUM_DETAILED)],
|
||||||
|
[() => ytmusic.search(query, "PLAYLIST"), LIST(PLAYLIST_DETAILED)],
|
||||||
|
[
|
||||||
|
() => ytmusic.search(query),
|
||||||
|
LIST(ALBUM_DETAILED, ARTIST_DETAILED, PLAYLIST_DETAILED, SONG_DETAILED, VIDEO_DETAILED)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
const queries = ["Lilac", "Weekend", "Yours Raiden", "Eminem"]
|
||||||
|
|
||||||
|
ytmusic.initialize().then(async () => {
|
||||||
|
queries.forEach(query => {
|
||||||
|
tests(query).forEach(async ([fetch, validator]) => {
|
||||||
|
const value = await fetch()
|
||||||
|
const result = validate(value, validator)
|
||||||
|
if (!result.success) {
|
||||||
|
console.log(JSON.stringify(value))
|
||||||
|
console.log(validator.formatSchema())
|
||||||
|
console.log(result.errors)
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -19,26 +19,27 @@ declare namespace YTMusic {
|
||||||
type: "VIDEO"
|
type: "VIDEO"
|
||||||
videoId: string
|
videoId: string
|
||||||
name: string
|
name: string
|
||||||
artist: ArtistBasic
|
artists: ArtistBasic[]
|
||||||
views: number
|
views: number
|
||||||
duration: number
|
duration: number
|
||||||
thumbnails: ThumbnailFull[]
|
thumbnails: ThumbnailFull[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ArtistBasic {
|
interface ArtistBasic {
|
||||||
artistId: string
|
artistId?: string
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ArtistDetailed extends ArtistBasic {
|
interface ArtistDetailed extends ArtistBasic {
|
||||||
type: "ARTIST"
|
type: "ARTIST"
|
||||||
|
artistId: string
|
||||||
thumbnails: ThumbnailFull[]
|
thumbnails: ThumbnailFull[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ArtistFull extends ArtistDetailed {
|
interface ArtistFull extends ArtistDetailed {
|
||||||
description: string
|
description: string
|
||||||
subscribers: number
|
subscribers: number
|
||||||
topTracks: (Omit<SongDetailed, "duration">)[]
|
topTracks: Omit<SongDetailed, "duration">[]
|
||||||
topAlbums: AlbumDetailed[]
|
topAlbums: AlbumDetailed[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ declare namespace YTMusic {
|
||||||
|
|
||||||
interface AlbumFull extends AlbumDetailed {
|
interface AlbumFull extends AlbumDetailed {
|
||||||
description: string
|
description: string
|
||||||
tracks: []
|
tracks: SongDetailed[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlaylistDetailed {
|
interface PlaylistDetailed {
|
||||||
|
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
import Parse from "./Parser"
|
|
||||||
import traverse from "./traverse"
|
|
||||||
|
|
||||||
export default class SearchParser {
|
|
||||||
private items: any[]
|
|
||||||
|
|
||||||
public constructor(data: any) {
|
|
||||||
this.items = [traverse(data, "musicResponsiveListItemRenderer")].flat()
|
|
||||||
}
|
|
||||||
|
|
||||||
public parseSongs(): YTMusic.SongDetailed[] {
|
|
||||||
return this.items.map(item => this.parseSong(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseSong(item: any): YTMusic.SongDetailed {
|
|
||||||
const flexColumns = traverse(item, "flexColumns")
|
|
||||||
const thumbnails = traverse(item, "thumbnails")
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "SONG",
|
|
||||||
videoId: traverse(item, "playlistItemData", "videoId"),
|
|
||||||
name: traverse(flexColumns[0], "runs", "text"),
|
|
||||||
artists: traverse(flexColumns[1], "runs")
|
|
||||||
.map((run: any) =>
|
|
||||||
"navigationEndpoint" in run
|
|
||||||
? { name: run.text, artistId: traverse(run, "browseId") }
|
|
||||||
: null
|
|
||||||
)
|
|
||||||
.slice(0, -3)
|
|
||||||
.filter(Boolean),
|
|
||||||
album: {
|
|
||||||
albumId: traverse(item, "browseId").at(-1),
|
|
||||||
name: traverse(flexColumns[1], "runs", "text").at(-3)
|
|
||||||
},
|
|
||||||
duration: Parse.parseDuration(traverse(flexColumns[1], "runs", "text").at(-1)),
|
|
||||||
thumbnails: [thumbnails].flat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public parseVideos(): YTMusic.VideoDetailed[] {
|
|
||||||
return this.items.map(item => this.parseVideo(item, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseVideo(item: any, specific: boolean): YTMusic.VideoDetailed {
|
|
||||||
const flexColumns = traverse(item, "flexColumns")
|
|
||||||
const thumbnails = traverse(item, "thumbnails")
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "VIDEO",
|
|
||||||
videoId: traverse(item, "playNavigationEndpoint", "videoId"),
|
|
||||||
name: traverse(flexColumns[0], "runs", "text"),
|
|
||||||
artist: {
|
|
||||||
artistId: traverse(flexColumns[1], "browseId"),
|
|
||||||
name: traverse(flexColumns[1], "runs", "text").at(specific ? 0 : 2)
|
|
||||||
},
|
|
||||||
views: Parse.parseNumber(traverse(flexColumns[1], "runs", "text").at(-3).slice(0, -6)),
|
|
||||||
duration: Parse.parseDuration(traverse(flexColumns[1], "text").at(-1)),
|
|
||||||
thumbnails: [thumbnails].flat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public parseArtists(): YTMusic.ArtistDetailed[] {
|
|
||||||
return this.items.map(item => this.parseArtist(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseArtist(item: any): YTMusic.ArtistDetailed {
|
|
||||||
const flexColumns = traverse(item, "flexColumns")
|
|
||||||
const thumbnails = traverse(item, "thumbnails")
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "ARTIST",
|
|
||||||
artistId: traverse(item, "browseId"),
|
|
||||||
name: traverse(flexColumns[0], "runs", "text"),
|
|
||||||
thumbnails: [thumbnails].flat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public parseAlbums(): YTMusic.AlbumDetailed[] {
|
|
||||||
return this.items.map(item => this.parseAlbum(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseAlbum(item: any): YTMusic.AlbumDetailed {
|
|
||||||
const flexColumns = traverse(item, "flexColumns")
|
|
||||||
const thumbnails = traverse(item, "thumbnails")
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "ALBUM",
|
|
||||||
albumId: traverse(item, "browseId").at(-1),
|
|
||||||
playlistId: traverse(item, "overlay", "playlistId"),
|
|
||||||
artists: traverse(flexColumns[1], "runs")
|
|
||||||
.map((run: any) =>
|
|
||||||
"navigationEndpoint" in run
|
|
||||||
? { name: run.text, artistId: traverse(run, "browseId") }
|
|
||||||
: null
|
|
||||||
)
|
|
||||||
.slice(0, -1)
|
|
||||||
.filter(Boolean),
|
|
||||||
name: traverse(flexColumns[0], "runs", "text"),
|
|
||||||
year: +traverse(flexColumns[1], "runs", "text").at(-1),
|
|
||||||
thumbnails: [thumbnails].flat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public parsePlaylists(): YTMusic.PlaylistDetailed[] {
|
|
||||||
return this.items.map(item => this.parsePlaylist(item, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
private parsePlaylist(item: any, specific: boolean): YTMusic.PlaylistDetailed {
|
|
||||||
const flexColumns = traverse(item, "flexColumns")
|
|
||||||
const thumbnails = traverse(item, "thumbnails")
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "PLAYLIST",
|
|
||||||
playlistId: traverse(item, "overlay", "playlistId"),
|
|
||||||
name: traverse(flexColumns[0], "runs", "text"),
|
|
||||||
artist: {
|
|
||||||
artistId: traverse(flexColumns[1], "browseId"),
|
|
||||||
name: traverse(flexColumns[1], "runs", "text").at(specific ? 0 : 2)
|
|
||||||
},
|
|
||||||
trackCount: +traverse(flexColumns[1], "runs", "text").at(-1).split(" ").at(0),
|
|
||||||
thumbnails: [thumbnails].flat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public parse(): YTMusic.SearchResult[] {
|
|
||||||
return this.items.map(item => {
|
|
||||||
const flexColumns = traverse(item, "flexColumns")
|
|
||||||
const type = traverse(flexColumns[1], "runs", "text").at(0) as
|
|
||||||
| "Song"
|
|
||||||
| "Video"
|
|
||||||
| "Artist"
|
|
||||||
| "EP"
|
|
||||||
| "Single"
|
|
||||||
| "Album"
|
|
||||||
| "Playlist"
|
|
||||||
|
|
||||||
return {
|
|
||||||
Song: () => this.parseSong(item),
|
|
||||||
Video: () => this.parseVideo(item, true),
|
|
||||||
Artist: () => this.parseArtist(item),
|
|
||||||
EP: () => this.parseAlbum(item),
|
|
||||||
Single: () => this.parseAlbum(item),
|
|
||||||
Album: () => this.parseAlbum(item),
|
|
||||||
Playlist: () => this.parsePlaylist(item, true)
|
|
||||||
}[type]()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,35 @@
|
||||||
|
const traverse = (data: any, keys: string, single = false) => {
|
||||||
|
const again = (data: any, key: string): any => {
|
||||||
|
var res = []
|
||||||
|
|
||||||
|
data.hasOwnProperty(key) && res.push(data[key])
|
||||||
|
if (single && data.hasOwnProperty(key)) {
|
||||||
|
return res.shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof Array) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
res = res.concat(again(data[i], key))
|
||||||
|
}
|
||||||
|
} else if (data instanceof Object) {
|
||||||
|
const c = Object.keys(data)
|
||||||
|
if (c.length > 0) {
|
||||||
|
for (let i = 0; i < c.length; i++) {
|
||||||
|
res = res.concat(again(data[c[i]], key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.length == 1 ? res.shift() : res
|
||||||
|
}
|
||||||
|
|
||||||
|
let z = keys.split(":"),
|
||||||
|
value = data
|
||||||
|
for (let i = 0; i < z.length; i++) {
|
||||||
|
value = again(value, z[i])
|
||||||
|
}
|
||||||
|
console.log(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(require("./data.json"), "playNavigationEndpoint:videoId")
|
||||||
|
|
||||||
|
export default {}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
const traverse = (data: any, keys: string[], single: boolean = false) => {
|
||||||
|
const again = (data: any, key: string): any => {
|
||||||
|
let res = []
|
||||||
|
|
||||||
|
if (data instanceof Object && key in data) {
|
||||||
|
if (single) return data[key]
|
||||||
|
res.push(data[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof Array) {
|
||||||
|
res.push(...data.map(v => again(v, key)).flat())
|
||||||
|
} else if (data instanceof Object) {
|
||||||
|
res.push(
|
||||||
|
...Object.keys(data)
|
||||||
|
.map(k => again(data[k], key))
|
||||||
|
.flat()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = data
|
||||||
|
for (const key of keys) {
|
||||||
|
value = again(value, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(require("./data.json")[0], ["playNavigationEndpoint", "videoId"], true)
|
||||||
|
|
||||||
|
export default {}
|
||||||
162
yarn.lock
162
yarn.lock
|
|
@ -44,6 +44,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40"
|
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40"
|
||||||
integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==
|
integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==
|
||||||
|
|
||||||
|
abbrev@1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||||
|
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||||
|
|
||||||
acorn-walk@^8.1.1:
|
acorn-walk@^8.1.1:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
|
|
@ -73,6 +78,37 @@ axios@^0.24.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.14.4"
|
follow-redirects "^1.14.4"
|
||||||
|
|
||||||
|
balanced-match@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
brace-expansion@^1.1.7:
|
||||||
|
version "1.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
|
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^1.0.0"
|
||||||
|
concat-map "0.0.1"
|
||||||
|
|
||||||
|
commander@^2.19.0:
|
||||||
|
version "2.20.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
|
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||||
|
|
||||||
|
config-chain@^1.1.12:
|
||||||
|
version "1.1.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
|
||||||
|
integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
|
||||||
|
dependencies:
|
||||||
|
ini "^1.3.4"
|
||||||
|
proto-list "~1.2.1"
|
||||||
|
|
||||||
create-require@^1.1.0:
|
create-require@^1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||||
|
|
@ -90,6 +126,16 @@ diff@^4.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||||
|
|
||||||
|
editorconfig@^0.15.3:
|
||||||
|
version "0.15.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
|
||||||
|
integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==
|
||||||
|
dependencies:
|
||||||
|
commander "^2.19.0"
|
||||||
|
lru-cache "^4.1.5"
|
||||||
|
semver "^5.6.0"
|
||||||
|
sigmund "^1.0.1"
|
||||||
|
|
||||||
follow-redirects@1.5.10:
|
follow-redirects@1.5.10:
|
||||||
version "1.5.10"
|
version "1.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
|
||||||
|
|
@ -102,26 +148,115 @@ follow-redirects@^1.14.4:
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
|
||||||
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
|
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
|
||||||
|
|
||||||
|
fs.realpath@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||||
|
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||||
|
|
||||||
|
glob@^7.1.3:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||||
|
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
http-debug@^0.1.2:
|
http-debug@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/http-debug/-/http-debug-0.1.2.tgz#aadbfef99bc39c206439ece14b99040c5a4b4d6e"
|
resolved "https://registry.yarnpkg.com/http-debug/-/http-debug-0.1.2.tgz#aadbfef99bc39c206439ece14b99040c5a4b4d6e"
|
||||||
integrity sha1-qtv++ZvDnCBkOezhS5kEDFpLTW4=
|
integrity sha1-qtv++ZvDnCBkOezhS5kEDFpLTW4=
|
||||||
|
|
||||||
|
inflight@^1.0.4:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
|
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||||
|
dependencies:
|
||||||
|
once "^1.3.0"
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
inherits@2:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
ini@^1.3.4:
|
||||||
|
version "1.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||||
|
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||||
|
|
||||||
|
js-beautify@^1.14.0:
|
||||||
|
version "1.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.0.tgz#2ce790c555d53ce1e3d7363227acf5dc69024c2d"
|
||||||
|
integrity sha512-yuck9KirNSCAwyNJbqW+BxJqJ0NLJ4PwBUzQQACl5O3qHMBXVkXb/rD0ilh/Lat/tn88zSZ+CAHOlk0DsY7GuQ==
|
||||||
|
dependencies:
|
||||||
|
config-chain "^1.1.12"
|
||||||
|
editorconfig "^0.15.3"
|
||||||
|
glob "^7.1.3"
|
||||||
|
nopt "^5.0.0"
|
||||||
|
|
||||||
lodash@^4.17.15:
|
lodash@^4.17.15:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
lru-cache@^4.1.5:
|
||||||
|
version "4.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
|
||||||
|
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
|
||||||
|
dependencies:
|
||||||
|
pseudomap "^1.0.2"
|
||||||
|
yallist "^2.1.2"
|
||||||
|
|
||||||
make-error@^1.1.1:
|
make-error@^1.1.1:
|
||||||
version "1.3.6"
|
version "1.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||||
|
|
||||||
|
minimatch@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||||
|
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||||
|
|
||||||
|
nopt@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
|
||||||
|
integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
|
||||||
|
dependencies:
|
||||||
|
abbrev "1"
|
||||||
|
|
||||||
|
once@^1.3.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
|
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||||
|
dependencies:
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
path-is-absolute@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||||
|
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||||
|
|
||||||
|
proto-list@~1.2.1:
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
||||||
|
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
|
||||||
|
|
||||||
|
pseudomap@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||||
|
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
|
||||||
|
|
||||||
psl@^1.1.33:
|
psl@^1.1.33:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
||||||
|
|
@ -132,6 +267,16 @@ punycode@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
|
semver@^5.6.0:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
|
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||||
|
|
||||||
|
sigmund@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
|
||||||
|
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
|
||||||
|
|
||||||
tough-cookie@^4.0.0:
|
tough-cookie@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
|
||||||
|
|
@ -169,6 +314,23 @@ universalify@^0.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||||
|
|
||||||
|
validate-any@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/validate-any/-/validate-any-1.1.1.tgz#0845d9e8f2b44e342633e6a73ffa21973b71507f"
|
||||||
|
integrity sha512-L3bCSEn/eH/mdPp+hFQg4nTBeMr19V4kbKMxMSOwRlbMA6N6LWi52Duz0gMQK9gJkz8VbMcvx5Yka2u9J8epjQ==
|
||||||
|
dependencies:
|
||||||
|
js-beautify "^1.14.0"
|
||||||
|
|
||||||
|
wrappy@1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
|
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||||
|
|
||||||
|
yallist@^2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||||
|
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
|
||||||
|
|
||||||
yn@3.1.1:
|
yn@3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue