@@ -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.
|
||||
|
||||
@@ -58,7 +58,11 @@
|
||||
|
||||
|
||||
|
||||
<release version="1.9.6" date="2025-6-14">
|
||||
|
||||
<release version="1.9.7" date="2025-6-15">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.7</url>
|
||||
</release>
|
||||
<release version="1.9.6" date="2025-6-14">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.6</url>
|
||||
</release>
|
||||
<release version="1.9.5" date="2025-6-09">
|
||||
|
||||
Generated
+15
-2
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.9.6",
|
||||
"version": "1.9.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -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)}
|
||||
></video>
|
||||
{#if isEmbed}
|
||||
<div class="chip blur" style="position: absolute;top: 10px;left: 10px;font-size: 18px;">
|
||||
{data.video.title}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if showVideoRetry}
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,10 @@
|
||||
videoId = url.pathname.split('/')[1];
|
||||
}
|
||||
|
||||
if (videoId === 'shorts') {
|
||||
videoId = url.pathname.split('/')[2];
|
||||
}
|
||||
|
||||
if (!videoId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -599,7 +619,7 @@
|
||||
{#if data.content.timestamps.length > 0}
|
||||
<article>
|
||||
<details>
|
||||
<summary id="chapter-section" class="bold none">
|
||||
<summary id="chapter-section" class="bold none" onclick={goToChapter}>
|
||||
<nav>
|
||||
<div class="max">
|
||||
<p>{$_('player.chapters')}</p>
|
||||
@@ -608,11 +628,12 @@
|
||||
</nav>
|
||||
</summary>
|
||||
<div class="space"></div>
|
||||
<div class="chapter-list">
|
||||
<div class="chapter-list" id="chapters">
|
||||
<ul class="list">
|
||||
{#each data.content.timestamps as timestamp}
|
||||
<li
|
||||
role="presentation"
|
||||
id={`timestamp-${timestamp.time}`}
|
||||
onclick={() => {
|
||||
if (playerElement) playerElement.currentTime = timestamp.time;
|
||||
}}
|
||||
@@ -788,7 +809,8 @@
|
||||
<style>
|
||||
.chapter-list {
|
||||
max-height: 300px;
|
||||
overflow-x: scroll;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.video-actions {
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { getVideo } from '$lib/api/index';
|
||||
import { playerProxyVideosStore } from '$lib/store';
|
||||
import type { PhasedDescription } from '$lib/timestamps';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export async function load({ params }) {
|
||||
let video;
|
||||
try {
|
||||
video = await getVideo(params.slug, get(playerProxyVideosStore));
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
let video;
|
||||
try {
|
||||
video = await getVideo(params.slug, get(playerProxyVideosStore));
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
|
||||
return {
|
||||
video: video,
|
||||
content: {
|
||||
description: '',
|
||||
timestamps: []
|
||||
} as PhasedDescription,
|
||||
playlistId: null
|
||||
};
|
||||
}
|
||||
return {
|
||||
video: video,
|
||||
content: {
|
||||
description: '',
|
||||
timestamps: []
|
||||
},
|
||||
playlistId: null
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
LATEST_VERSION = "1.9.6"
|
||||
LATEST_VERSION = "1.9.7"
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user