diff --git a/materialious/src/lib/api/history.ts b/materialious/src/lib/api/history.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/materialious/src/lib/api/index.ts b/materialious/src/lib/api/index.ts index 2055c621..4f173344 100644 --- a/materialious/src/lib/api/index.ts +++ b/materialious/src/lib/api/index.ts @@ -74,6 +74,8 @@ import { saveWatchHistoryBackend, updateWatchHistoryBackend } from './backend/history'; +import { localDb } from '$lib/dexie'; +import { getBestThumbnail } from '$lib/images'; export async function getPopular(fetchOptions?: RequestInit): Promise { // Doesn't exist in YTjs. @@ -288,6 +290,30 @@ export async function getWatchHistory( if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) { return getWatchHistoryBackend(options); } + + let watchHistory = await localDb.watchHistory.toArray(); + watchHistory.sort((a, b) => b.watched.getDate() - a.watched.getDate()); + + if (options.videoIds) { + watchHistory = watchHistory.filter((item) => !options.videoIds?.includes(item.videoId)); + } + + const cullAfter = 1000; + if (watchHistory.length > cullAfter) { + const videosToDelete = watchHistory.slice(cullAfter); + const videoIdsToDelete = videosToDelete.map((video) => video.videoId); + await localDb.watchHistory.where('videoId').anyOf(videoIdsToDelete).delete(); + + // Don't display culled videos. + watchHistory = watchHistory.slice(0, cullAfter); + } + + const maxResults = 100; + + const start = (options.page ?? 1 - 1) * maxResults; + const end = start + maxResults; + + return watchHistory.splice(start, end); } export async function getVideoWatchHistory( @@ -296,12 +322,16 @@ export async function getVideoWatchHistory( if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) { return getVideoWatchHistoryBackend(videoId); } + + return await localDb.watchHistory.get({ videoId }); } export async function deleteWatchHistory() { if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) { return deleteWatchHistoryBackend(); } + + await localDb.watchHistory.clear(); } export async function updateWatchHistory( @@ -314,12 +344,28 @@ export async function updateWatchHistory( } if (get(invidiousAuthStore)) postHistoryInvidious(videoId, fetchOptions); + + await localDb.watchHistory.update({ videoId }, { progress }); } export async function saveWatchHistory(video: VideoPlay, progress: number = 0) { if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) { return saveWatchHistoryBackend(video, progress); } + + if (await localDb.watchHistory.get({ videoId: video.videoId })) return; + + await localDb.watchHistory.add({ + author: video.author, + watched: new Date(), + lengthSeconds: video.lengthSeconds, + progress, + id: video.videoId, + title: video.title, + thumbnail: getBestThumbnail(video.videoThumbnails), + videoId: video.videoId, + type: 'historyVideo' + }); } export async function getPlaylist( diff --git a/materialious/src/lib/dexie.ts b/materialious/src/lib/dexie.ts index f4840bc2..982d4714 100644 --- a/materialious/src/lib/dexie.ts +++ b/materialious/src/lib/dexie.ts @@ -21,11 +21,11 @@ export class MaterialiousDb extends Dexie { constructor() { super('materialious'); - this.version(2).stores({ + this.version(3).stores({ favouriteChannels: 'channelId', channelSubscriptions: 'channelId', subscriptionFeed: 'videoId, authorId, published', - watchHistory: 'videoId, id' + watchHistory: 'videoId' }); } } diff --git a/materialious/src/lib/navPages.ts b/materialious/src/lib/navPages.ts index 61262adc..7e016903 100644 --- a/materialious/src/lib/navPages.ts +++ b/materialious/src/lib/navPages.ts @@ -26,16 +26,14 @@ export function getPages(): Pages { href: '/playlists', name: get(_)('pages.playlists'), requiresAuth: true - } - ]; - - if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) - pages.push({ + }, + { icon: 'history', href: '/history', name: get(_)('pages.history'), requiresAuth: false - }); + } + ]; pages = pages.filter((page) => { return !page.requiresAuth || (get(invidiousAuthStore) && !isYTBackend()); diff --git a/materialious/src/routes/(app)/+page.ts b/materialious/src/routes/(app)/+page.ts index 810c3388..34c06b48 100644 --- a/materialious/src/routes/(app)/+page.ts +++ b/materialious/src/routes/(app)/+page.ts @@ -1,5 +1,6 @@ import { resolve } from '$app/paths'; -import { getPopular, HTTPError } from '$lib/api/index'; +import { getPopular } from '$lib/api/index'; +import { HTTPError } from '$lib/api/invidious/request'; import { isYTBackend } from '$lib/misc'; import { feedCacheStore, invidiousInstanceStore } from '$lib/store'; import { error, redirect } from '@sveltejs/kit'; diff --git a/materialious/src/routes/(app)/history/+page.ts b/materialious/src/routes/(app)/history/+page.ts index 9b8473ee..7a29c19f 100644 --- a/materialious/src/routes/(app)/history/+page.ts +++ b/materialious/src/routes/(app)/history/+page.ts @@ -1,13 +1,5 @@ -import { resolve } from '$app/paths'; -import { getWatchHistoryBackend } from '$lib/api/backend/history'; -import { isOwnBackend } from '$lib/shared'; -import { rawMasterKeyStore } from '$lib/store'; -import { redirect } from '@sveltejs/kit'; -import { get } from 'svelte/store'; +import { getWatchHistory } from '$lib/api'; export async function load() { - if (!isOwnBackend()?.internalAuth || !get(rawMasterKeyStore)) - throw redirect(302, resolve('/', {})); - - return { videos: await getWatchHistoryBackend() }; + return { videos: await getWatchHistory() }; }