From bed48042042d991d4d9cee88f51c397f35b7646d Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sun, 28 Apr 2024 19:54:46 +1200 Subject: [PATCH 1/4] add store --- materialious/src/lib/Api/index.ts | 57 +++++++++++++++++++++++++- materialious/src/lib/Api/model.ts | 9 ++++ materialious/src/routes/+layout.svelte | 33 +++++++++++++++ materialious/src/store.ts | 23 +++++++---- 4 files changed, 113 insertions(+), 9 deletions(-) diff --git a/materialious/src/lib/Api/index.ts b/materialious/src/lib/Api/index.ts index 277d0598..4d6fa107 100644 --- a/materialious/src/lib/Api/index.ts +++ b/materialious/src/lib/Api/index.ts @@ -3,7 +3,8 @@ import { authStore, deArrowInstanceStore, deArrowThumbnailInstanceStore, - returnYTDislikesInstanceStore + returnYTDislikesInstanceStore, + synciousInstanceStore } from '../../store'; import type { Channel, @@ -18,6 +19,7 @@ import type { ReturnYTDislikes, SearchSuggestion, Subscription, + SynciousProgressModel, Video, VideoPlay } from './model'; @@ -301,3 +303,56 @@ export async function getThumbnail(videoId: string, time: number): Promise { + const resp = await fetchErrorHandle( + await fetch( + `${get(synciousInstanceStore)}/video/${encodeURIComponent(videoId)}`, + buildAuthHeaders() + ) + ); + + return resp.json(); +} + +export async function saveVideoProgress(videoId: string, time: number) { + const headers: Record> = buildAuthHeaders(); + headers['headers']['Content-type'] = 'application/json'; + + await fetchErrorHandle( + await fetch( + `${get(synciousInstanceStore)}/video/${encodeURIComponent(videoId)}`, + { + ...headers, + method: "POST", + body: JSON.stringify({ + time: time + }) + } + ) + ); +} + +export async function deleteVideoProgress(videoId: string) { + await fetchErrorHandle( + await fetch( + `${get(synciousInstanceStore)}/video/${videoId}`, + { + method: "DELETE", + ...buildAuthHeaders() + } + ) + ); +} + +export async function deleteAllVideoProgress() { + await fetchErrorHandle( + await fetch( + `${get(synciousInstanceStore)}/videos`, + { + method: "DELETE", + ...buildAuthHeaders() + } + ) + ); +} \ No newline at end of file diff --git a/materialious/src/lib/Api/model.ts b/materialious/src/lib/Api/model.ts index dd1604b7..6fe6552f 100644 --- a/materialious/src/lib/Api/model.ts +++ b/materialious/src/lib/Api/model.ts @@ -250,3 +250,12 @@ export interface DeArrow { randomTime: number; videoDuration: number; } + +export interface SynciousProgressModel { + time: number; + video_id: string; +} + +export interface SynciousSaveProgressModel { + time: number; +} diff --git a/materialious/src/routes/+layout.svelte b/materialious/src/routes/+layout.svelte index 4752fb0f..cf5791bd 100644 --- a/materialious/src/routes/+layout.svelte +++ b/materialious/src/routes/+layout.svelte @@ -42,6 +42,8 @@ sponsorBlockUrlStore, syncPartyConnectionsStore, syncPartyPeerStore, + synciousInstanceStore, + synciousStore, themeColorStore } from '../store'; @@ -61,6 +63,7 @@ let notifications: Notification[] = []; let sponsorBlockInstance = get(sponsorBlockUrlStore); + let synciousInstance = get(synciousInstanceStore); let returnYTInstance = get(returnYTDislikesInstanceStore); let deArrowUrl = get(deArrowInstanceStore); let deArrowThumbnailUrl = get(deArrowThumbnailInstanceStore); @@ -604,6 +607,36 @@ +
+
Syncious
+ +
synciousInstanceStore.set(synciousInstance)}> + +
+ + +
+
Sponsorblock
diff --git a/materialious/src/store.ts b/materialious/src/store.ts index ff329409..7d2f5dd1 100644 --- a/materialious/src/store.ts +++ b/materialious/src/store.ts @@ -3,10 +3,12 @@ import type { DataConnection } from 'peerjs'; import { persisted } from 'svelte-persisted-store'; import { writable, type Writable } from 'svelte/store'; -export const returnYTDislikesInstanceStore: Writable = persisted( - 'returnYTDislikesInstance', - import.meta.env.VITE_DEFAULT_RETURNYTDISLIKES_INSTANCE +export const authStore: Writable = persisted( + 'authToken', + null ); + + export const darkModeStore: Writable = persisted('darkMode', null); export const themeColorStore: Writable = persisted('themeColor', null); @@ -22,15 +24,20 @@ export const playerTheatreModeByDefaultStore = persisted('theatreModeByDefault', export const playerAutoplayNextByDefaultStore = persisted('autoplayNextByDefault', false); export const returnYtDislikesStore = persisted('returnYtDislikes', false); +export const returnYTDislikesInstanceStore: Writable = persisted( + 'returnYTDislikesInstance', + import.meta.env.VITE_DEFAULT_RETURNYTDISLIKES_INSTANCE +); + +export const synciousStore = persisted('syncious', true); +export const synciousInstanceStore: Writable = persisted( + 'synciousInstance', + import.meta.env.VITE_DEFAULT_SYNCIOUS_INSTANCE +); export const interfaceSearchSuggestionsStore = persisted('searchSuggestions', true); export const interfacePreviewVideoOnHoverStore = persisted('previewVideoOnHover', true); -export const authStore: Writable = persisted( - 'authToken', - null -); - export const sponsorBlockStore = persisted('sponsorBlock', true); export const sponsorBlockUrlStore: Writable = persisted( 'sponsorBlockUrl', From 29c5bc5ada9c8bf2a5cfbbdaf61c2ccea10ef15e Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sun, 28 Apr 2024 20:29:00 +1200 Subject: [PATCH 2/4] Add player time sync with syncious --- materialious/src/lib/Player.svelte | 37 +++++++++++++++----- materialious/src/routes/history/+page.svelte | 10 ++++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/materialious/src/lib/Player.svelte b/materialious/src/lib/Player.svelte index 3be87ae7..5a0c3d27 100644 --- a/materialious/src/lib/Player.svelte +++ b/materialious/src/lib/Player.svelte @@ -8,14 +8,17 @@ import type { MediaTimeUpdateEvent, PlayerSrc } from 'vidstack'; import type { MediaPlayerElement } from 'vidstack/elements'; import { + authStore, playerAlwaysLoopStore, playerAutoPlayStore, playerDashStore, playerProxyVideosStore, playerSavePlaybackPositionStore, sponsorBlockCategoriesStore, - sponsorBlockUrlStore + sponsorBlockUrlStore, + synciousStore } from '../store'; + import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from './Api'; import type { VideoPlay } from './Api/model'; import { getBestThumbnail, proxyVideoUrl, videoLength, type PhasedDescription } from './misc'; import { getDynamicTheme } from './theme'; @@ -114,8 +117,8 @@ } if (get(playerDashStore)) { - player.addEventListener('dash-can-play', () => { - loadPlayerPos(); + player.addEventListener('dash-can-play', async () => { + await loadPlayerPos(); }); src = [{ src: data.video.dashUrl + '?local=true', type: 'application/dash+xml' }]; @@ -180,10 +183,16 @@ document.documentElement.style.setProperty('--audio-bg', currentTheme['--surface']); }); - function loadPlayerPos() { + async function loadPlayerPos() { if (playerPosSet) return; playerPosSet = true; if (get(playerSavePlaybackPositionStore)) { + if (get(synciousStore) && get(authStore)) { + try { + player.currentTime = (await getVideoProgress(data.video.videoId))[0].time; + } catch {} + } + try { const playerPos = localStorage.getItem(`v_${data.video.videoId}`); if (playerPos) { @@ -196,15 +205,25 @@ function savePlayerPos() { if (data.video.hlsUrl) return; - try { - if (get(playerSavePlaybackPositionStore) && player.currentTime) { - if (player.currentTime < player.duration - 10 && player.currentTime > 10) { + if (get(playerSavePlaybackPositionStore) && player.currentTime) { + if (player.currentTime < player.duration - 10 && player.currentTime > 10) { + try { localStorage.setItem(`v_${data.video.videoId}`, player.currentTime.toString()); - } else { + } catch {} + + if (get(synciousStore) && get(authStore)) { + saveVideoProgress(data.video.videoId, player.currentTime); + } + } else { + try { localStorage.removeItem(`v_${data.video.videoId}`); + } catch {} + + if (get(synciousStore) && get(authStore)) { + deleteVideoProgress(data.video.videoId); } } - } catch {} + } } onDestroy(() => { diff --git a/materialious/src/routes/history/+page.svelte b/materialious/src/routes/history/+page.svelte index f25a721b..eaa5b567 100644 --- a/materialious/src/routes/history/+page.svelte +++ b/materialious/src/routes/history/+page.svelte @@ -1,5 +1,5 @@