diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index a70d21c7..02332b07 100644 --- a/materialious/android/app/build.gradle +++ b/materialious/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "us.materialio.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 164 - versionName "1.11.4" + versionCode 165 + versionName "1.11.5" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/materialious/electron/materialious.metainfo.xml b/materialious/electron/materialious.metainfo.xml index 347c731b..058be182 100644 --- a/materialious/electron/materialious.metainfo.xml +++ b/materialious/electron/materialious.metainfo.xml @@ -74,7 +74,11 @@ - + + + https://github.com/Materialious/Materialious/releases/tag/1.11.5 + + https://github.com/Materialious/Materialious/releases/tag/1.11.4 diff --git a/materialious/electron/package-lock.json b/materialious/electron/package-lock.json index 357eb009..2b1650c8 100644 --- a/materialious/electron/package-lock.json +++ b/materialious/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "Materialious", - "version": "1.11.4", + "version": "1.11.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Materialious", - "version": "1.11.4", + "version": "1.11.5", "license": "MIT", "dependencies": { "@capacitor-community/electron": "^5.0.0", diff --git a/materialious/electron/package.json b/materialious/electron/package.json index 6f0d5099..1238f92a 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.11.4", + "version": "1.11.5", "description": "Modern material design for Invidious.", "author": { "name": "Ward Pearce", diff --git a/materialious/package-lock.json b/materialious/package-lock.json index cc45d50a..8a3bb2d6 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -1,12 +1,12 @@ { "name": "materialious", - "version": "1.11.3", + "version": "1.11.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "materialious", - "version": "1.11.3", + "version": "1.11.5", "hasInstallScript": true, "dependencies": { "@capacitor-community/electron": "^5.0.1", @@ -62,7 +62,7 @@ "svelte": "^5.43.2", "svelte-check": "^4.3.3", "tslib": "^2.7.0", - "typescript": "^5.6.3", + "typescript": "^5.9.3", "vite": "^7.1.12" } }, @@ -12189,9 +12189,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "bin": { diff --git a/materialious/package.json b/materialious/package.json index 32409e71..0dcbfde6 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.11.4", + "version": "1.11.5", "private": true, "scripts": { "dev": "vite dev", @@ -37,7 +37,7 @@ "svelte": "^5.43.2", "svelte-check": "^4.3.3", "tslib": "^2.7.0", - "typescript": "^5.6.3", + "typescript": "^5.9.3", "vite": "^7.1.12" }, "type": "module", diff --git a/materialious/src/lib/components/ItemsList.svelte b/materialious/src/lib/components/ItemsList.svelte index 2e77cedf..d4e102ba 100644 --- a/materialious/src/lib/components/ItemsList.svelte +++ b/materialious/src/lib/components/ItemsList.svelte @@ -192,6 +192,16 @@ // Watch for focus changes to detect when we're active document.addEventListener('focusin', checkComponentFocus); document.addEventListener('focusout', checkComponentFocus); + } else if ($feedLastItemId) { + const element = document.getElementById($feedLastItemId); + + if (element) { + element.scrollIntoView({ + behavior: 'instant', + block: 'start', + inline: 'nearest' + }); + } } }); diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte index 245915aa..d2430121 100644 --- a/materialious/src/lib/components/Player.svelte +++ b/materialious/src/lib/components/Player.svelte @@ -546,7 +546,6 @@ const stringBody = new TextDecoder().decode(response.data); // position:0% for LTR text and position:100% for RTL text const cleaned = stringBody.replaceAll(/ align:start position:(?:10)?0%$/gm, ''); - // @ts-expect-error Type is acceptable, is shaka player response.data = new TextEncoder().encode(cleaned).buffer; } }); diff --git a/materialious/src/lib/store.ts b/materialious/src/lib/store.ts index f4e6556f..b8f1c98e 100644 --- a/materialious/src/lib/store.ts +++ b/materialious/src/lib/store.ts @@ -62,14 +62,13 @@ export const playerStatisticsByDefault = persisted('playerStatistics', false); export const playerMiniplayerEnabled = persisted('miniplayerEnabled', true); export const playerPlaylistHistory: Writable = writable([]); -export const playerState: Writable< - | { - data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null }; - isSyncing?: boolean; - playerElement?: HTMLMediaElement | undefined; - } - | undefined -> = writable(undefined); +export interface PlayerState { + data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null }; + isSyncing?: boolean; + playerElement?: HTMLMediaElement | undefined; +} + +export const playerState: Writable = writable(undefined); export const playertheatreModeIsActive = writable(false); export const returnYtDislikesStore = persisted('returnYtDislikes', false); diff --git a/materialious/src/routes/(app)/+layout.svelte b/materialious/src/routes/(app)/+layout.svelte index 83b18f98..f29db09b 100644 --- a/materialious/src/routes/(app)/+layout.svelte +++ b/materialious/src/routes/(app)/+layout.svelte @@ -487,7 +487,7 @@ .pip { position: fixed; bottom: 10px; - right: 0; + right: 30px; width: 400px; z-index: 99999; padding: 1em; @@ -506,6 +506,7 @@ .pip { width: 100%; bottom: 100px; + right: 0px; } .pip > .pip-info > .player { diff --git a/materialious/src/routes/(app)/watch/[slug]/+page.svelte b/materialious/src/routes/(app)/watch/[slug]/+page.svelte index 46db00de..4c89b21c 100644 --- a/materialious/src/routes/(app)/watch/[slug]/+page.svelte +++ b/materialious/src/routes/(app)/watch/[slug]/+page.svelte @@ -25,7 +25,8 @@ playlistCacheStore, playlistSettingsStore, syncPartyConnectionsStore, - syncPartyPeerStore + syncPartyPeerStore, + type PlayerState } from '$lib/store'; import ui from 'beercss'; import type { DataConnection } from 'peerjs'; @@ -71,7 +72,10 @@ let premiereTime = $state(''); let premiereUpdateInterval: NodeJS.Timeout; - if (!data.video.premiereTimestamp) { + if ( + !data.video.premiereTimestamp && + (!$playerState || $playerState.data.video.videoId !== data.video.videoId) + ) { playerState.set({ data: data, isSyncing: $syncPartyPeerStore !== null @@ -223,53 +227,61 @@ playerSyncEvents(connections[connections.length - 1]); }); - onMount(() => { + async function load(state: PlayerState) { + playerElement = state.playerElement; + + if (data.playlistId) { + await goToCurrentPlaylistItem(); + playerPlaylistHistory.set([data.video.videoId, ...$playerPlaylistHistory]); + } + + if ($interfaceAutoExpandChapters) { + expandSummery('chapter-section'); + } + + if ($syncPartyConnectionsStore) { + $syncPartyConnectionsStore.forEach((conn) => { + playerSyncEvents(conn); + }); + } + + if (playerElement) { + playerElement.addEventListener('timeupdate', () => { + if (!playerElement) return; + playerCurrentTime = playerElement.currentTime; + }); + } + + if (data.video.premiereTimestamp) { + premiereTime = humanFriendlyTimestamp(data.video.premiereTimestamp); + premiereUpdateInterval = setInterval(async () => { + data = await getWatchDetails(data.video.videoId, page.url); + + if (data.video.premiereTimestamp) { + premiereTime = humanFriendlyTimestamp(data.video.premiereTimestamp); + } else { + clearInterval(premiereUpdateInterval); + playerState.set({ ...$playerState, data: { ...data } }); + } + }, 60000); + } + } + + onMount(async () => { // Required due to needing the playerElement - let loadedPlayer = false; - playerState.subscribe(async (updatedPlayerState) => { - if (!updatedPlayerState?.playerElement || loadedPlayer) { - return; - } - loadedPlayer = true; + if ($playerState?.playerElement) { + await load($playerState); + } else { + let loadedPlayer = false; + playerState.subscribe(async (updatedPlayerState) => { + if (!updatedPlayerState?.playerElement || loadedPlayer) { + return; + } + loadedPlayer = true; - playerElement = updatedPlayerState.playerElement; - - if (data.playlistId) { - await goToCurrentPlaylistItem(); - playerPlaylistHistory.set([data.video.videoId, ...$playerPlaylistHistory]); - } - - if ($interfaceAutoExpandChapters) { - expandSummery('chapter-section'); - } - - if ($syncPartyConnectionsStore) { - $syncPartyConnectionsStore.forEach((conn) => { - playerSyncEvents(conn); - }); - } - - if (playerElement) { - playerElement.addEventListener('timeupdate', () => { - if (!playerElement) return; - playerCurrentTime = playerElement.currentTime; - }); - } - - if (data.video.premiereTimestamp) { - premiereTime = humanFriendlyTimestamp(data.video.premiereTimestamp); - premiereUpdateInterval = setInterval(async () => { - data = await getWatchDetails(data.video.videoId, page.url); - - if (data.video.premiereTimestamp) { - premiereTime = humanFriendlyTimestamp(data.video.premiereTimestamp); - } else { - clearInterval(premiereUpdateInterval); - playerState.set({ ...$playerState, data: { ...data } }); - } - }, 60000); - } - }); + await load(updatedPlayerState); + }); + } }); onDestroy(() => { diff --git a/update_versions.py b/update_versions.py index adcbf7b4..e12c3358 100644 --- a/update_versions.py +++ b/update_versions.py @@ -3,7 +3,7 @@ import os import re from datetime import datetime -LATEST_VERSION = "1.11.4" +LATEST_VERSION = "1.11.5" RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")