Implemented batch loading for API Extended
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// src/lib/requestQueue.ts
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import type { SynciousProgressModel } from './model';
|
||||
import { getVideoProgress } from '.';
|
||||
|
||||
const synciousCacheStore: Writable<SynciousProgressModel[] | null> = writable(null);
|
||||
|
||||
const videoIds: string[] = [];
|
||||
const pendingResolves = new Map<string, (result: SynciousProgressModel | undefined) => void>();
|
||||
|
||||
let timeout: ReturnType<typeof setTimeout> | null = null;
|
||||
const DEBOUNCE_MS = 1000;
|
||||
const BATCH_SIZE = 100;
|
||||
|
||||
async function processBatches(): Promise<void> {
|
||||
const batches: string[][] = [];
|
||||
|
||||
while (videoIds.length > 0) {
|
||||
batches.push(videoIds.splice(0, BATCH_SIZE));
|
||||
}
|
||||
|
||||
const results: SynciousProgressModel[] = [];
|
||||
|
||||
for (const batch of batches) {
|
||||
const res: SynciousProgressModel[] = await getVideoProgress(batch.join(','));
|
||||
results.push(...res);
|
||||
|
||||
// Resolve pending promises for this batch
|
||||
for (const videoId of batch) {
|
||||
const match = res.find((item) => item.video_id === videoId);
|
||||
const resolve = pendingResolves.get(videoId);
|
||||
if (resolve) {
|
||||
resolve(match);
|
||||
pendingResolves.delete(videoId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synciousCacheStore.set(results);
|
||||
}
|
||||
|
||||
export function queueSyncious(videoId: string): Promise<SynciousProgressModel | undefined> {
|
||||
videoIds.push(videoId);
|
||||
|
||||
const promise = new Promise<SynciousProgressModel | undefined>((resolve) => {
|
||||
pendingResolves.set(videoId, resolve);
|
||||
});
|
||||
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
processBatches().catch((e) => {
|
||||
console.error('Failed to process batches:', e);
|
||||
});
|
||||
}, DEBOUNCE_MS);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
export { synciousCacheStore };
|
||||
@@ -64,7 +64,7 @@ export function buildAuthHeaders(): { headers: Record<string, string> } {
|
||||
if (authToken.startsWith('SID=')) {
|
||||
return { headers: { __sid_auth: authToken } };
|
||||
} else {
|
||||
return { headers: { Authorization: `Bearer ${get(authStore)?.token}` } };
|
||||
return { headers: { Authorization: `Bearer ${authToken}` } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,18 @@
|
||||
played: (await getDynamicTheme())['--primary']
|
||||
}
|
||||
});
|
||||
|
||||
setChapterMarkers();
|
||||
|
||||
const overflowMenuButton = document.querySelector('.shaka-overflow-menu-button');
|
||||
if (overflowMenuButton) {
|
||||
overflowMenuButton.innerHTML = 'settings';
|
||||
}
|
||||
|
||||
const backToOverflowButton = document.querySelector('.shaka-back-to-overflow-button');
|
||||
if (backToOverflowButton) {
|
||||
backToOverflowButton.innerHTML = 'arrow_back_ios_new';
|
||||
}
|
||||
}
|
||||
|
||||
themeColorStore.subscribe(updateSeekBarTheme);
|
||||
@@ -477,12 +488,11 @@
|
||||
'statistics'
|
||||
],
|
||||
playbackRates: playbackRates,
|
||||
enableTooltips: false,
|
||||
seekBarColors: {
|
||||
played: (await getDynamicTheme())['--primary']
|
||||
}
|
||||
enableTooltips: false
|
||||
});
|
||||
|
||||
updateSeekBarTheme();
|
||||
|
||||
player.addEventListener('error', async (event) => {
|
||||
const error = (event as CustomEvent).detail as shaka.util.Error;
|
||||
console.error('Player error:', error);
|
||||
@@ -504,16 +514,6 @@
|
||||
|
||||
await androidHandleRotate();
|
||||
|
||||
const overflowMenuButton = document.querySelector('.shaka-overflow-menu-button');
|
||||
if (overflowMenuButton) {
|
||||
overflowMenuButton.innerHTML = 'settings';
|
||||
}
|
||||
|
||||
const backToOverflowButton = document.querySelector('.shaka-back-to-overflow-button');
|
||||
if (backToOverflowButton) {
|
||||
backToOverflowButton.innerHTML = 'arrow_back_ios_new';
|
||||
}
|
||||
|
||||
Mousetrap.bind('space', () => {
|
||||
if (!playerElement) return;
|
||||
|
||||
@@ -580,8 +580,6 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
setChapterMarkers();
|
||||
|
||||
if ($isAndroidTvStore) {
|
||||
Mousetrap.bind('enter', () => {
|
||||
if (playerElement?.paused) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { _ } from '$lib/i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { getDeArrow, getThumbnail, getVideoProgress } from '../api';
|
||||
import { getDeArrow, getThumbnail } from '../api';
|
||||
import type { Notification, PlaylistPageVideo, Video, VideoBase } from '../api/model';
|
||||
import { insecureRequestImageHandler, truncate } from '../misc';
|
||||
import type { PlayerEvents } from '../player';
|
||||
@@ -21,6 +21,7 @@
|
||||
synciousStore
|
||||
} from '../store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { queueSyncious } from '$lib/api/apiExtended';
|
||||
|
||||
interface Props {
|
||||
video: VideoBase | Video | Notification | PlaylistPageVideo;
|
||||
@@ -109,7 +110,7 @@
|
||||
|
||||
if (get(synciousStore) && get(synciousInstanceStore) && get(authStore)) {
|
||||
try {
|
||||
progress = (await getVideoProgress(video.videoId, { priority: 'low' }))[0].time.toString();
|
||||
progress = (await queueSyncious(video.videoId))?.time?.toString() ?? undefined;
|
||||
} catch {}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
amSubscribed,
|
||||
getComments,
|
||||
getDislikes,
|
||||
getPersonalPlaylists,
|
||||
getVideo,
|
||||
postHistory
|
||||
} from '$lib/api/index';
|
||||
import { loadEntirePlaylist } from '$lib/playlist';
|
||||
import {
|
||||
authStore,
|
||||
playerProxyVideosStore,
|
||||
returnYTDislikesInstanceStore,
|
||||
returnYtDislikesStore
|
||||
} from '$lib/store';
|
||||
import { phaseDescription } from '$lib/timestamps';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export async function getWatchDetails(videoId: string, url: URL) {
|
||||
let video;
|
||||
try {
|
||||
video = await getVideo(videoId, get(playerProxyVideosStore), { priority: 'high' });
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
|
||||
let personalPlaylists;
|
||||
if (get(authStore)) {
|
||||
postHistory(video.videoId);
|
||||
personalPlaylists = getPersonalPlaylists({ priority: 'low' });
|
||||
} else {
|
||||
personalPlaylists = null;
|
||||
}
|
||||
|
||||
let comments;
|
||||
try {
|
||||
comments = video.liveNow
|
||||
? null
|
||||
: getComments(videoId, { sort_by: 'top', source: 'youtube' }, { priority: 'low' });
|
||||
} catch {
|
||||
comments = null;
|
||||
}
|
||||
|
||||
let returnYTDislikes;
|
||||
const returnYTDislikesInstance = get(returnYTDislikesInstanceStore);
|
||||
if (returnYTDislikesInstance && returnYTDislikesInstance !== '') {
|
||||
try {
|
||||
returnYTDislikes = get(returnYtDislikesStore)
|
||||
? getDislikes(videoId, { priority: 'low' })
|
||||
: null;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const playlistId = url.searchParams.get('playlist');
|
||||
if (playlistId) {
|
||||
await loadEntirePlaylist(playlistId);
|
||||
}
|
||||
|
||||
return {
|
||||
video: video,
|
||||
content: phaseDescription(video.videoId, video.descriptionHtml, video.fallbackPatch),
|
||||
playlistId: playlistId,
|
||||
streamed: {
|
||||
personalPlaylists: personalPlaylists,
|
||||
returnYTDislikes: returnYTDislikes,
|
||||
comments: comments,
|
||||
subscribed: amSubscribed(video.authorId)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,71 +1,5 @@
|
||||
import {
|
||||
amSubscribed,
|
||||
getComments,
|
||||
getDislikes,
|
||||
getPersonalPlaylists,
|
||||
getVideo,
|
||||
postHistory
|
||||
} from '$lib/api/index';
|
||||
import { loadEntirePlaylist } from '$lib/playlist';
|
||||
import {
|
||||
authStore,
|
||||
playerProxyVideosStore,
|
||||
returnYTDislikesInstanceStore,
|
||||
returnYtDislikesStore
|
||||
} from '$lib/store';
|
||||
import { phaseDescription } from '$lib/timestamps';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
import { getWatchDetails } from '$lib/watch.js';
|
||||
|
||||
export async function load({ params, url }) {
|
||||
let video;
|
||||
try {
|
||||
video = await getVideo(params.slug, get(playerProxyVideosStore), { priority: 'high' });
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
|
||||
let personalPlaylists;
|
||||
if (get(authStore)) {
|
||||
postHistory(video.videoId);
|
||||
personalPlaylists = getPersonalPlaylists({ priority: 'low' });
|
||||
} else {
|
||||
personalPlaylists = null;
|
||||
}
|
||||
|
||||
let comments;
|
||||
try {
|
||||
comments = video.liveNow
|
||||
? null
|
||||
: getComments(params.slug, { sort_by: 'top', source: 'youtube' }, { priority: 'low' });
|
||||
} catch {
|
||||
comments = null;
|
||||
}
|
||||
|
||||
let returnYTDislikes;
|
||||
const returnYTDislikesInstance = get(returnYTDislikesInstanceStore);
|
||||
if (returnYTDislikesInstance && returnYTDislikesInstance !== '') {
|
||||
try {
|
||||
returnYTDislikes = get(returnYtDislikesStore)
|
||||
? getDislikes(params.slug, { priority: 'low' })
|
||||
: null;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const playlistId = url.searchParams.get('playlist');
|
||||
if (playlistId) {
|
||||
await loadEntirePlaylist(playlistId);
|
||||
}
|
||||
|
||||
return {
|
||||
video: video,
|
||||
content: phaseDescription(video.videoId, video.descriptionHtml, video.fallbackPatch),
|
||||
playlistId: playlistId,
|
||||
streamed: {
|
||||
personalPlaylists: personalPlaylists,
|
||||
returnYTDislikes: returnYTDislikes,
|
||||
comments: comments,
|
||||
subscribed: amSubscribed(video.authorId)
|
||||
}
|
||||
};
|
||||
return getWatchDetails(params.slug, url);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
import { getVideo } from '$lib/api/index';
|
||||
import { playerProxyVideosStore } from '$lib/store';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
import { getWatchDetails } from '$lib/watch.js';
|
||||
|
||||
export async function load({ params }) {
|
||||
let video;
|
||||
try {
|
||||
video = await getVideo(params.slug, get(playerProxyVideosStore));
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
|
||||
return {
|
||||
video: video,
|
||||
content: {
|
||||
description: '',
|
||||
timestamps: []
|
||||
},
|
||||
playlistId: null
|
||||
};
|
||||
export async function load({ params, url }) {
|
||||
return getWatchDetails(params.slug, url);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user