From e1b149d799c3eec756f4c696bdc5c20599147b13 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 01:24:50 +0200 Subject: [PATCH 01/15] feat: base new parsers --- deno.json | 1 + deno.lock | 160 +++--------------- examples/authentication.ts | 13 +- src/mixins2/browse/home.ts | 13 ++ src/mixins2/browse/mod.ts | 1 + src/mixins2/mod.ts | 1 + src/mod.ts | 10 +- .../endpoint/endpoints/browse.test.ts | 23 +++ src/parsers2/endpoint/endpoints/browse.ts | 27 +++ src/parsers2/endpoint/endpoints/mod.ts | 1 + src/parsers2/endpoint/mod.ts | 3 + src/parsers2/endpoint/parse.test.ts | 18 ++ src/parsers2/endpoint/parse.ts | 42 +++++ src/parsers2/endpoint/types.d.ts | 17 ++ src/parsers2/tabs/mod.ts | 2 + src/parsers2/tabs/single_column.test.ts | 19 +++ src/parsers2/tabs/single_column.ts | 19 +++ src/parsers2/tabs/tab_renderer.test.ts | 32 ++++ src/parsers2/tabs/tab_renderer.ts | 33 ++++ src/parsers2/types.d.ts | 3 + src/util.ts | 6 +- 21 files changed, 286 insertions(+), 158 deletions(-) create mode 100644 src/mixins2/browse/home.ts create mode 100644 src/mixins2/browse/mod.ts create mode 100644 src/mixins2/mod.ts create mode 100644 src/parsers2/endpoint/endpoints/browse.test.ts create mode 100644 src/parsers2/endpoint/endpoints/browse.ts create mode 100644 src/parsers2/endpoint/endpoints/mod.ts create mode 100644 src/parsers2/endpoint/mod.ts create mode 100644 src/parsers2/endpoint/parse.test.ts create mode 100644 src/parsers2/endpoint/parse.ts create mode 100644 src/parsers2/endpoint/types.d.ts create mode 100644 src/parsers2/tabs/mod.ts create mode 100644 src/parsers2/tabs/single_column.test.ts create mode 100644 src/parsers2/tabs/single_column.ts create mode 100644 src/parsers2/tabs/tab_renderer.test.ts create mode 100644 src/parsers2/tabs/tab_renderer.ts create mode 100644 src/parsers2/types.d.ts diff --git a/deno.json b/deno.json index e27c18b..47babf6 100644 --- a/deno.json +++ b/deno.json @@ -2,6 +2,7 @@ "tasks": { "example": "deno run --allow-net --allow-read --allow-write --allow-env=DEBUG ./examples/authentication.ts", "test": "deno test --allow-net --allow-read --allow-write --allow-env=DEBUG --fail-fast ./tests/", + "test2": "deno test src", "npm": "deno run -A ./scripts/npm.ts" }, "lint": { diff --git a/deno.lock b/deno.lock index a884338..7163f41 100644 --- a/deno.lock +++ b/deno.lock @@ -2,162 +2,44 @@ "version": "3", "packages": { "specifiers": { - "jsr:@david/code-block-writer@^13.0.2": "jsr:@david/code-block-writer@13.0.3", - "jsr:@deno/cache-dir@^0.10.3": "jsr:@deno/cache-dir@0.10.3", - "jsr:@deno/dnt": "jsr:@deno/dnt@0.41.3", - "jsr:@deno/graph@^0.73.1": "jsr:@deno/graph@0.73.1", - "jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0", - "jsr:@std/assert@^0.226.0": "jsr:@std/assert@0.226.0", - "jsr:@std/bytes@^0.223.0": "jsr:@std/bytes@0.223.0", - "jsr:@std/fmt@1": "jsr:@std/fmt@1.0.2", - "jsr:@std/fmt@^0.223": "jsr:@std/fmt@0.223.0", - "jsr:@std/fs@1": "jsr:@std/fs@1.0.4", - "jsr:@std/fs@^0.223": "jsr:@std/fs@0.223.0", - "jsr:@std/fs@^0.229.3": "jsr:@std/fs@0.229.3", - "jsr:@std/io@^0.223": "jsr:@std/io@0.223.0", - "jsr:@std/path": "jsr:@std/path@1.0.6", - "jsr:@std/path@1": "jsr:@std/path@1.0.6", - "jsr:@std/path@1.0.0-rc.1": "jsr:@std/path@1.0.0-rc.1", - "jsr:@std/path@^0.223": "jsr:@std/path@0.223.0", - "jsr:@std/path@^0.225.2": "jsr:@std/path@0.225.2", - "jsr:@std/path@^1.0.6": "jsr:@std/path@1.0.6", - "jsr:@ts-morph/bootstrap@^0.24.0": "jsr:@ts-morph/bootstrap@0.24.0", - "jsr:@ts-morph/common@^0.24.0": "jsr:@ts-morph/common@0.24.0", - "npm:@types/node": "npm:@types/node@18.16.19", - "npm:jsonpath-plus@10.0.0": "npm:jsonpath-plus@10.0.0_jsep@1.3.9", - "npm:lodash-es@4.17.21": "npm:lodash-es@4.17.21" + "jsr:@std/assert": "jsr:@std/assert@1.0.3", + "jsr:@std/assert@^1.0.3": "jsr:@std/assert@1.0.3", + "jsr:@std/expect": "jsr:@std/expect@1.0.1", + "jsr:@std/internal@^1.0.2": "jsr:@std/internal@1.0.2", + "jsr:@std/testing": "jsr:@std/testing@1.0.1", + "npm:@types/node": "npm:@types/node@18.16.19" }, "jsr": { - "@david/code-block-writer@13.0.3": { - "integrity": "f98c77d320f5957899a61bfb7a9bead7c6d83ad1515daee92dbacc861e13bb7f" - }, - "@deno/cache-dir@0.10.3": { - "integrity": "eb022f84ecc49c91d9d98131c6e6b118ff63a29e343624d058646b9d50404776", + "@std/assert@1.0.3": { + "integrity": "b0d03ce1ced880df67132eea140623010d415848df66f6aa5df76507ca7c26d8", "dependencies": [ - "jsr:@deno/graph@^0.73.1", - "jsr:@std/fmt@^0.223", - "jsr:@std/fs@^0.223", - "jsr:@std/io@^0.223", - "jsr:@std/path@^0.223" + "jsr:@std/internal@^1.0.2" ] }, - "@deno/dnt@0.41.3": { - "integrity": "b2ef2c8a5111eef86cb5bfcae103d6a2938e8e649e2461634a7befb7fc59d6d2", + "@std/expect@1.0.1": { + "integrity": "44075d9c2cb701ddfc4d5a260da2fb26ba3cfd94c45d3a0359f63cabbf85a058", "dependencies": [ - "jsr:@david/code-block-writer@^13.0.2", - "jsr:@deno/cache-dir@^0.10.3", - "jsr:@std/fmt@1", - "jsr:@std/fs@1", - "jsr:@std/path@1", - "jsr:@ts-morph/bootstrap@^0.24.0" + "jsr:@std/assert@^1.0.3", + "jsr:@std/internal@^1.0.2" ] }, - "@deno/graph@0.73.1": { - "integrity": "cd69639d2709d479037d5ce191a422eabe8d71bb68b0098344f6b07411c84d41" - }, - "@std/assert@0.223.0": { - "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" - }, - "@std/assert@0.226.0": { - "integrity": "0dfb5f7c7723c18cec118e080fec76ce15b4c31154b15ad2bd74822603ef75b3" - }, - "@std/bytes@0.223.0": { - "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" - }, - "@std/fmt@0.223.0": { - "integrity": "6deb37794127dfc7d7bded2586b9fc6f5d50e62a8134846608baf71ffc1a5208" - }, - "@std/fmt@1.0.2": { - "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7" - }, - "@std/fs@0.223.0": { - "integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c" - }, - "@std/fs@0.229.3": { - "integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb", - "dependencies": [ - "jsr:@std/path@1.0.0-rc.1" - ] - }, - "@std/fs@1.0.4": { - "integrity": "2907d32d8d1d9e540588fd5fe0ec21ee638134bd51df327ad4e443aaef07123c", - "dependencies": [ - "jsr:@std/path@^1.0.6" - ] - }, - "@std/io@0.223.0": { - "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", - "dependencies": [ - "jsr:@std/assert@^0.223.0", - "jsr:@std/bytes@^0.223.0" - ] - }, - "@std/path@0.223.0": { - "integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989", - "dependencies": [ - "jsr:@std/assert@^0.223.0" - ] + "@std/internal@1.0.2": { + "integrity": "f4cabe2021352e8bfc24e6569700df87bf070914fc38d4b23eddd20108ac4495" }, - "@std/path@0.225.2": { - "integrity": "0f2db41d36b50ef048dcb0399aac720a5348638dd3cb5bf80685bf2a745aa506", - "dependencies": [ - "jsr:@std/assert@^0.226.0" - ] - }, - "@std/path@1.0.0-rc.1": { - "integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6" - }, - "@std/path@1.0.6": { - "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" - }, - "@ts-morph/bootstrap@0.24.0": { - "integrity": "a826a2ef7fa8a7c3f1042df2c034d20744d94da2ee32bf29275bcd4dffd3c060", - "dependencies": [ - "jsr:@ts-morph/common@^0.24.0" - ] - }, - "@ts-morph/common@0.24.0": { - "integrity": "12b625b8e562446ba658cdbe9ad77774b4bd96b992ae8bd34c60dbf24d06c1f3", - "dependencies": [ - "jsr:@std/fs@^0.229.3", - "jsr:@std/path@^0.225.2" - ] + "@std/testing@1.0.1": { + "integrity": "9c25841137ee818933e1722091bb9ed5fdc251c35e84c97979a52196bdb6c5c3" } }, "npm": { - "@jsep-plugin/assignment@1.2.1_jsep@1.3.9": { - "integrity": "sha512-gaHqbubTi29aZpVbBlECRpmdia+L5/lh2BwtIJTmtxdbecEyyX/ejAOg7eQDGNvGOUmPY7Z2Yxdy9ioyH/VJeA==", - "dependencies": { - "jsep": "jsep@1.3.9" - } - }, - "@jsep-plugin/regex@1.0.3_jsep@1.3.9": { - "integrity": "sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==", - "dependencies": { - "jsep": "jsep@1.3.9" - } - }, "@types/node@18.16.19": { "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", "dependencies": {} - }, - "jsep@1.3.9": { - "integrity": "sha512-i1rBX5N7VPl0eYb6+mHNp52sEuaS2Wi8CDYx1X5sn9naevL78+265XJqy1qENEk7mRKwS06NHpUqiBwR7qeodw==", - "dependencies": {} - }, - "jsonpath-plus@10.0.0_jsep@1.3.9": { - "integrity": "sha512-v7j76HGp/ibKlXYeZ7UrfCLSNDaBWuJMA0GaMjA4sZJtCtY89qgPyToDDcl2zdeHh4B5q/B3g2pQdW76fOg/dA==", - "dependencies": { - "@jsep-plugin/assignment": "@jsep-plugin/assignment@1.2.1_jsep@1.3.9", - "@jsep-plugin/regex": "@jsep-plugin/regex@1.0.3_jsep@1.3.9", - "jsep": "jsep@1.3.9" - } - }, - "lodash-es@4.17.21": { - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "dependencies": {} } } }, + "redirects": { + "https://esm.sh/v135/@types/lodash-es@~4.17/index.d.ts": "https://esm.sh/v135/@types/lodash-es@4.17.12/index.d.ts", + "https://esm.sh/v135/@types/lodash@4.14.202/index": "https://esm.sh/v135/@types/lodash@4.14.202/index~.d.ts" + }, "remote": {} } diff --git a/examples/authentication.ts b/examples/authentication.ts index 589bef8..c39ef71 100644 --- a/examples/authentication.ts +++ b/examples/authentication.ts @@ -43,10 +43,9 @@ auth.addEventListener("requires-login", (event) => { resolve(auth_flow); }); -muse.get_library() - .then((data) => { - return Deno.writeTextFile( - "store/library.json", - JSON.stringify(data, null, 2), - ); - }); +const data = await muse.get_home(); + +await Deno.writeTextFile( + "store/home.json", + JSON.stringify(data, null, 2), +); diff --git a/src/mixins2/browse/home.ts b/src/mixins2/browse/home.ts new file mode 100644 index 0000000..5954f4d --- /dev/null +++ b/src/mixins2/browse/home.ts @@ -0,0 +1,13 @@ +import { request_json } from "../../mixins/_request.ts"; + +import { parse_single_column_browse_results_renderer } from "../../parsers2/tabs/mod.ts"; + +export async function get_home() { + const json = await request_json("browse", { + data: { browseId: "FEmusic_home" }, + }); + + const tab = parse_single_column_browse_results_renderer(json.contents).tab; + + return tab; +} diff --git a/src/mixins2/browse/mod.ts b/src/mixins2/browse/mod.ts new file mode 100644 index 0000000..a142b52 --- /dev/null +++ b/src/mixins2/browse/mod.ts @@ -0,0 +1 @@ +export * from "./home.ts"; diff --git a/src/mixins2/mod.ts b/src/mixins2/mod.ts new file mode 100644 index 0000000..950ad87 --- /dev/null +++ b/src/mixins2/mod.ts @@ -0,0 +1 @@ +export * from "./browse/mod.ts"; diff --git a/src/mod.ts b/src/mod.ts index bdaf0b7..f6daba0 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -5,12 +5,4 @@ export * from "./errors.ts"; export * from "./request.ts"; export * from "./auth.ts"; -export * from "./mixins/browsing.ts"; -export * from "./mixins/explore.ts"; -export * from "./mixins/library.ts"; -export * from "./mixins/playlist.ts"; -export * from "./mixins/queue.ts"; -export * from "./mixins/search.ts"; -export * from "./mixins/uploads.ts"; - -export type * from "./mixins/utils.ts"; +export * from "./mixins2/mod.ts"; diff --git a/src/parsers2/endpoint/endpoints/browse.test.ts b/src/parsers2/endpoint/endpoints/browse.test.ts new file mode 100644 index 0000000..dca84bf --- /dev/null +++ b/src/parsers2/endpoint/endpoints/browse.test.ts @@ -0,0 +1,23 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parseBrowseEndpoint } from "./browse.ts"; +import { EndpointType } from "../parse.ts"; + +export const sampleBrowseEndpoint = { + browseId: "SOME_BROWSE_ID", +}; + +describe("browseEndpoint", () => { + const parsed = parseBrowseEndpoint(sampleBrowseEndpoint); + + it("has correct type", () => { + expect(parsed.type).toBe(EndpointType.BROWSE); + }); + + it("parses ID", () => { + expect( + parseBrowseEndpoint({ browseId: "SOME_BROWSE_ID" }).id, + ).toBe("SOME_BROWSE_ID"); + }); +}); diff --git a/src/parsers2/endpoint/endpoints/browse.ts b/src/parsers2/endpoint/endpoints/browse.ts new file mode 100644 index 0000000..98bf886 --- /dev/null +++ b/src/parsers2/endpoint/endpoints/browse.ts @@ -0,0 +1,27 @@ +import { RawJSON } from "../../types.d.ts"; + +import { EndpointType } from "../mod.ts"; +import { BaseEndpoint } from "../types.d.ts"; + +/** + * An endpoint the user can navigate to + */ +export interface BrowseEndpoint extends BaseEndpoint { + type: EndpointType.BROWSE; + /** + * The ID to use while navigating + */ + id: string; +} + +/** + * Parses a browse endpoint + * @param content JSON + * @returns + */ +export function parseBrowseEndpoint(content: RawJSON): BrowseEndpoint { + return { + type: EndpointType.BROWSE, + id: content.browseId, + }; +} diff --git a/src/parsers2/endpoint/endpoints/mod.ts b/src/parsers2/endpoint/endpoints/mod.ts new file mode 100644 index 0000000..3b66a3c --- /dev/null +++ b/src/parsers2/endpoint/endpoints/mod.ts @@ -0,0 +1 @@ +export * from "./browse.ts"; diff --git a/src/parsers2/endpoint/mod.ts b/src/parsers2/endpoint/mod.ts new file mode 100644 index 0000000..796d5c2 --- /dev/null +++ b/src/parsers2/endpoint/mod.ts @@ -0,0 +1,3 @@ +export * from "./endpoints/mod.ts"; +export * from "./parse.ts"; +export * from "./types.d.ts"; diff --git a/src/parsers2/endpoint/parse.test.ts b/src/parsers2/endpoint/parse.test.ts new file mode 100644 index 0000000..7f96408 --- /dev/null +++ b/src/parsers2/endpoint/parse.test.ts @@ -0,0 +1,18 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parseEndpoint } from "./parse.ts"; + +import { sampleBrowseEndpoint } from "./endpoints/browse.test.ts"; + +describe("parseEndpoint", () => { + it("has `endpoints` key", () => { + expect(parseEndpoint({})).toHaveProperty("endpoints"); + }); + + it("parses the browseEndpoint", () => { + expect( + parseEndpoint({ browseEndpoint: sampleBrowseEndpoint }).endpoints, + ).toHaveProperty("browse"); + }); +}); diff --git a/src/parsers2/endpoint/parse.ts b/src/parsers2/endpoint/parse.ts new file mode 100644 index 0000000..d416317 --- /dev/null +++ b/src/parsers2/endpoint/parse.ts @@ -0,0 +1,42 @@ +import { RawJSON } from "../types.d.ts"; +import { BaseEndpointMap, KnownEndpointName } from "./types.d.ts"; + +import { parseBrowseEndpoint } from "./endpoints/browse.ts"; +import { KnownEndpoint } from "./types.d.ts"; + +export const _endpointMap = { + "browse": parseBrowseEndpoint, +} satisfies BaseEndpointMap; + +export enum EndpointType { + BROWSE, +} + +type EndpointResult = ReturnType< + typeof _endpointMap[T] +>; + +type ParsedEndpoints = { + [endpoint in KnownEndpointName]: EndpointResult<"browse">; +}; + +export interface ParseEndpointResult { + endpoints: Partial; +} + +export function parseEndpoint(content: RawJSON): ParseEndpointResult { + const endpointEntries = Object.entries(_endpointMap) + .map(([name, parser]) => { + const endpoint = content[name + "Endpoint"]; + if (!endpoint) return [name, null] as const; + return [name, parser(endpoint)] as const; + }) + .filter(([, parsed]) => !!parsed); + + const endpoints = Object.fromEntries(endpointEntries) as Record< + KnownEndpointName, + KnownEndpoint + >; + + return { endpoints }; +} diff --git a/src/parsers2/endpoint/types.d.ts b/src/parsers2/endpoint/types.d.ts new file mode 100644 index 0000000..ac2fa0e --- /dev/null +++ b/src/parsers2/endpoint/types.d.ts @@ -0,0 +1,17 @@ +import type { Parser } from "../types.d.ts"; +import { _endpointMap } from "./parse.ts"; + +import { EndpointType } from "./mod.ts"; +import type { BrowseEndpoint } from "./endpoints/browse.ts"; + +type EndpointMap = typeof _endpointMap; + +export type KnownEndpointName = keyof EndpointMap; + +export type BaseEndpointMap = Record>; + +export type KnownEndpoint = BrowseEndpoint; + +export interface BaseEndpoint { + type: EndpointType; +} diff --git a/src/parsers2/tabs/mod.ts b/src/parsers2/tabs/mod.ts new file mode 100644 index 0000000..805136a --- /dev/null +++ b/src/parsers2/tabs/mod.ts @@ -0,0 +1,2 @@ +export * from "./single_column.ts"; +export * from "./tab_renderer.ts"; diff --git a/src/parsers2/tabs/single_column.test.ts b/src/parsers2/tabs/single_column.test.ts new file mode 100644 index 0000000..d9ce66e --- /dev/null +++ b/src/parsers2/tabs/single_column.test.ts @@ -0,0 +1,19 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_single_column_browse_results_renderer } from "./single_column.ts"; +import { sampleTab } from "./tab_renderer.test.ts"; + +describe("parse_single_column_browse_results_renderer", () => { + const result = parse_single_column_browse_results_renderer({ + singleColumnBrowseResultsRenderer: { + tabs: [ + sampleTab, + ], + }, + }); + + it("parses tab", () => { + expect(result).toHaveProperty("tab"); + }); +}); diff --git a/src/parsers2/tabs/single_column.ts b/src/parsers2/tabs/single_column.ts new file mode 100644 index 0000000..8252ffc --- /dev/null +++ b/src/parsers2/tabs/single_column.ts @@ -0,0 +1,19 @@ +import { j } from "../../util.ts"; +import { RawJSON } from "../types.d.ts"; + +import { ParseTabRendererResult } from "./tab_renderer.ts"; +import { parse_tab_renderer } from "./tab_renderer.ts"; + +export interface ParseSingleColumnBrowseResultsRendererResult { + tab: ParseTabRendererResult; +} + +export function parse_single_column_browse_results_renderer( + content: RawJSON, +): ParseSingleColumnBrowseResultsRendererResult { + const tab = j(content, "singleColumnBrowseResultsRenderer", "tabs", "0"); + + return { + tab: parse_tab_renderer(tab), + }; +} diff --git a/src/parsers2/tabs/tab_renderer.test.ts b/src/parsers2/tabs/tab_renderer.test.ts new file mode 100644 index 0000000..a707f64 --- /dev/null +++ b/src/parsers2/tabs/tab_renderer.test.ts @@ -0,0 +1,32 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_tab_renderer } from "./tab_renderer.ts"; + +export const sampleTab = { + tabRenderer: { + endpoint: { + browseEndpoint: { + browseId: "PSCBR_ID", + }, + }, + title: "PSCBR Title", + content: "Hello", + }, +}; + +describe("parse_tab_renderer", () => { + const result = parse_tab_renderer(sampleTab); + + it("parses browseId", () => { + expect(result.browseId).toBe("PSCBR_ID"); + }); + + it("parses title", () => { + expect(result.title).toBe("PSCBR Title"); + }); + + it("parses content", () => { + expect(result.content).toBe("Hello"); + }); +}); diff --git a/src/parsers2/tabs/tab_renderer.ts b/src/parsers2/tabs/tab_renderer.ts new file mode 100644 index 0000000..e78e303 --- /dev/null +++ b/src/parsers2/tabs/tab_renderer.ts @@ -0,0 +1,33 @@ +import { assert, assertEquals } from "jsr:@std/assert"; + +import { RawJSON } from "../types.d.ts"; + +import { parseEndpoint } from "../endpoint/mod.ts"; + +export interface ParseTabRendererResult { + browseId: string; + title: string; + content: RawJSON; +} + +export function parse_tab_renderer(content: RawJSON): ParseTabRendererResult { + const tab = content.tabRenderer; + const browseEndpoint = parseEndpoint(tab.endpoint).endpoints.browse; + + assert( + browseEndpoint, + "singleColumnBrowseResultsRenderer must have a browseId", + ); + + const { title } = tab; + + // assertions + assertEquals(typeof title, "string", "Tab must have a title"); + assert(tab.content, "Tab must have content"); + + return { + browseId: browseEndpoint.id, + title, + content: tab.content, + }; +} diff --git a/src/parsers2/types.d.ts b/src/parsers2/types.d.ts new file mode 100644 index 0000000..65a2604 --- /dev/null +++ b/src/parsers2/types.d.ts @@ -0,0 +1,3 @@ +export type RawJSON = any; + +export type Parser = (content: RawJSON) => T; diff --git a/src/util.ts b/src/util.ts index f135774..3f271b4 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,7 +2,7 @@ import { JSONPath, JSONPathOptions } from "./deps.ts"; import { ERROR_CODE, MuseError } from "./errors.ts"; import { get_option } from "./setup.ts"; -type JSON = string | number | boolean | object | any[] | null; +type JPathJSON = JSONPathOptions["json"]; /** * Wait a given number of milliseconds, then resolve @@ -22,7 +22,7 @@ export const jom = ( path: string, resultType?: JSONPathOptions["resultType"], ): any => { - const result = JSONPath({ path, json: json as JSON, resultType }); + const result = JSONPath({ path, json: json as JPathJSON, resultType }); return result.length ? result : null; }; export const jo = ( @@ -32,7 +32,7 @@ export const jo = ( ): any => { const result = JSONPath({ path: [path, ...others].join("."), - json: json as JSON, + json: json as JPathJSON, }); return result.length ? result[0] : null; }; From cb91caca329cfa5ee5fb8454da43d5e018f64322 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 01:54:29 +0200 Subject: [PATCH 02/15] chore(parsers/endpoints): convert to lowercase --- src/parsers2/endpoint/endpoints/browse.test.ts | 17 +++++++++++++---- src/parsers2/endpoint/endpoints/browse.ts | 7 ++++++- src/parsers2/endpoint/parse.test.ts | 6 +++--- src/parsers2/endpoint/parse.ts | 6 +++--- src/parsers2/tabs/tab_renderer.ts | 4 ++-- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/parsers2/endpoint/endpoints/browse.test.ts b/src/parsers2/endpoint/endpoints/browse.test.ts index dca84bf..73659e0 100644 --- a/src/parsers2/endpoint/endpoints/browse.test.ts +++ b/src/parsers2/endpoint/endpoints/browse.test.ts @@ -1,7 +1,7 @@ import { expect } from "jsr:@std/expect"; import { describe, it } from "jsr:@std/testing/bdd"; -import { parseBrowseEndpoint } from "./browse.ts"; +import { parse_browse_endpoint } from "./browse.ts"; import { EndpointType } from "../parse.ts"; export const sampleBrowseEndpoint = { @@ -9,7 +9,7 @@ export const sampleBrowseEndpoint = { }; describe("browseEndpoint", () => { - const parsed = parseBrowseEndpoint(sampleBrowseEndpoint); + const parsed = parse_browse_endpoint(sampleBrowseEndpoint); it("has correct type", () => { expect(parsed.type).toBe(EndpointType.BROWSE); @@ -17,7 +17,16 @@ describe("browseEndpoint", () => { it("parses ID", () => { expect( - parseBrowseEndpoint({ browseId: "SOME_BROWSE_ID" }).id, - ).toBe("SOME_BROWSE_ID"); + parse_browse_endpoint(sampleBrowseEndpoint), + ).toHaveProperty("id", "SOME_BROWSE_ID"); + }); + + it("parses params", () => { + expect( + parse_browse_endpoint({ + browseId: "SOME_BROWSE_ID", + params: "SOME_PARAMS", + }), + ).toHaveProperty("params", "SOME_PARAMS"); }); }); diff --git a/src/parsers2/endpoint/endpoints/browse.ts b/src/parsers2/endpoint/endpoints/browse.ts index 98bf886..b505e53 100644 --- a/src/parsers2/endpoint/endpoints/browse.ts +++ b/src/parsers2/endpoint/endpoints/browse.ts @@ -12,6 +12,10 @@ export interface BrowseEndpoint extends BaseEndpoint { * The ID to use while navigating */ id: string; + /** + * Optional params, usually used to get more data of a specific kind + */ + params?: string; } /** @@ -19,9 +23,10 @@ export interface BrowseEndpoint extends BaseEndpoint { * @param content JSON * @returns */ -export function parseBrowseEndpoint(content: RawJSON): BrowseEndpoint { +export function parse_browse_endpoint(content: RawJSON): BrowseEndpoint { return { type: EndpointType.BROWSE, id: content.browseId, + params: content.params, }; } diff --git a/src/parsers2/endpoint/parse.test.ts b/src/parsers2/endpoint/parse.test.ts index 7f96408..71bee93 100644 --- a/src/parsers2/endpoint/parse.test.ts +++ b/src/parsers2/endpoint/parse.test.ts @@ -1,18 +1,18 @@ import { expect } from "jsr:@std/expect"; import { describe, it } from "jsr:@std/testing/bdd"; -import { parseEndpoint } from "./parse.ts"; +import { parse_endpoint } from "./parse.ts"; import { sampleBrowseEndpoint } from "./endpoints/browse.test.ts"; describe("parseEndpoint", () => { it("has `endpoints` key", () => { - expect(parseEndpoint({})).toHaveProperty("endpoints"); + expect(parse_endpoint({})).toHaveProperty("endpoints"); }); it("parses the browseEndpoint", () => { expect( - parseEndpoint({ browseEndpoint: sampleBrowseEndpoint }).endpoints, + parse_endpoint({ browseEndpoint: sampleBrowseEndpoint }).endpoints, ).toHaveProperty("browse"); }); }); diff --git a/src/parsers2/endpoint/parse.ts b/src/parsers2/endpoint/parse.ts index d416317..d341de3 100644 --- a/src/parsers2/endpoint/parse.ts +++ b/src/parsers2/endpoint/parse.ts @@ -1,11 +1,11 @@ import { RawJSON } from "../types.d.ts"; import { BaseEndpointMap, KnownEndpointName } from "./types.d.ts"; -import { parseBrowseEndpoint } from "./endpoints/browse.ts"; +import { parse_browse_endpoint } from "./endpoints/browse.ts"; import { KnownEndpoint } from "./types.d.ts"; export const _endpointMap = { - "browse": parseBrowseEndpoint, + "browse": parse_browse_endpoint, } satisfies BaseEndpointMap; export enum EndpointType { @@ -24,7 +24,7 @@ export interface ParseEndpointResult { endpoints: Partial; } -export function parseEndpoint(content: RawJSON): ParseEndpointResult { +export function parse_endpoint(content: RawJSON): ParseEndpointResult { const endpointEntries = Object.entries(_endpointMap) .map(([name, parser]) => { const endpoint = content[name + "Endpoint"]; diff --git a/src/parsers2/tabs/tab_renderer.ts b/src/parsers2/tabs/tab_renderer.ts index e78e303..bfb0d43 100644 --- a/src/parsers2/tabs/tab_renderer.ts +++ b/src/parsers2/tabs/tab_renderer.ts @@ -2,7 +2,7 @@ import { assert, assertEquals } from "jsr:@std/assert"; import { RawJSON } from "../types.d.ts"; -import { parseEndpoint } from "../endpoint/mod.ts"; +import { parse_endpoint } from "../endpoint/mod.ts"; export interface ParseTabRendererResult { browseId: string; @@ -12,7 +12,7 @@ export interface ParseTabRendererResult { export function parse_tab_renderer(content: RawJSON): ParseTabRendererResult { const tab = content.tabRenderer; - const browseEndpoint = parseEndpoint(tab.endpoint).endpoints.browse; + const browseEndpoint = parse_endpoint(tab.endpoint).endpoints.browse; assert( browseEndpoint, From 2dacfda5d791a0eef9f9096709bbf332af325ba3 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 01:55:04 +0200 Subject: [PATCH 03/15] feat(parsers/runs/text_runs_simple): new parser --- src/parsers2/runs/text_runs_simple.test.ts | 18 ++++++++++++++++++ src/parsers2/runs/text_runs_simple.ts | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/parsers2/runs/text_runs_simple.test.ts create mode 100644 src/parsers2/runs/text_runs_simple.ts diff --git a/src/parsers2/runs/text_runs_simple.test.ts b/src/parsers2/runs/text_runs_simple.test.ts new file mode 100644 index 0000000..42837b0 --- /dev/null +++ b/src/parsers2/runs/text_runs_simple.test.ts @@ -0,0 +1,18 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_text_runs_simple } from "./text_runs_simple.ts"; + +describe("parse_text_runs_simple", () => { + it("parses", () => { + expect( + parse_text_runs_simple({ + runs: [ + { + text: "Hello, World!", + }, + ], + }), + ).toBe("Hello, World!"); + }); +}); diff --git a/src/parsers2/runs/text_runs_simple.ts b/src/parsers2/runs/text_runs_simple.ts new file mode 100644 index 0000000..2059bfd --- /dev/null +++ b/src/parsers2/runs/text_runs_simple.ts @@ -0,0 +1,19 @@ +import { RawJSON } from "../types.d.ts"; + +/** + * Parses text runs into a simple string + * + * @example + * + * ```ts + * const text = parse_text_runs_simple({ + * runs: [ + * { text: "String" } + * ], + * }); + * expect(text).toBe("String"); + * ``` + */ +export function parse_text_runs_simple(content: RawJSON) { + return content.runs[0].text; +} From 73239f9c86c19870f50afbfebe2efea6f72a3d0a Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 01:55:22 +0200 Subject: [PATCH 04/15] feat(parsers/chip): new parser --- src/parsers2/chips/chip.test.ts | 59 +++++++++++++++++++++++++++++++++ src/parsers2/chips/chip.ts | 37 +++++++++++++++++++++ src/parsers2/chips/mod.ts | 1 + 3 files changed, 97 insertions(+) create mode 100644 src/parsers2/chips/chip.test.ts create mode 100644 src/parsers2/chips/chip.ts create mode 100644 src/parsers2/chips/mod.ts diff --git a/src/parsers2/chips/chip.test.ts b/src/parsers2/chips/chip.test.ts new file mode 100644 index 0000000..a8f9b4b --- /dev/null +++ b/src/parsers2/chips/chip.test.ts @@ -0,0 +1,59 @@ +import { assert } from "jsr:@std/assert/assert"; +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { EndpointType } from "../endpoint/mod.ts"; +import { parse_chip } from "./chip.ts"; +import { RawJSON } from "../types.d.ts"; + +describe("parse_text_runs_simple", () => { + const text = { runs: [{ text: "Chip Title" }] }; + + function getChip(overrides: RawJSON) { + return { + chipCloudChipRenderer: { + text, + isSelected: false, + navigationEndpoint: {}, + ...overrides, + }, + }; + } + + describe("parses selected", () => { + it("parses selected false", () => { + expect( + parse_chip(getChip({ isSelected: false })).selected, + ).toBe(false); + }); + + it("parses selected false", () => { + expect( + parse_chip(getChip({ isSelected: true })).selected, + ).toBe(true); + }); + }); + + it("parses text", () => { + expect( + parse_chip(getChip({ text: { runs: [{ text: "Hello, World!" }] } })).text, + ).toBe("Hello, World!"); + }); + + it("parses navigation", () => { + const chip = parse_chip(getChip({ + navigationEndpoint: { + browseEndpoint: { + browseId: "BROWSE_ID", + params: "PARAMS", + }, + }, + })); + + assert(chip.navigation.browse, "must parse a browse endpoint"); + + expect( + chip.navigation.browse.type, + ).toBe(EndpointType.BROWSE); + }); +}); diff --git a/src/parsers2/chips/chip.ts b/src/parsers2/chips/chip.ts new file mode 100644 index 0000000..3ebaf0c --- /dev/null +++ b/src/parsers2/chips/chip.ts @@ -0,0 +1,37 @@ +import { assertEquals } from "jsr:@std/assert"; + +import { RawJSON } from "../types.d.ts"; + +import { parse_endpoint, ParseEndpointResult } from "../endpoint/mod.ts"; +import { parse_text_runs_simple } from "../runs/text_runs_simple.ts"; + +export interface ParseChipResult { + /** + * The text associated with this chip + */ + text: string; + /** + * Whether this chip is selected or not + */ + selected: boolean; + /** + * The endpoint to navigate to when this chip is clicked + */ + navigation: ParseEndpointResult["endpoints"]; +} + +/** + * Parses a chip, usually part of a chipCloud + */ +export function parse_chip(content: RawJSON): ParseChipResult { + const chip = content.chipCloudChipRenderer; + const selected = chip.isSelected; + + assertEquals(typeof selected, "boolean", "the chip must be selected or not"); + + return { + text: parse_text_runs_simple(chip.text), + selected, + navigation: parse_endpoint(chip.navigationEndpoint).endpoints, + }; +} diff --git a/src/parsers2/chips/mod.ts b/src/parsers2/chips/mod.ts new file mode 100644 index 0000000..f719e8d --- /dev/null +++ b/src/parsers2/chips/mod.ts @@ -0,0 +1 @@ +export * from "./chip.ts"; From ab9c86f188781975f32bb877845c915b4b7903da Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 02:27:07 +0200 Subject: [PATCH 05/15] feat(parsers/chips/chip_list): new parser --- src/parsers2/chips/_chip_list.test.ts | 26 ++++++++++++++++++++++++++ src/parsers2/chips/_chip_list.ts | 7 +++++++ src/parsers2/chips/chip.ts | 5 +++++ 3 files changed, 38 insertions(+) create mode 100644 src/parsers2/chips/_chip_list.test.ts create mode 100644 src/parsers2/chips/_chip_list.ts diff --git a/src/parsers2/chips/_chip_list.test.ts b/src/parsers2/chips/_chip_list.test.ts new file mode 100644 index 0000000..5b2493b --- /dev/null +++ b/src/parsers2/chips/_chip_list.test.ts @@ -0,0 +1,26 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { _parse_chips_list } from "./_chip_list.ts"; + +describe("_parse_chips_list", () => { + const stub_parser = (t: string) => t; + + it("parses chips", () => { + expect( + _parse_chips_list( + { chipCloudRenderer: { chips: ["hello"] } }, + stub_parser, + )[0], + ).toBe("hello"); + }); + + it("produces expected amount of chips", () => { + expect( + _parse_chips_list( + { chipCloudRenderer: { chips: ["hello", "world", "!"] } }, + stub_parser, + ), + ).toHaveLength(3); + }); +}); diff --git a/src/parsers2/chips/_chip_list.ts b/src/parsers2/chips/_chip_list.ts new file mode 100644 index 0000000..11173ca --- /dev/null +++ b/src/parsers2/chips/_chip_list.ts @@ -0,0 +1,7 @@ +import { Parser, RawJSON } from "../types.d.ts"; + +export function _parse_chips_list(content: RawJSON, parser: Parser): T[] { + const chips = content.chipCloudRenderer.chips; + + return chips.map(parser); +} diff --git a/src/parsers2/chips/chip.ts b/src/parsers2/chips/chip.ts index 3ebaf0c..d757946 100644 --- a/src/parsers2/chips/chip.ts +++ b/src/parsers2/chips/chip.ts @@ -2,6 +2,7 @@ import { assertEquals } from "jsr:@std/assert"; import { RawJSON } from "../types.d.ts"; +import { _parse_chips_list } from "./_chip_list.ts"; import { parse_endpoint, ParseEndpointResult } from "../endpoint/mod.ts"; import { parse_text_runs_simple } from "../runs/text_runs_simple.ts"; @@ -35,3 +36,7 @@ export function parse_chip(content: RawJSON): ParseChipResult { navigation: parse_endpoint(chip.navigationEndpoint).endpoints, }; } + +export function parse_chips(content: RawJSON) { + return _parse_chips_list(content, parse_chip); +} From d9a14d09c807e7ae4d9c308d9d50f02d21a73f7b Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 02:27:23 +0200 Subject: [PATCH 06/15] feat(parsers/chips/mood): new parser --- src/parsers2/chips/mod.ts | 1 + src/parsers2/chips/mooc.test.ts | 31 +++++++++++++++++++++++++++++++ src/parsers2/chips/mood.ts | 27 +++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 src/parsers2/chips/mooc.test.ts create mode 100644 src/parsers2/chips/mood.ts diff --git a/src/parsers2/chips/mod.ts b/src/parsers2/chips/mod.ts index f719e8d..c697a66 100644 --- a/src/parsers2/chips/mod.ts +++ b/src/parsers2/chips/mod.ts @@ -1 +1,2 @@ export * from "./chip.ts"; +export * from "./mood.ts"; diff --git a/src/parsers2/chips/mooc.test.ts b/src/parsers2/chips/mooc.test.ts new file mode 100644 index 0000000..61d3654 --- /dev/null +++ b/src/parsers2/chips/mooc.test.ts @@ -0,0 +1,31 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_mood_chip } from "./mood.ts"; + +describe("parse_mood_chip", () => { + const parsed = parse_mood_chip({ + chipCloudChipRenderer: { + text: { runs: [{ text: "Mood Chip" }] }, + isSelected: false, + navigationEndpoint: { + browseEndpoint: { + browseId: "MOOD_BROWSE_ID", + params: "MOOD_PARAMS", + }, + }, + }, + }); + + it("parses title", () => { + expect(parsed.text).toBe("Mood Chip") + }); + + it("parses selected", () => { + expect(parsed.selected).toBe(false) + }); + + it("parses params", () => { + expect(parsed.params).toBe("MOOD_PARAMS") + }); +}); diff --git a/src/parsers2/chips/mood.ts b/src/parsers2/chips/mood.ts new file mode 100644 index 0000000..4ee9532 --- /dev/null +++ b/src/parsers2/chips/mood.ts @@ -0,0 +1,27 @@ +import { assert } from "jsr:@std/assert/assert"; +import { RawJSON } from "../types.d.ts"; + +import { parse_chip } from "./chip.ts"; +import { _parse_chips_list } from "./_chip_list.ts"; + +export interface ParseMoodChipResult { + text: string; + selected: boolean; + params: string; +} + +export function parse_mood_chip(content: RawJSON): ParseMoodChipResult { + const chip = parse_chip(content); + + assert(chip.navigation.browse?.params, "mood chip must have browseId"); + + return { + text: chip.text, + selected: chip.selected, + params: chip.navigation.browse.params, + }; +} + +export function parse_mood_chips(content: RawJSON) { + return _parse_chips_list(content, parse_mood_chip); +} From 87183ea8d3f0b9f97a8228824c41db46645e48e0 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 02:33:33 +0200 Subject: [PATCH 07/15] feat(parsers/continuation): new parser --- .../continuations/continuations.test.ts | 25 ++++++++++++++++ src/parsers2/continuations/continuations.ts | 29 +++++++++++++++++++ src/parsers2/continuations/moc.ts | 1 + 3 files changed, 55 insertions(+) create mode 100644 src/parsers2/continuations/continuations.test.ts create mode 100644 src/parsers2/continuations/continuations.ts create mode 100644 src/parsers2/continuations/moc.ts diff --git a/src/parsers2/continuations/continuations.test.ts b/src/parsers2/continuations/continuations.test.ts new file mode 100644 index 0000000..615b56a --- /dev/null +++ b/src/parsers2/continuations/continuations.test.ts @@ -0,0 +1,25 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_continuations } from "./continuations.ts"; + +describe("parse_continuations", () => { + const parsed = parse_continuations({ + continuations: [ + { nextContinuationData: { continuation: "NEXT_CONTINUATION" } }, + { reloadContinuationData: { continuation: "RELOAD_CONTINUATION" } }, + ], + }); + + it("parses next continuation", () => { + expect(parsed.nextContinuation).toBe("NEXT_CONTINUATION"); + }); + + it("parses reload continuation", () => { + expect(parsed.reloadContinuation).toBe("RELOAD_CONTINUATION"); + }); + + it("does not parse undefined continuation", () => { + expect(parsed.undefinedContinuation).toBeUndefined(); + }); +}); diff --git a/src/parsers2/continuations/continuations.ts b/src/parsers2/continuations/continuations.ts new file mode 100644 index 0000000..8c652b1 --- /dev/null +++ b/src/parsers2/continuations/continuations.ts @@ -0,0 +1,29 @@ +import { RawJSON } from "../types.d.ts"; + +export type ParseContinuationsResult = Record; + +/** + * Parse an object with continuations + */ +export function parse_continuations(content: RawJSON): ParseContinuationsResult { + const continuations: ParseContinuationsResult = {}; + + for (const continuation of content.continuations) { + /** + * A continuation has the following syntax: + * + * { + * "nextContinuationData": { + * "continuation": "..." + * } + * } + */ + const name = Object.keys(continuation)[0]; + const value = continuation[name].continuation; + + if (!name.endsWith("Data")) continue; + continuations[name.slice(undefined, -4)] = value; + } + + return continuations; +} diff --git a/src/parsers2/continuations/moc.ts b/src/parsers2/continuations/moc.ts new file mode 100644 index 0000000..3d49d3d --- /dev/null +++ b/src/parsers2/continuations/moc.ts @@ -0,0 +1 @@ +export * from "./continuations.ts"; From 8c275d9fb3db1459cf9cdac8582dbf7d5341def3 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 02:36:29 +0200 Subject: [PATCH 08/15] feat(parsers/list/section): new parser --- src/parsers2/list/mod.ts | 1 + src/parsers2/list/section.test.ts | 28 ++++++++++++++++++++++++++++ src/parsers2/list/section.ts | 20 ++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/parsers2/list/mod.ts create mode 100644 src/parsers2/list/section.test.ts create mode 100644 src/parsers2/list/section.ts diff --git a/src/parsers2/list/mod.ts b/src/parsers2/list/mod.ts new file mode 100644 index 0000000..cd26bef --- /dev/null +++ b/src/parsers2/list/mod.ts @@ -0,0 +1 @@ +export * from "./section.ts"; diff --git a/src/parsers2/list/section.test.ts b/src/parsers2/list/section.test.ts new file mode 100644 index 0000000..3240b47 --- /dev/null +++ b/src/parsers2/list/section.test.ts @@ -0,0 +1,28 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_section_list } from "./section.ts"; + +describe("parse_section_list", () => { + const parsed = parse_section_list({ + sectionListRenderer: { + header: "SECTION_LIST_HEADER", + contents: "SECTION_LIST_CONTENTS", + continuations: [ + { nextContinuationData: { continuation: "SECTION_LIST_CONTINUATION" } }, + ], + }, + }); + + it("parses header", () => { + expect(parsed.header).toBe("SECTION_LIST_HEADER"); + }); + + it("parses contents", () => { + expect(parsed.contents).toBe("SECTION_LIST_CONTENTS"); + }); + + it("parses next continuation", () => { + expect(parsed.nextContinuation).toBe("SECTION_LIST_CONTINUATION"); + }); +}); diff --git a/src/parsers2/list/section.ts b/src/parsers2/list/section.ts new file mode 100644 index 0000000..3bebcb9 --- /dev/null +++ b/src/parsers2/list/section.ts @@ -0,0 +1,20 @@ +import { RawJSON } from "../types.d.ts"; + +import { parse_continuations } from "../continuations/moc.ts"; + +export interface ParseSectionListResult { + header: RawJSON; + contents: RawJSON[]; + nextContinuation: string | null; +} + +export function parse_section_list(content: RawJSON): ParseSectionListResult { + const section_list = content.sectionListRenderer; + const nextContinuation = parse_continuations(section_list).nextContinuation; + + return { + header: section_list.header, + contents: section_list.contents, + nextContinuation: nextContinuation ?? null, + }; +} From c9ac62d9d92af1f6465ba6d8110237009788d648 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 02:48:59 +0200 Subject: [PATCH 09/15] feat(parsers/thumbnail: new parsers: background, thumbnails --- src/parsers2/thumbnail/background.test.ts | 23 +++++++++++++++++ src/parsers2/thumbnail/background.ts | 14 +++++++++++ src/parsers2/thumbnail/mod.ts | 3 +++ src/parsers2/thumbnail/thumbnails.test.ts | 30 +++++++++++++++++++++++ src/parsers2/thumbnail/thumbnails.ts | 9 +++++++ src/parsers2/thumbnail/types.d.ts | 5 ++++ 6 files changed, 84 insertions(+) create mode 100644 src/parsers2/thumbnail/background.test.ts create mode 100644 src/parsers2/thumbnail/background.ts create mode 100644 src/parsers2/thumbnail/mod.ts create mode 100644 src/parsers2/thumbnail/thumbnails.test.ts create mode 100644 src/parsers2/thumbnail/thumbnails.ts create mode 100644 src/parsers2/thumbnail/types.d.ts diff --git a/src/parsers2/thumbnail/background.test.ts b/src/parsers2/thumbnail/background.test.ts new file mode 100644 index 0000000..d980249 --- /dev/null +++ b/src/parsers2/thumbnail/background.test.ts @@ -0,0 +1,23 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { sampleThumbnail } from "./thumbnails.test.ts"; + +import { parse_background } from "./background.ts"; + +describe("parse_background", () => { + it("parses thumbnails", () => { + const thumbnails = [sampleThumbnail]; + expect( + parse_background({ + background: { + musicThumbnailRenderer: { + thumbnail: { + thumbnails, + }, + }, + }, + }).thumbnails, + ).toEqual(thumbnails); + }); +}); diff --git a/src/parsers2/thumbnail/background.ts b/src/parsers2/thumbnail/background.ts new file mode 100644 index 0000000..6332af8 --- /dev/null +++ b/src/parsers2/thumbnail/background.ts @@ -0,0 +1,14 @@ +import { RawJSON } from "../types.d.ts"; +import { parse_thumbnails } from "./thumbnails.ts"; + +import { Thumbnail } from "./types.d.ts"; + +export interface ParseBackgroundResult { + thumbnails: Thumbnail[]; +} + +export function parse_background(content: RawJSON): ParseBackgroundResult { + return { + thumbnails: parse_thumbnails(content.background.musicThumbnailRenderer), + }; +} diff --git a/src/parsers2/thumbnail/mod.ts b/src/parsers2/thumbnail/mod.ts new file mode 100644 index 0000000..57a8efd --- /dev/null +++ b/src/parsers2/thumbnail/mod.ts @@ -0,0 +1,3 @@ +export * from "./types.d.ts"; +export * from "./background.ts"; +export * from "./thumbnails.ts"; diff --git a/src/parsers2/thumbnail/thumbnails.test.ts b/src/parsers2/thumbnail/thumbnails.test.ts new file mode 100644 index 0000000..ad049df --- /dev/null +++ b/src/parsers2/thumbnail/thumbnails.test.ts @@ -0,0 +1,30 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { parse_thumbnails } from "./thumbnails.ts"; + +export const sampleThumbnail = { + url: "THUMBNAIL_URL", + width: 200, + height: 100, +}; + +describe("parse_thumbnails", () => { + it("parses a thumbnail", () => { + expect( + parse_thumbnails({ + thumbnail: { + thumbnails: [sampleThumbnail], + }, + })[0], + ).toBeTruthy(); + }); + + it("parses a number of thumbnails", () => { + expect(parse_thumbnails({ + thumbnail: { + thumbnails: [sampleThumbnail, sampleThumbnail, sampleThumbnail], + }, + })).toHaveLength(3); + }); +}); diff --git a/src/parsers2/thumbnail/thumbnails.ts b/src/parsers2/thumbnail/thumbnails.ts new file mode 100644 index 0000000..b8fad5d --- /dev/null +++ b/src/parsers2/thumbnail/thumbnails.ts @@ -0,0 +1,9 @@ +import { RawJSON } from "../types.d.ts"; + +import { Thumbnail } from "./types.d.ts"; + +export type ParseThumbnailsResult = Thumbnail[]; + +export function parse_thumbnails(content: RawJSON): ParseThumbnailsResult { + return content.thumbnail.thumbnails; +} diff --git a/src/parsers2/thumbnail/types.d.ts b/src/parsers2/thumbnail/types.d.ts new file mode 100644 index 0000000..260dbc6 --- /dev/null +++ b/src/parsers2/thumbnail/types.d.ts @@ -0,0 +1,5 @@ +export interface Thumbnail { + url: string; + width: string; + height: number; +} From aa2d4b8d0696e8367e4d655a5b422e5b65295f89 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Fri, 30 Aug 2024 02:52:10 +0200 Subject: [PATCH 10/15] feat(mixins2/browse/home): implement `moods`, `thumbnails`, `continuations` and stub out `content` --- src/mixins2/browse/home.ts | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/mixins2/browse/home.ts b/src/mixins2/browse/home.ts index 5954f4d..45fa2d7 100644 --- a/src/mixins2/browse/home.ts +++ b/src/mixins2/browse/home.ts @@ -1,13 +1,36 @@ import { request_json } from "../../mixins/_request.ts"; +import { RawJSON } from "../../parsers2/types.d.ts"; import { parse_single_column_browse_results_renderer } from "../../parsers2/tabs/mod.ts"; +import { parse_background, Thumbnail } from "../../parsers2/thumbnail/mod.ts"; +import { parse_mood_chips } from "../../parsers2/chips/mod.ts"; +import { ParseMoodChipResult } from "../../parsers2/chips/mood.ts"; +import { parse_section_list } from "../../parsers2/list/mod.ts"; -export async function get_home() { +export type MoodChip = ParseMoodChipResult; + +export interface Home { + moods: MoodChip[]; + thumbnails: Thumbnail[]; + continuation: string | null; + content: RawJSON; +} + +export async function get_home(): Promise { const json = await request_json("browse", { data: { browseId: "FEmusic_home" }, }); + const background = parse_background(json); const tab = parse_single_column_browse_results_renderer(json.contents).tab; + const section_list = parse_section_list(tab.content); + + const moods = parse_mood_chips(section_list.header); - return tab; + return { + moods, + thumbnails: background.thumbnails, + continuation: section_list.nextContinuation, + content: section_list.contents, + }; } From 8ab41f2ea763aa2075786c1411934f4df0485132 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Sat, 2 Nov 2024 14:24:01 +0200 Subject: [PATCH 11/15] feat(parsers2/thumbnail): add parse_music_thumbnail_renderer --- src/parsers2/thumbnail/background.ts | 4 ++-- src/parsers2/thumbnail/mod.ts | 1 + src/parsers2/thumbnail/music.test.ts | 21 +++++++++++++++++++++ src/parsers2/thumbnail/music.ts | 8 ++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/parsers2/thumbnail/music.test.ts create mode 100644 src/parsers2/thumbnail/music.ts diff --git a/src/parsers2/thumbnail/background.ts b/src/parsers2/thumbnail/background.ts index 6332af8..e1ab829 100644 --- a/src/parsers2/thumbnail/background.ts +++ b/src/parsers2/thumbnail/background.ts @@ -1,5 +1,5 @@ import { RawJSON } from "../types.d.ts"; -import { parse_thumbnails } from "./thumbnails.ts"; +import { parse_music_thumbnail_renderer } from "./music.ts"; import { Thumbnail } from "./types.d.ts"; @@ -9,6 +9,6 @@ export interface ParseBackgroundResult { export function parse_background(content: RawJSON): ParseBackgroundResult { return { - thumbnails: parse_thumbnails(content.background.musicThumbnailRenderer), + thumbnails: parse_music_thumbnail_renderer(content.background), }; } diff --git a/src/parsers2/thumbnail/mod.ts b/src/parsers2/thumbnail/mod.ts index 57a8efd..df70ea2 100644 --- a/src/parsers2/thumbnail/mod.ts +++ b/src/parsers2/thumbnail/mod.ts @@ -1,3 +1,4 @@ export * from "./types.d.ts"; export * from "./background.ts"; +export * from "./music.ts"; export * from "./thumbnails.ts"; diff --git a/src/parsers2/thumbnail/music.test.ts b/src/parsers2/thumbnail/music.test.ts new file mode 100644 index 0000000..5ce9cba --- /dev/null +++ b/src/parsers2/thumbnail/music.test.ts @@ -0,0 +1,21 @@ +import { expect } from "jsr:@std/expect"; +import { describe, it } from "jsr:@std/testing/bdd"; + +import { sampleThumbnail } from "./thumbnails.test.ts"; + +import { parse_music_thumbnail_renderer } from "./music.ts"; + +describe("parse_music_thumbnail_renderer", () => { + it("parses thumbnails", () => { + const thumbnails = [sampleThumbnail]; + expect( + parse_music_thumbnail_renderer({ + musicThumbnailRenderer: { + thumbnail: { + thumbnails, + }, + }, + }), + ).toEqual(thumbnails); + }); +}); diff --git a/src/parsers2/thumbnail/music.ts b/src/parsers2/thumbnail/music.ts new file mode 100644 index 0000000..9f9f00f --- /dev/null +++ b/src/parsers2/thumbnail/music.ts @@ -0,0 +1,8 @@ +import { RawJSON } from "../types.d.ts"; +import { parse_thumbnails, ParseThumbnailsResult } from "./mod.ts"; + +export type ParseMusicThumbnailRendererResult = ParseThumbnailsResult; + +export function parse_music_thumbnail_renderer(content: RawJSON) { + return parse_thumbnails(content.musicThumbnailRenderer); +} From 382bc366443f9021f9aef1d22ee50abb284b2e48 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Sat, 2 Nov 2024 14:24:40 +0200 Subject: [PATCH 12/15] feat(mixins2/browse/home): partial parse of music carousel --- src/mixins2/browse/home.ts | 3 +- src/parsers2/music_carousel/mod.ts | 2 ++ src/parsers2/music_carousel/shelf_renderer.ts | 31 +++++++++++++++++++ .../music_carousel/shelf_renderers.ts | 6 ++++ src/parsers2/runs/mod.ts | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/parsers2/music_carousel/mod.ts create mode 100644 src/parsers2/music_carousel/shelf_renderer.ts create mode 100644 src/parsers2/music_carousel/shelf_renderers.ts create mode 100644 src/parsers2/runs/mod.ts diff --git a/src/mixins2/browse/home.ts b/src/mixins2/browse/home.ts index 45fa2d7..c945531 100644 --- a/src/mixins2/browse/home.ts +++ b/src/mixins2/browse/home.ts @@ -6,6 +6,7 @@ import { parse_background, Thumbnail } from "../../parsers2/thumbnail/mod.ts"; import { parse_mood_chips } from "../../parsers2/chips/mod.ts"; import { ParseMoodChipResult } from "../../parsers2/chips/mood.ts"; import { parse_section_list } from "../../parsers2/list/mod.ts"; +import { parse_music_carousel_shelf_renderers } from "../../parsers2/music_carousel/mod.ts"; export type MoodChip = ParseMoodChipResult; @@ -31,6 +32,6 @@ export async function get_home(): Promise { moods, thumbnails: background.thumbnails, continuation: section_list.nextContinuation, - content: section_list.contents, + content: parse_music_carousel_shelf_renderers(section_list.contents), }; } diff --git a/src/parsers2/music_carousel/mod.ts b/src/parsers2/music_carousel/mod.ts new file mode 100644 index 0000000..b882250 --- /dev/null +++ b/src/parsers2/music_carousel/mod.ts @@ -0,0 +1,2 @@ +export * from "./shelf_renderer.ts"; +export * from "./shelf_renderers.ts"; diff --git a/src/parsers2/music_carousel/shelf_renderer.ts b/src/parsers2/music_carousel/shelf_renderer.ts new file mode 100644 index 0000000..26e2611 --- /dev/null +++ b/src/parsers2/music_carousel/shelf_renderer.ts @@ -0,0 +1,31 @@ +import { RawJSON } from "../types.d.ts"; + +import { Thumbnail } from "../thumbnail/types.d.ts"; +import { parse_text_runs_simple } from "../runs/mod.ts"; +import { parse_music_thumbnail_renderer } from "../thumbnail/mod.ts"; + +export interface ParseMusicCarouselShelfRendererResult { + title: string; + subtitle: string | null; + thumbnails: Thumbnail[]; + contents: RawJSON[]; +} + +export function parse_music_carousel_shelf_renderer( + content: RawJSON, +): ParseMusicCarouselShelfRendererResult { + const renderer = content.musicCarouselShelfRenderer; + + const header = renderer.header.musicCarouselShelfBasicHeaderRenderer; + const title = header.title; + const subtitle = header.strapline; + + return { + title: parse_text_runs_simple(title), + subtitle: subtitle ? parse_text_runs_simple(subtitle) : null, + thumbnails: header.thumbnail + ? parse_music_thumbnail_renderer(header.thumbnail) + : [], + contents: renderer.contents, + }; +} diff --git a/src/parsers2/music_carousel/shelf_renderers.ts b/src/parsers2/music_carousel/shelf_renderers.ts new file mode 100644 index 0000000..970a3cb --- /dev/null +++ b/src/parsers2/music_carousel/shelf_renderers.ts @@ -0,0 +1,6 @@ +import { RawJSON } from "../types.d.ts"; +import { parse_music_carousel_shelf_renderer } from "./shelf_renderer.ts"; + +export function parse_music_carousel_shelf_renderers(content: RawJSON) { + return content.map(parse_music_carousel_shelf_renderer); +} diff --git a/src/parsers2/runs/mod.ts b/src/parsers2/runs/mod.ts new file mode 100644 index 0000000..eeeda24 --- /dev/null +++ b/src/parsers2/runs/mod.ts @@ -0,0 +1 @@ +export * from "./text_runs_simple.ts"; From 316ee905b29bf6c085ff76b6f86b46fef84ea25e Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Sat, 2 Nov 2024 14:34:04 +0200 Subject: [PATCH 13/15] tests(parsers/music_carousel): add tests for shelf_renderer --- .../music_carousel/shelf_renderer.test.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/parsers2/music_carousel/shelf_renderer.test.ts diff --git a/src/parsers2/music_carousel/shelf_renderer.test.ts b/src/parsers2/music_carousel/shelf_renderer.test.ts new file mode 100644 index 0000000..8092c99 --- /dev/null +++ b/src/parsers2/music_carousel/shelf_renderer.test.ts @@ -0,0 +1,44 @@ +import { expect } from "jsr:@std/expect"; +import { describe, test } from "jsr:@std/testing/bdd"; + +import { parse_music_carousel_shelf_renderer } from "./shelf_renderer.ts"; +import { sampleThumbnail } from "../thumbnail/thumbnails.test.ts"; + +describe("parse_music_carousel_shelf_renderer", () => { + const thumbnails = [sampleThumbnail]; + + const result = parse_music_carousel_shelf_renderer({ + musicCarouselShelfRenderer: { + header: { + musicCarouselShelfBasicHeaderRenderer: { + title: { runs: [{ text: "Title" }] }, + strapline: { runs: [{ text: "Subtitle" }] }, + thumbnail: { + musicThumbnailRenderer: { + thumbnail: { + thumbnails, + }, + }, + }, + }, + }, + contents: "Contents", + }, + }); + + test("title", () => { + expect(result.title).toBe("Title"); + }); + + test("subtitle", () => { + expect(result.subtitle).toBe("Subtitle"); + }); + + test("thumbnails", () => { + expect(result.thumbnails).toBe(thumbnails); + }); + + test("content", () => { + expect(result.contents).toBe("Contents"); + }); +}); From 537355affb4f4e68aa34699942a4f44515a44fd4 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Sat, 2 Nov 2024 14:49:11 +0200 Subject: [PATCH 14/15] feat(parsers2/browse): parse pageType --- src/parsers2/endpoint/endpoints/browse.test.ts | 13 +++++++++++++ src/parsers2/endpoint/endpoints/browse.ts | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/src/parsers2/endpoint/endpoints/browse.test.ts b/src/parsers2/endpoint/endpoints/browse.test.ts index 73659e0..9bfc5bb 100644 --- a/src/parsers2/endpoint/endpoints/browse.test.ts +++ b/src/parsers2/endpoint/endpoints/browse.test.ts @@ -29,4 +29,17 @@ describe("browseEndpoint", () => { }), ).toHaveProperty("params", "SOME_PARAMS"); }); + + it("parses pageType", () => { + expect( + parse_browse_endpoint({ + browseId: "SOME_BROWSE_ID", + browseEndpointContextSupportedConfigs: { + browseEndpointContextMusicConfig: { + pageType: "Page Type", + }, + }, + }), + ).toHaveProperty("pageType", "Page Type"); + }); }); diff --git a/src/parsers2/endpoint/endpoints/browse.ts b/src/parsers2/endpoint/endpoints/browse.ts index b505e53..f3af4fe 100644 --- a/src/parsers2/endpoint/endpoints/browse.ts +++ b/src/parsers2/endpoint/endpoints/browse.ts @@ -16,6 +16,10 @@ export interface BrowseEndpoint extends BaseEndpoint { * Optional params, usually used to get more data of a specific kind */ params?: string; + /** + * The type of this item + */ + pageType?: string; } /** @@ -28,5 +32,7 @@ export function parse_browse_endpoint(content: RawJSON): BrowseEndpoint { type: EndpointType.BROWSE, id: content.browseId, params: content.params, + pageType: content.browseEndpointContextSupportedConfigs + ?.browseEndpointContextMusicConfig.pageType, }; } From f817f438ee19cc01a0ed4d9534bb4101f8c707ad Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Sat, 2 Nov 2024 15:02:03 +0200 Subject: [PATCH 15/15] feat: add a simple shelf renderer --- src/mixins2/browse/home.ts | 12 +++++-- src/parsers2/endpoint/endpoints/mod.ts | 1 + src/parsers2/endpoint/endpoints/navigation.ts | 17 +++++++++ src/parsers2/endpoint/parse.ts | 1 + src/parsers2/items/mod.ts | 1 + src/parsers2/items/music_two_row.ts | 36 +++++++++++++++++++ .../music_carousel/shelf_renderers.ts | 9 +++-- 7 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 src/parsers2/endpoint/endpoints/navigation.ts create mode 100644 src/parsers2/items/mod.ts create mode 100644 src/parsers2/items/music_two_row.ts diff --git a/src/mixins2/browse/home.ts b/src/mixins2/browse/home.ts index c945531..a659390 100644 --- a/src/mixins2/browse/home.ts +++ b/src/mixins2/browse/home.ts @@ -7,6 +7,7 @@ import { parse_mood_chips } from "../../parsers2/chips/mod.ts"; import { ParseMoodChipResult } from "../../parsers2/chips/mood.ts"; import { parse_section_list } from "../../parsers2/list/mod.ts"; import { parse_music_carousel_shelf_renderers } from "../../parsers2/music_carousel/mod.ts"; +import { parse_music_two_row_item_renderer } from "../../parsers2/items/mod.ts"; export type MoodChip = ParseMoodChipResult; @@ -25,13 +26,20 @@ export async function get_home(): Promise { const background = parse_background(json); const tab = parse_single_column_browse_results_renderer(json.contents).tab; const section_list = parse_section_list(tab.content); - const moods = parse_mood_chips(section_list.header); + const contents = parse_music_carousel_shelf_renderers(section_list.contents) + .map((content) => { + return { + ...content, + contents: content.contents.map(parse_music_two_row_item_renderer), + }; + }); + return { moods, thumbnails: background.thumbnails, continuation: section_list.nextContinuation, - content: parse_music_carousel_shelf_renderers(section_list.contents), + content: contents, }; } diff --git a/src/parsers2/endpoint/endpoints/mod.ts b/src/parsers2/endpoint/endpoints/mod.ts index 3b66a3c..4edf35f 100644 --- a/src/parsers2/endpoint/endpoints/mod.ts +++ b/src/parsers2/endpoint/endpoints/mod.ts @@ -1 +1,2 @@ export * from "./browse.ts"; +export * from "./navigation.ts"; diff --git a/src/parsers2/endpoint/endpoints/navigation.ts b/src/parsers2/endpoint/endpoints/navigation.ts new file mode 100644 index 0000000..c9871c9 --- /dev/null +++ b/src/parsers2/endpoint/endpoints/navigation.ts @@ -0,0 +1,17 @@ +import { RawJSON } from "../../types.d.ts"; + +import { EndpointType } from "../mod.ts"; +import { BrowseEndpoint, parse_browse_endpoint } from "./browse.ts"; + +export interface NavigationEndpoint extends Omit { + type: EndpointType.NAVIGATION; +} + +export function parse_navigation_endpoint( + content: RawJSON, +): NavigationEndpoint { + return { + ...parse_browse_endpoint(content.browseEndpoint), + type: EndpointType.NAVIGATION, + }; +} diff --git a/src/parsers2/endpoint/parse.ts b/src/parsers2/endpoint/parse.ts index d341de3..0c85d11 100644 --- a/src/parsers2/endpoint/parse.ts +++ b/src/parsers2/endpoint/parse.ts @@ -10,6 +10,7 @@ export const _endpointMap = { export enum EndpointType { BROWSE, + NAVIGATION, } type EndpointResult = ReturnType< diff --git a/src/parsers2/items/mod.ts b/src/parsers2/items/mod.ts new file mode 100644 index 0000000..872582d --- /dev/null +++ b/src/parsers2/items/mod.ts @@ -0,0 +1 @@ +export * from "./music_two_row.ts"; diff --git a/src/parsers2/items/music_two_row.ts b/src/parsers2/items/music_two_row.ts new file mode 100644 index 0000000..41bade4 --- /dev/null +++ b/src/parsers2/items/music_two_row.ts @@ -0,0 +1,36 @@ +import { RawJSON } from "../types.d.ts"; + +import { parse_navigation_endpoint } from "../endpoint/mod.ts"; +import { parse_text_runs_simple } from "../runs/text_runs_simple.ts"; +import { parse_music_thumbnail_renderer } from "../thumbnail/mod.ts"; +import { Thumbnail } from "../thumbnail/types.d.ts"; + +export interface ParseMusicTwoRowItemRendererResult { + thumbnails: Thumbnail[]; + title: string; + subtitle: string; + id?: string; + pageType?: string; +} + +export function parse_music_two_row_item_renderer( + content: RawJSON, +): ParseMusicTwoRowItemRendererResult { + const item = content.musicTwoRowItemRenderer; + + const title = item.title; + const subtitle = item.subtitle; + const titleNavigationEndpoint = title.runs[0].navigationEndpoint; + const navigationEndpoint = titleNavigationEndpoint + ? parse_navigation_endpoint(titleNavigationEndpoint) + : null; + + return { + thumbnails: parse_music_thumbnail_renderer(item.thumbnailRenderer), + title: parse_text_runs_simple(title), + id: navigationEndpoint?.id, + pageType: navigationEndpoint?.pageType, + subtitle: parse_text_runs_simple(subtitle), + other: item, + }; +} diff --git a/src/parsers2/music_carousel/shelf_renderers.ts b/src/parsers2/music_carousel/shelf_renderers.ts index 970a3cb..50268d5 100644 --- a/src/parsers2/music_carousel/shelf_renderers.ts +++ b/src/parsers2/music_carousel/shelf_renderers.ts @@ -1,6 +1,11 @@ import { RawJSON } from "../types.d.ts"; -import { parse_music_carousel_shelf_renderer } from "./shelf_renderer.ts"; +import { + parse_music_carousel_shelf_renderer, + ParseMusicCarouselShelfRendererResult, +} from "./shelf_renderer.ts"; -export function parse_music_carousel_shelf_renderers(content: RawJSON) { +export function parse_music_carousel_shelf_renderers( + content: RawJSON, +): ParseMusicCarouselShelfRendererResult[] { return content.map(parse_music_carousel_shelf_renderer); }