Implemented on device watch history

This commit is contained in:
WardPearce
2026-03-04 23:10:36 +13:00
parent c2be626ca5
commit 7c936ebfd4
6 changed files with 56 additions and 19 deletions
View File
+46
View File
@@ -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<Video[]> {
// 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(
+2 -2
View File
@@ -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'
});
}
}
+4 -6
View File
@@ -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());
+2 -1
View File
@@ -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';
+2 -10
View File
@@ -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() };
}