From c3a1aa56c530db5bab24618f8c0685f857dd9dfb Mon Sep 17 00:00:00 2001 From: WardPearce Date: Wed, 11 Feb 2026 21:01:46 +1300 Subject: [PATCH] Improved yt search --- materialious/src/lib/api/model.ts | 4 +- materialious/src/lib/api/youtubejs/search.ts | 48 ++++++++++++------- materialious/src/lib/store.ts | 3 +- .../routes/(app)/search/[slug]/+page.svelte | 26 ++++++---- 4 files changed, 55 insertions(+), 26 deletions(-) diff --git a/materialious/src/lib/api/model.ts b/materialious/src/lib/api/model.ts index faef1a43..34ce0b87 100644 --- a/materialious/src/lib/api/model.ts +++ b/materialious/src/lib/api/model.ts @@ -304,4 +304,6 @@ export interface SynciousSaveProgressModel { time: number; } -export type SearchResults = (Channel | Video | Playlist | HashTag)[]; +export type SearchResults = (Channel | Video | Playlist | HashTag)[] & { + getContinuation?: () => Promise; +}; diff --git a/materialious/src/lib/api/youtubejs/search.ts b/materialious/src/lib/api/youtubejs/search.ts index b2a3601b..1f495f96 100644 --- a/materialious/src/lib/api/youtubejs/search.ts +++ b/materialious/src/lib/api/youtubejs/search.ts @@ -3,23 +3,9 @@ import { convertToSeconds } from '$lib/time'; import { getInnertube } from '.'; import { searchSetDefaults } from '../misc'; import type { Channel, SearchOptions, SearchResults, Thumbnail, Video } from '../model'; -import { YTNodes, type Types } from 'youtubei.js'; - -export async function getSearchYTjs( - search: string, - options: SearchOptions -): Promise { - const innertube = await getInnertube(); - - searchSetDefaults(options); - - const innerResults = await innertube.search(search, { - sort_by: options.sort_by, - duration: options.duration, - features: [options.features] as Types.Feature[], - upload_date: options.date - }); +import { YTNodes, type Types, YT } from 'youtubei.js'; +function invidiousSchema(innerResults: YT.Search): SearchResults { const searchResults: SearchResults = []; innerResults.results.forEach((result) => { @@ -66,3 +52,33 @@ export async function getSearchYTjs( return searchResults; } + +function setGetContinuation(innerResults: YT.Search) { + return async () => { + const result = await innerResults.getContinuation(); + const searchResults = invidiousSchema(result); + searchResults.getContinuation = setGetContinuation(result); + return searchResults; + }; +} + +export async function getSearchYTjs( + search: string, + options: SearchOptions +): Promise { + const innertube = await getInnertube(); + + searchSetDefaults(options); + + const innerResults = await innertube.search(search, { + sort_by: options.sort_by, + duration: options.duration, + features: [options.features] as Types.Feature[], + upload_date: options.date + }); + + const searchResults = invidiousSchema(innerResults); + searchResults.getContinuation = setGetContinuation(innerResults); + + return searchResults; +} diff --git a/materialious/src/lib/store.ts b/materialious/src/lib/store.ts index fe437ccd..d6ccb185 100644 --- a/materialious/src/lib/store.ts +++ b/materialious/src/lib/store.ts @@ -20,6 +20,7 @@ import type { Playlist, PlaylistPage, PlaylistPageVideo, + SearchResults, Video, VideoBase, VideoPlay @@ -334,7 +335,7 @@ export const feedCacheStore: Writable<{ [key: string]: (VideoBase | Video | PlaylistPageVideo)[]; }> = writable({}); export const searchCacheStore: Writable<{ - [searchTypeAndQuery: string]: (Channel | Video | Playlist | HashTag)[]; + [searchTypeAndQuery: string]: SearchResults; }> = writable({}); export const feedLastItemId: Writable = writable(undefined); export const playlistCacheStore: Writable<{ diff --git a/materialious/src/routes/(app)/search/[slug]/+page.svelte b/materialious/src/routes/(app)/search/[slug]/+page.svelte index 8a1e8d5e..8f6fb22a 100644 --- a/materialious/src/routes/(app)/search/[slug]/+page.svelte +++ b/materialious/src/routes/(app)/search/[slug]/+page.svelte @@ -5,7 +5,7 @@ import { _ } from '$lib/i18n'; import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading'; import ItemsList from '$lib/components/ItemsList.svelte'; - import type { SearchOptions } from '$lib/api/model.js'; + import type { SearchOptions, SearchResults } from '$lib/api/model.js'; let { data } = $props(); @@ -69,18 +69,28 @@ } async function loadMore(event: InfiniteEvent) { - currentPage++; - searchOptions = { - ...searchOptions, - page: currentPage.toString() - }; - const newSearch = await getSearch(data.slug, searchOptions); + let newSearch: SearchResults; + + const searchCacheItem = $searchCacheStore[data.searchStoreId]; + + if (searchCacheItem.getContinuation) { + newSearch = await searchCacheItem.getContinuation(); + searchCacheItem.getContinuation = newSearch.getContinuation; + } else { + currentPage++; + searchOptions = { + ...searchOptions, + page: currentPage.toString() + }; + + newSearch = await getSearch(data.slug, searchOptions); + } if (newSearch.length === 0) { event.detail.complete(); } else { searchCacheStore.set({ - [data.searchStoreId]: [...($searchCacheStore[data.searchStoreId] ?? []), ...newSearch] + [data.searchStoreId]: [...(searchCacheItem ?? []), ...newSearch] }); event.detail.loaded(); }