diff --git a/.github/workflows/prod-desktop.yml b/.github/workflows/prod-desktop.yml index 2c43d2cf..726f7bb5 100644 --- a/.github/workflows/prod-desktop.yml +++ b/.github/workflows/prod-desktop.yml @@ -20,7 +20,6 @@ jobs: node-version: [latest] runtime: - linux-x64 - - win-x64 - osx-x64 include: @@ -28,8 +27,6 @@ jobs: os: ubuntu-latest - runtime: osx-x64 os: macOS-latest - - runtime: win-x64 - os: windows-latest runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index 9a06857b..3437397b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ - Dash support. - Chapters. - Audio only mode. +- Mini player (PIP.) - Playlists. - PWA support. - YT path redirects (So your redirect plugins should still work!) diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index 10167ee9..bbb7472e 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 162 - versionName "1.11.2" + versionCode 163 + versionName "1.11.3" 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 141006f3..b76c4b6f 100644 --- a/materialious/electron/materialious.metainfo.xml +++ b/materialious/electron/materialious.metainfo.xml @@ -72,7 +72,11 @@ - + + + https://github.com/Materialious/Materialious/releases/tag/1.11.3 + + https://github.com/Materialious/Materialious/releases/tag/1.11.2 diff --git a/materialious/electron/package-lock.json b/materialious/electron/package-lock.json index 0626e891..57285d6d 100644 --- a/materialious/electron/package-lock.json +++ b/materialious/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "Materialious", - "version": "1.11.2", + "version": "1.11.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Materialious", - "version": "1.11.2", + "version": "1.11.3", "license": "MIT", "dependencies": { "@capacitor-community/electron": "^5.0.0", diff --git a/materialious/electron/package.json b/materialious/electron/package.json index b55c6f18..997c1e78 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.11.2", + "version": "1.11.3", "description": "Modern material design for Invidious.", "author": { "name": "Ward Pearce", @@ -24,6 +24,7 @@ "dependencies": { "@capacitor-community/electron": "^5.0.0", "bgutils-js": "^3.2.0", + "capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz", "chokidar": "~4.0.3", "electron-is-dev": "~2.0.0", "electron-serve": "~3.0.0", @@ -43,4 +44,4 @@ "capacitor", "electron" ] -} \ No newline at end of file +} diff --git a/materialious/electron/src/rt/electron-plugins.js b/materialious/electron/src/rt/electron-plugins.js index b33b2826..5f312366 100644 --- a/materialious/electron/src/rt/electron-plugins.js +++ b/materialious/electron/src/rt/electron-plugins.js @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/no-var-requires */ +const CapacitorNodejs = require('../../../node_modules/capacitor-nodejs/electron/dist/plugin.js'); module.exports = { + CapacitorNodejs, } \ No newline at end of file diff --git a/materialious/package-lock.json b/materialious/package-lock.json index 97446ac4..cc45d50a 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -1,12 +1,12 @@ { "name": "materialious", - "version": "1.11.0", + "version": "1.11.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "materialious", - "version": "1.11.0", + "version": "1.11.3", "hasInstallScript": true, "dependencies": { "@capacitor-community/electron": "^5.0.1", @@ -17,7 +17,7 @@ "@capacitor/clipboard": "^7.0.0", "@capacitor/core": "^7.4.3", "@capacitor/screen-orientation": "^7.0.0", - "beercss": "^3.12.11", + "beercss": "^3.12.13", "bgutils-js": "^3.2.0", "capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz", "dayjs": "^1.11.19", @@ -42,9 +42,9 @@ "@capacitor/assets": "^3.0.5", "@capacitor/cli": "^7.4.3", "@sveltejs/adapter-auto": "^6.0.1", - "@sveltejs/adapter-static": "^3.0.8", + "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.46.5", - "@sveltejs/vite-plugin-svelte": "^6.1.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/eslint": "^8.56.7", "@types/event-source-polyfill": "^1.0.5", "@types/he": "^1.2.3", @@ -56,7 +56,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.11.0", - "patch-package": "^8.0.0", + "patch-package": "^8.0.1", "prettier": "^3.6.2", "prettier-plugin-svelte": "^3.4.0", "svelte": "^5.43.2", @@ -3249,9 +3249,9 @@ } }, "node_modules/@sveltejs/adapter-static": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz", - "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3298,9 +3298,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.4.tgz", - "integrity": "sha512-4jfkfvsGI+U2OhHX8OPCKtMCf7g7ledXhs3E6UcA4EY0jQWsiVbe83pTAHp9XTifzYNOiD4AJieJUsI0qqxsbw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", + "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4294,9 +4294,9 @@ } }, "node_modules/beercss": { - "version": "3.12.11", - "resolved": "https://registry.npmjs.org/beercss/-/beercss-3.12.11.tgz", - "integrity": "sha512-9NQWL3kxSOb5RPWAV5BPVs64CS72F5Z4HgxusqD07Ho4asaTJB33/SkJBMclQeUQs7hFH2CJOgGgDnIsRk2RyQ==", + "version": "3.12.13", + "resolved": "https://registry.npmjs.org/beercss/-/beercss-3.12.13.tgz", + "integrity": "sha512-zsg15us8pSLTupIOzFFJqpJ7eJDLV6whsxT6GcRYGnYaF0ka45+HQ/u3+ldSUkCeyA5J24g6csEuMW8tqrI/0w==", "license": "MIT", "dependencies": { "material-dynamic-colors": "^1.1.2" @@ -9159,16 +9159,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -9279,9 +9269,9 @@ } }, "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "dev": true, "license": "MIT", "dependencies": { @@ -9290,15 +9280,14 @@ "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", - "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", - "tmp": "^0.0.33", + "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { @@ -9309,66 +9298,19 @@ "npm": ">5" } }, - "node_modules/patch-package/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/patch-package/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/patch-package/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/patch-package/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/patch-package/node_modules/open": { @@ -9388,20 +9330,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/patch-package/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -9412,19 +9340,6 @@ "node": ">=6" } }, - "node_modules/patch-package/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -12025,9 +11940,9 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", "engines": { diff --git a/materialious/package.json b/materialious/package.json index d4be42b9..a23b07af 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.11.2", + "version": "1.11.3", "private": true, "scripts": { "dev": "vite dev", @@ -17,9 +17,9 @@ "@capacitor/assets": "^3.0.5", "@capacitor/cli": "^7.4.3", "@sveltejs/adapter-auto": "^6.0.1", - "@sveltejs/adapter-static": "^3.0.8", + "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.46.5", - "@sveltejs/vite-plugin-svelte": "^6.1.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/eslint": "^8.56.7", "@types/event-source-polyfill": "^1.0.5", "@types/he": "^1.2.3", @@ -31,7 +31,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.11.0", - "patch-package": "^8.0.0", + "patch-package": "^8.0.1", "prettier": "^3.6.2", "prettier-plugin-svelte": "^3.4.0", "svelte": "^5.43.2", @@ -50,7 +50,7 @@ "@capacitor/clipboard": "^7.0.0", "@capacitor/core": "^7.4.3", "@capacitor/screen-orientation": "^7.0.0", - "beercss": "^3.12.11", + "beercss": "^3.12.13", "bgutils-js": "^3.2.0", "capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz", "dayjs": "^1.11.19", diff --git a/materialious/src/lib/api/index.ts b/materialious/src/lib/api/index.ts index 6a3e1705..b1971860 100644 --- a/materialious/src/lib/api/index.ts +++ b/materialious/src/lib/api/index.ts @@ -145,11 +145,15 @@ export async function getChannel( return await resp.json(); } +export type channelSortBy = 'oldest' | 'newest' | 'popular'; +export type channelContentTypes = 'videos' | 'playlists' | 'streams' | 'shorts'; + export async function getChannelContent( channelId: string, parameters: { - type?: 'videos' | 'playlists' | 'streams' | 'shorts'; + type?: channelContentTypes; continuation?: string; + sortBy?: channelSortBy; }, fetchOptions?: RequestInit ): Promise { @@ -160,10 +164,23 @@ export async function getChannelContent( if (typeof parameters.continuation !== 'undefined') url.searchParams.set('continuation', parameters.continuation); + if (typeof parameters.sortBy !== 'undefined') url.searchParams.set('sort_by', parameters.sortBy); + const resp = await fetchErrorHandle(await fetch(url.toString(), fetchOptions)); return await resp.json(); } +export async function searchChannelContent( + channelId: string, + search: string, + fetchOptions?: RequestInit +) { + const path = buildPath(`channel/${channelId}/search`); + path.search = new URLSearchParams({ q: search }).toString(); + const resp = await fetchErrorHandle(await fetch(path, fetchOptions)); + return await resp.json(); +} + export async function getSearchSuggestions( search: string, fetchOptions?: RequestInit diff --git a/materialious/src/lib/components/ItemsList.svelte b/materialious/src/lib/components/ItemsList.svelte index fa23bca5..2e77cedf 100644 --- a/materialious/src/lib/components/ItemsList.svelte +++ b/materialious/src/lib/components/ItemsList.svelte @@ -162,14 +162,6 @@ } onMount(async () => { - if ($feedLastItemId && !$isAndroidTvStore) { - document - .getElementById($feedLastItemId) - ?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' }); - - feedLastItemId.set(undefined); - } - if ($isAndroidTvStore) { await tick(); // Setup Android TV navigation diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte index bddfba86..245915aa 100644 --- a/materialious/src/lib/components/Player.svelte +++ b/materialious/src/lib/components/Player.svelte @@ -18,14 +18,13 @@ import { _ } from '$lib/i18n'; import { get } from 'svelte/store'; import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from '../api'; - import type { PlaylistPageVideo, VideoPlay } from '../api/model'; + import type { VideoPlay } from '../api/model'; import { authStore, darkModeStore, instanceStore, isAndroidTvStore, playerAndroidLockOrientation, - playerAutoplayNextByDefaultStore, playerAutoPlayStore, playerCCByDefault, playerDefaultLanguage, @@ -33,26 +32,21 @@ playerDefaultQualityStore, playerProxyVideosStore, playerSavePlaybackPositionStore, + playerState, playerStatisticsByDefault, playerYouTubeJsFallback, - playlistSettingsStore, sponsorBlockCategoriesStore, sponsorBlockDisplayToastStore, sponsorBlockStore, sponsorBlockUrlStore, synciousInstanceStore, synciousStore, - syncPartyConnectionsStore, themeColorStore } from '../store'; import { getDynamicTheme, setStatusBarColor } from '../theme'; import { patchYoutubeJs } from '$lib/patches/youtubejs'; import { goToNextVideo, goToPreviousVideo, playbackRates } from '$lib/player'; import { EndTimeElement } from '$lib/shaka-elements/endTime'; - import { loadEntirePlaylist } from '$lib/playlist'; - import { goto } from '$app/navigation'; - import { unsafeRandomItem } from '$lib/misc'; - import type { PlayerEvents } from '$lib/player'; import { dashManifestDomainInclusion } from '$lib/android/youtube/dash'; import { injectSabr } from '$lib/sabr'; import type { SabrStreamingAdapter } from 'googlevideo/sabr-streaming-adapter'; @@ -61,16 +55,12 @@ data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null }; isSyncing?: boolean; isEmbed?: boolean; - segments?: Segment[]; playerElement?: HTMLMediaElement | undefined; } - let { - data, - isEmbed = false, - segments = $bindable([]), - playerElement = $bindable(undefined) - }: Props = $props(); + let { data, isEmbed = false, playerElement = $bindable(undefined) }: Props = $props(); + + let segments: Segment[] = []; let snackBarAlert = $state(''); let originalOrigination: ScreenOrientationResult | undefined; @@ -352,7 +342,6 @@ } if (data.video.captions) { - console.log(data.video.captions); for (const caption of data.video.captions) { let captionUrl: string; if (!import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE) { @@ -470,6 +459,24 @@ }); playerElement = document.getElementById('player') as HTMLMediaElement; + if ($playerState) { + playerState.set({ ...$playerState, playerElement: playerElement }); + } + + // Due to how our player is rendered in layout for stateful pip + // we calaculate player height to then allow children pages + // to wrap around it. + function updateVideoPlayerHeight() { + const container = document.getElementById('shaka-container') as HTMLElement; + + if (container) { + const height = container.getBoundingClientRect().height; + document.documentElement.style.setProperty('--video-player-height', `${height + 10}px`); + } + } + window.addEventListener('resize', updateVideoPlayerHeight); + updateVideoPlayerHeight(); + // Change instantly to stop video from being loud for a second restoreVolumePreference(); diff --git a/materialious/src/lib/components/PlaylistThumbnail.svelte b/materialious/src/lib/components/PlaylistThumbnail.svelte index 712c585c..6b57ce8f 100644 --- a/materialious/src/lib/components/PlaylistThumbnail.svelte +++ b/materialious/src/lib/components/PlaylistThumbnail.svelte @@ -25,9 +25,6 @@ if (get(interfaceLowBandwidthMode)) return; let imgSrc = ''; - - console.log(playlist); - if (playlist.videos.length > 0) { imgSrc = getBestThumbnail(playlist.videos[0].videoThumbnails) || ''; } else if (playlist.playlistThumbnail.length > 0) { diff --git a/materialious/src/lib/components/settings/Player.svelte b/materialious/src/lib/components/settings/Player.svelte index 8492a3b0..467c1707 100644 --- a/materialious/src/lib/components/settings/Player.svelte +++ b/materialious/src/lib/components/settings/Player.svelte @@ -13,6 +13,7 @@ playerDefaultLanguage, playerDefaultPlaybackSpeed, playerDefaultQualityStore, + playerMiniplayerEnabled, playerProxyVideosStore, playerSavePlaybackPositionStore, playerStatisticsByDefault, @@ -71,7 +72,7 @@ bind:value={defaultLanguage} onchange={() => playerDefaultLanguage.set(defaultLanguage)} > - + {#each languageNames as language} +
+ +
+
- {#if $authStore} - + {:else} + - {:else} - +
+ {$_('loginRequired')} +
+ + {/if} {/if} diff --git a/materialious/src/lib/css/global.css b/materialious/src/lib/css/global.css index 481ecaca..17589e02 100644 --- a/materialious/src/lib/css/global.css +++ b/materialious/src/lib/css/global.css @@ -2,6 +2,10 @@ display: none; } +:root { + --video-player-height: 0px; +} + @font-face { font-family: Inter; src: url('/static/inter.ttf'); diff --git a/materialious/src/lib/externalSettings.ts b/materialious/src/lib/externalSettings.ts index b0978166..f96d310e 100644 --- a/materialious/src/lib/externalSettings.ts +++ b/materialious/src/lib/externalSettings.ts @@ -35,7 +35,8 @@ import { interfaceAutoExpandChapters, playerDefaultPlaybackSpeed, playerStatisticsByDefault, - playerCCByDefault + playerCCByDefault, + playerMiniplayerEnabled } from '$lib/store'; import { get, type Writable } from 'svelte/store'; @@ -223,6 +224,11 @@ const persistedStores: { name: 'CCByDefault', store: playerCCByDefault, type: 'boolean' + }, + { + name: 'miniplayerEnabled', + store: playerMiniplayerEnabled, + type: 'boolean' } ]; diff --git a/materialious/src/lib/i18n/locales/de.json b/materialious/src/lib/i18n/locales/de.json index 67a53402..cddd42ee 100644 --- a/materialious/src/lib/i18n/locales/de.json +++ b/materialious/src/lib/i18n/locales/de.json @@ -49,7 +49,9 @@ "private": "Privat", "playVideos": "Videos abspielen", "shuffleVideos": "Videos in zufälliger Reihenfolge abspielen", - "loopPlaylist": "Wiedergabeliste in Schleife abspielen" + "loopPlaylist": "Wiedergabeliste in Schleife abspielen", + "next": "Weiter", + "previous": "Vorheriges" }, "thumbnail": { "live": "LIVE", @@ -98,7 +100,8 @@ "enableYoutubejsTemp": "Nutze einmalig den lokalen Video-Fallback", "chapters": "Kapitel", "retryText": "Das Video konnte nicht geladen werden, wie möchtest du weiterfahren?", - "youtubeJsLoading": "Das Video konnte nicht geladen werden, es wird mit lokalem Video-Fallback erneut versucht." + "youtubeJsLoading": "Das Video konnte nicht geladen werden, es wird mit lokalem Video-Fallback erneut versucht.", + "premiere": "Video wird verfügbar sein" }, "layout": { "interface": "Benutzeroberfläche", @@ -188,5 +191,14 @@ "premium": "YouTube-Premium-Inhalte können auf Materialious nicht angeschaut werden.", "isUpcoming": "Video ist noch nicht in einem anschaubaren Zustand.", "version": "Version", - "contributors": "Mitwirkende" + "contributors": "Mitwirkende", + "popularPageDisabled": "Die Seite „Populär“ wurde von den Administratoren deaktiviert", + "filters": { + "filters": "Filter", + "type": "Typ", + "uploadDate": "Hochladedatum", + "duration": "Dauer", + "sortBy": "Sortieren nach", + "features": "Eigenschaften" + } } diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index 51de37fa..9511049b 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -20,7 +20,7 @@ "type": "Type", "uploadDate": "Upload date", "duration": "Duration", - "sortBy": "Short By", + "sortBy": "Sort By", "features": "Features" }, "title": "Title", @@ -45,6 +45,9 @@ "hideReplies": "Hide replies", "hideVideo": "Hide video", "defaultPage": "Default page", + "newest": "Newest", + "oldest": "Oldest", + "popular": "Popular", "syncParty": { "userJoined": "User joined your watch party.", "userLeft": "User left your watch party.", diff --git a/materialious/src/lib/i18n/locales/gsw.json b/materialious/src/lib/i18n/locales/gsw.json index 99a9582c..15f512a8 100644 --- a/materialious/src/lib/i18n/locales/gsw.json +++ b/materialious/src/lib/i18n/locales/gsw.json @@ -135,7 +135,9 @@ "private": "Privat", "playVideos": "Videos abspiele", "loopPlaylist": "Widergabelischte inere Schleife abspiele", - "shuffleVideos": "Videos in zuefälliger Reihefolg abspiele" + "shuffleVideos": "Videos in zuefälliger Reihefolg abspiele", + "next": "Wiiter", + "previous": "Vorherigs" }, "pages": { "home": "Startsiite", @@ -179,7 +181,8 @@ "retryText": "S Video het nöd chönne glade werde, wie willsch wiitermache?", "enableYoutubejsPerm": "Lokale Video-Fallback immer erlaube", "enableYoutubejsTemp": "Nutz eimalig de lokali Video-Fallback", - "youtubeJsFallBack": "Lokale Video-Fallback wird gnutzt" + "youtubeJsFallBack": "Lokale Video-Fallback wird gnutzt", + "premiere": "Video wird verfüegbar sii" }, "subscribe": "Abonniere", "invidiousLogin": "Bitte meld dich mit dim Invidious-Konto aa", @@ -188,5 +191,14 @@ "isUpcoming": "Video isch nonig imene aaluegbare Zuestand.", "premium": "YouTube-Premium-Inhält chönd uf Materialious nonig aagluegt werde.", "version": "Version", - "contributors": "Mitwirkendi" + "contributors": "Mitwirkendi", + "popularPageDisabled": "D Siite «Populär» isch vo de Administratore deaktiviert worde", + "filters": { + "filters": "Filter", + "type": "Typ", + "uploadDate": "Hochladedatum", + "duration": "Duur", + "sortBy": "Sortiere nach", + "features": "Eigeschafte" + } } diff --git a/materialious/src/lib/store.ts b/materialious/src/lib/store.ts index 8251c800..f4e6556f 100644 --- a/materialious/src/lib/store.ts +++ b/materialious/src/lib/store.ts @@ -11,9 +11,11 @@ import type { PlaylistPage, PlaylistPageVideo, Video, - VideoBase + VideoBase, + VideoPlay } from './api/model'; import { ensureNoTrailingSlash } from './misc'; +import type { PhasedDescription } from './timestamps'; function platformDependentDefault(givenValue: any, defaultValue: any): any { if (typeof givenValue !== 'undefined' && givenValue !== null) { @@ -57,8 +59,19 @@ export const playerDefaultLanguage = persisted('defaultLanguage', 'original'); export const playerCCByDefault = persisted('CCByDefault', false); export const playerDefaultPlaybackSpeed: Writable = persisted('defaultPlaybackSpeed', 1); 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 const playertheatreModeIsActive = writable(false); + export const returnYtDislikesStore = persisted('returnYtDislikes', false); export const returnYTDislikesInstanceStore: Writable = persisted( 'returnYTDislikesInstance', @@ -146,4 +159,3 @@ export const playlistCacheStore: Writable<{ }> = writable({}); export const isAndroidTvStore: Writable = writable(false); -export const isTvRouteLoading: Writable = writable(false); diff --git a/materialious/src/lib/watch.ts b/materialious/src/lib/watch.ts index 38299bee..f79562b3 100644 --- a/materialious/src/lib/watch.ts +++ b/materialious/src/lib/watch.ts @@ -10,6 +10,7 @@ import { loadEntirePlaylist } from '$lib/playlist'; import { authStore, playerProxyVideosStore, + playerState, returnYTDislikesInstanceStore, returnYtDislikesStore } from '$lib/store'; @@ -19,11 +20,18 @@ import { get } from 'svelte/store'; import { _ } from './i18n'; export async function getWatchDetails(videoId: string, url: URL) { + const playerStateRetrieved = get(playerState); + let video; - try { - video = await getVideo(videoId, get(playerProxyVideosStore), { priority: 'high' }); - } catch (errorMessage: any) { - error(500, errorMessage); + + if (playerStateRetrieved && playerStateRetrieved.data.video.videoId === videoId) { + video = playerStateRetrieved.data.video; + } else { + try { + video = await getVideo(videoId, get(playerProxyVideosStore), { priority: 'high' }); + } catch (errorMessage: any) { + error(500, errorMessage); + } } if (video.premium) { diff --git a/materialious/src/routes/(app)/+layout.svelte b/materialious/src/routes/(app)/+layout.svelte index bef93813..2f576fba 100644 --- a/materialious/src/routes/(app)/+layout.svelte +++ b/materialious/src/routes/(app)/+layout.svelte @@ -12,6 +12,7 @@ import Settings from '$lib/components/settings/Settings.svelte'; import SyncParty from '$lib/components/SyncParty.svelte'; import Thumbnail from '$lib/components/Thumbnail.svelte'; + import Player from '$lib/components/Player.svelte'; import '$lib/css/global.css'; import { bookmarkletLoadFromUrl, loadSettingsFromEnv } from '$lib/externalSettings'; import { getPages } from '$lib/navPages'; @@ -23,6 +24,8 @@ interfaceAmoledTheme, interfaceDefaultPage, isAndroidTvStore, + playerState, + playertheatreModeIsActive, playlistCacheStore, searchCacheStore, syncPartyPeerStore, @@ -40,6 +43,9 @@ import { get } from 'svelte/store'; import { pwaInfo } from 'virtual:pwa-info'; import Mousetrap from 'mousetrap'; + import { truncate } from '$lib/misc'; + import Author from '$lib/components/watch/Author.svelte'; + import LikesDislikes from '$lib/components/watch/LikesDislikes.svelte'; let { children } = $props(); @@ -52,6 +58,12 @@ let notifications: Notification[] = $state([]); + let playerIsPip: boolean = $state(false); + + page.subscribe((pageData) => { + playerIsPip = !pageData.url.pathname.includes('/watch'); + }); + interfaceAmoledTheme.subscribe(async () => { setAmoledTheme(); @@ -383,6 +395,42 @@
+ {#if $playerState} +
+
+ {#if playerIsPip} + +
+ {/if} +
+ {#key $playerState.data.video.videoId} + + {/key} +
+ {#if playerIsPip} + + {/if} +
+
+ {/if} + {#if $navigating} {:else} @@ -425,4 +473,23 @@ min-inline-size: 0.5rem; padding: 0; } + + .pip { + position: fixed; + bottom: 10px; + right: 0; + width: 400px; + z-index: 99999; + padding: 1em; + border: 0.0625rem solid var(--outline-variant); + background-color: var(--surface-container-low); + color: var(--on-surface); + border-radius: 0.75rem; + } + + @media only screen and (max-width: 993px) { + .pip { + width: 100%; + } + } diff --git a/materialious/src/routes/(app)/channel/[slug]/+page.svelte b/materialious/src/routes/(app)/channel/[slug]/+page.svelte index 0e3868d3..33fc6592 100644 --- a/materialious/src/routes/(app)/channel/[slug]/+page.svelte +++ b/materialious/src/routes/(app)/channel/[slug]/+page.svelte @@ -1,9 +1,15 @@ @@ -15,7 +14,7 @@ -{#if $isTvRouteLoading || $navigating} +{#if $navigating} {:else} {@render children?.()} diff --git a/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts b/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts index 57c51522..05e9da2e 100644 --- a/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts +++ b/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts @@ -1,9 +1,5 @@ -import { isTvRouteLoading } from '$lib/store'; import { getWatchDetails } from '$lib/watch'; export async function load({ params, url }) { - isTvRouteLoading.set(true); - const details = await getWatchDetails(params.slug, url); - isTvRouteLoading.set(false); - return details; + return await getWatchDetails(params.slug, url); } diff --git a/update_versions.py b/update_versions.py index 6cf8998d..51d9074d 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.2" +LATEST_VERSION = "1.11.3" 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")