diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index 9c0bebe7..a7c5c886 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 123 - versionName "1.9.6" + versionCode 124 + versionName "1.9.7" 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 eb5ee56d..0f99a27a 100644 --- a/materialious/electron/materialious.metainfo.xml +++ b/materialious/electron/materialious.metainfo.xml @@ -58,7 +58,11 @@ - + + + https://github.com/Materialious/Materialious/releases/tag/1.9.7 + + https://github.com/Materialious/Materialious/releases/tag/1.9.6 diff --git a/materialious/electron/package-lock.json b/materialious/electron/package-lock.json index ec05df4b..628e9b17 100644 --- a/materialious/electron/package-lock.json +++ b/materialious/electron/package-lock.json @@ -1,16 +1,17 @@ { "name": "Materialious", - "version": "1.9.6", + "version": "1.9.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Materialious", - "version": "1.9.6", + "version": "1.9.7", "license": "MIT", "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.8/capacitor-nodejs.tgz", "chokidar": "~4.0.3", "electron-is-dev": "~2.0.0", "electron-serve": "~1.1.0", @@ -1797,6 +1798,18 @@ "node": ">= 0.4" } }, + "node_modules/capacitor-nodejs": { + "version": "1.0.0-beta.8", + "resolved": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.8/capacitor-nodejs.tgz", + "integrity": "sha512-VAMCTBqz+sA3762kFhSyzXS0unm0EpsVSHLmDYsTYPTiXXZaFcGk27vfXakRyyXJexAUG2Y5rZka5BvH6EmeZw==", + "license": "MIT", + "engines": { + "node": ">=20.10" + }, + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/materialious/electron/package.json b/materialious/electron/package.json index 146177c7..6617d3ea 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.9.6", + "version": "1.9.7", "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.8/capacitor-nodejs.tgz", "chokidar": "~4.0.3", "electron-is-dev": "~2.0.0", "electron-serve": "~1.1.0", 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.json b/materialious/package.json index b9cde933..00d43eac 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.9.6", + "version": "1.9.7", "private": true, "scripts": { "dev": "vite dev", diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte index 81399264..c2ef8a92 100644 --- a/materialious/src/lib/components/Player.svelte +++ b/materialious/src/lib/components/Player.svelte @@ -44,6 +44,7 @@ import { injectSABR } from '$lib/sabr'; import { patchYoutubeJs } from '$lib/patches/youtubejs'; import { playbackRates } from '$lib/const'; + import { EndTimeElement } from '$lib/shaka-elements/endTime'; interface Props { data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null }; @@ -387,13 +388,19 @@ playerElement ); + shaka.ui.Controls.registerElement('end_time', { + create: (parent: HTMLElement, controls: shaka.ui.Controls) => { + return new EndTimeElement(parent, controls); + } + }); + shakaUi.configure({ controlPanelElements: [ 'play_pause', Capacitor.getPlatform() === 'android' ? '' : 'volume', 'spacer', - 'chapter', 'time_and_duration', + data.video.liveNow || data.video.lengthSeconds < 240 ? '' : 'end_time', 'captions', 'overflow_menu', 'fullscreen' @@ -401,7 +408,6 @@ overflowMenuButtons: [ 'cast', 'airplay', - 'captions', 'quality', 'playback_rate', 'loop', @@ -655,6 +661,11 @@ id="player" poster={getBestThumbnail(data.video.videoThumbnails, 1251, 781)} > + {#if isEmbed} +
+ {data.video.title} +
+ {/if} {#if showVideoRetry} diff --git a/materialious/src/lib/shaka-elements/endTime.ts b/materialious/src/lib/shaka-elements/endTime.ts new file mode 100644 index 00000000..70175b19 --- /dev/null +++ b/materialious/src/lib/shaka-elements/endTime.ts @@ -0,0 +1,41 @@ +import shaka from 'shaka-player/dist/shaka-player.ui'; + +export class EndTimeElement extends shaka.ui.Element { + public video: HTMLMediaElement; + protected button: HTMLButtonElement; + + constructor(parent: HTMLElement, controls: shaka.ui.Controls) { + super(parent, controls); + + this.video = (this.player as shaka.Player).getMediaElement() as HTMLMediaElement; + this.button = document.createElement('button'); + this.button.classList.add('shaka-current-time'); + this.button.disabled = true; + (this.parent as HTMLElement).appendChild(this.button); + + this.updateTime = this.updateTime.bind(this); + (this.eventManager as shaka.util.EventManager).listen( + this.video, + 'timeupdate', + this.updateTime + ); + } + + private updateTime(): void { + const duration = this.video.duration; + const currentTime = this.video.currentTime; + + if (isNaN(duration) || isNaN(currentTime)) return; + + const remainingTime: number = duration - currentTime; + const end: Date = new Date(Date.now() + remainingTime * 1000); + + const formatted: string = end.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + hour12: true + }); + + this.button.textContent = `Ends at ${formatted}`; + } +} diff --git a/materialious/src/routes/(app)/+layout.svelte b/materialious/src/routes/(app)/+layout.svelte index 763df960..2d375497 100644 --- a/materialious/src/routes/(app)/+layout.svelte +++ b/materialious/src/routes/(app)/+layout.svelte @@ -70,6 +70,10 @@ videoId = url.pathname.split('/')[1]; } + if (videoId === 'shorts') { + videoId = url.pathname.split('/')[2]; + } + if (!videoId) { return; } diff --git a/materialious/src/routes/(app)/watch/[slug]/+page.svelte b/materialious/src/routes/(app)/watch/[slug]/+page.svelte index 318a9613..dca39497 100644 --- a/materialious/src/routes/(app)/watch/[slug]/+page.svelte +++ b/materialious/src/routes/(app)/watch/[slug]/+page.svelte @@ -72,6 +72,8 @@ let playerCurrentTime: number = $state(0); + let currentChapterStartTime: number = $state(0); + function expandSummery(id: string) { const element = document.getElementById(id); if (element) { @@ -243,6 +245,15 @@ playerElement.addEventListener('timeupdate', () => { if (!playerElement) return; playerCurrentTime = playerElement.currentTime; + + if (data.content.timestamps) { + for (const timestamp of data.content.timestamps) { + if (timestamp.time >= playerCurrentTime && timestamp.endTime <= playerCurrentTime) { + currentChapterStartTime = timestamp.time; + break; + } + } + } }); playerElement.addEventListener('ended', async () => { @@ -409,6 +420,15 @@ ui('#pause-timer'); } + + function goToChapter() { + const chaptersScrollable = document.getElementById('chapters'); + const currentTimestamp = document.getElementById(`timestamp-${currentChapterStartTime}`); + + if (!chaptersScrollable || !currentTimestamp) return; + + chaptersScrollable.scrollTop = currentTimestamp.offsetTop - chaptersScrollable.offsetTop - 550; + } @@ -599,7 +619,7 @@ {#if data.content.timestamps.length > 0}
- +
-
+
    {#each data.content.timestamps as timestamp}