Improved timestamp handling

This commit is contained in:
WardPearce
2025-06-25 00:23:03 +12:00
parent ed3c9f2cd6
commit 76fad817c2
12 changed files with 73 additions and 63 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 133
versionName "1.9.16"
versionCode 134
versionName "1.9.17"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -68,7 +68,11 @@
<release version="1.9.16" date="2025-6-24">
<release version="1.9.17" date="2025-6-25">
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.17</url>
</release>
<release version="1.9.16" date="2025-6-24">
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.16</url>
</release>
<release version="1.9.15" date="2025-6-23">
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "Materialious",
"version": "1.9.16",
"version": "1.9.17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
"version": "1.9.16",
"version": "1.9.17",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.9.16",
"version": "1.9.17",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
@@ -44,4 +44,4 @@
"capacitor",
"electron"
]
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.9.16",
"version": "1.9.17",
"private": true,
"scripts": {
"dev": "vite dev",
-1
View File
@@ -1,4 +1,3 @@
// src/lib/requestQueue.ts
import { writable, type Writable } from 'svelte/store';
import type { SynciousProgressModel } from './model';
import { getVideoProgress } from '.';
@@ -61,14 +61,14 @@
isSyncing?: boolean;
isEmbed?: boolean;
segments?: Segment[];
playerElement: HTMLMediaElement | undefined;
playerElement?: HTMLMediaElement | undefined;
}
let {
data,
isEmbed = false,
segments = $bindable([]),
playerElement = $bindable()
playerElement = $bindable(undefined)
}: Props = $props();
let snackBarAlert = $state('');
-1
View File
@@ -5,7 +5,6 @@ import type Peer from 'peerjs';
import { get } from 'svelte/store';
import { instanceStore, interfaceAllowInsecureRequests } from './store';
import type { Channel, HashTag, Playlist, PlaylistPageVideo, Video, VideoBase } from './api/model';
import { Capacitor } from '@capacitor/core';
export function truncate(value: string, maxLength: number = 50): string {
return value.length > maxLength ? `${value.substring(0, maxLength)}...` : value;
+1 -1
View File
@@ -16,7 +16,7 @@ import type {
import { ensureNoTrailingSlash } from './misc';
function platformDependentDefault(givenValue: any, defaultValue: any): any {
if (typeof givenValue !== 'undefined' && typeof givenValue !== null) {
if (typeof givenValue !== 'undefined' && givenValue !== null) {
return givenValue;
} else if (defaultValue && Capacitor.getPlatform() !== 'web') {
return defaultValue;
+56 -46
View File
@@ -41,64 +41,74 @@ export function phaseDescription(
): PhasedDescription {
const timestamps: Timestamps = [];
const lines = content.split('\n');
// Regular expressions for different timestamp formats
const urlRegex = /<a href="([^"]+)"/;
const timestampRegexInvidious =
/<a href="([^"]+)" data-onclick="jump_to_time" data-jump-time="(\d+)">(\d+:\d+(?::\d+)?)<\/a>\s*(.+)/;
const timestampRegexYtJs = new RegExp(
`href="https://www\\.youtube\\.com/watch\\?v=${videoId}(?:&t=(\\d+)s)?"[^>]*>\\s*<span[^>]*>\\s*([^<]+)\\s*</span>\\s*</a>\\s*<span[^>]*>\\s*([^<]+)\\s*</span>`,
'i'
);
const filteredLines: string[] = [];
const parser = new DOMParser();
lines.forEach((line) => {
const urlMatch = urlRegex.exec(line);
// Use appropriate regex based on the `usingYoutubeJs` flag
const timestampMatch = (
fallbackPatch === 'youtubejs' ? timestampRegexYtJs : timestampRegexInvidious
).exec(fallbackPatch === 'youtubejs' ? line + '</span>' : line);
const doc = parser.parseFromString(line, 'text/html');
const link = doc.querySelector('a');
if (urlMatch !== null && timestampMatch === null) {
// If line contains a URL but not a timestamp, modify the URL
const modifiedLine = processYoutubeLink(line).replace(
/<a href="([^"]+)"/,
'<a href="$1" target="_blank" rel="noopener noreferrer" class="link"'
);
filteredLines.push(modifiedLine);
} else if (timestampMatch !== null) {
// If line contains a timestamp, extract details and push into timestamps array
const time = (fallbackPatch === 'youtubejs' ? timestampMatch[1] : timestampMatch[2]) || '0';
const timestamp = fallbackPatch === 'youtubejs' ? timestampMatch[2] : timestampMatch[3];
const title =
fallbackPatch === 'youtubejs' ? timestampMatch[3] || '' : timestampMatch[4] || '';
if (link) {
const href = link.getAttribute('href') || '';
timestamps.push({
time: convertToSeconds(time),
// Remove any HTML in the timestamp title.
title: decodeHtmlCharCodes(
title
.replace(/<[^>]+>/g, '')
.replace(/\n/g, '')
.trim()
),
timePretty: timestamp,
endTime: -1
});
// Handle youtubejs timestamps
if (
fallbackPatch === 'youtubejs' &&
href.includes(`https://www.youtube.com/watch?v=${videoId}`)
) {
const url = new URL(href);
const timeParam = url.searchParams.get('t') || '0';
const timePretty = link.textContent?.trim() || '';
const spans = doc.querySelectorAll('span');
const title = spans.length > 1 ? spans[1].textContent?.trim() || '' : '';
timestamps.push({
time: convertToSeconds(timeParam.replace('s', '')),
title: decodeHtmlCharCodes(title),
timePretty,
endTime: -1
});
}
// Handle invidious-like timestamps
else if (
fallbackPatch !== 'youtubejs' &&
link.hasAttribute('data-onclick') &&
link.getAttribute('data-onclick') === 'jump_to_time'
) {
const timePretty = link.textContent?.trim() || '';
const time = link.getAttribute('data-jump-time') || '0';
// Get remaining text after the link for title
const title = link.nextSibling?.textContent?.trim() || '';
timestamps.push({
time: convertToSeconds(time),
title: decodeHtmlCharCodes(title),
timePretty,
endTime: -1
});
}
// Normal link, modify to not use youtube redirect
else {
const modifiedLine = processYoutubeLink(line).replace(
/<a href="([^"]+)"/,
'<a href="$1" target="_blank" rel="noopener noreferrer" class="link"'
);
filteredLines.push(modifiedLine);
}
} else {
filteredLines.push(line);
}
});
// Set endTime for each timestamp
timestamps.forEach((ts, idx) => {
if (idx < timestamps.length - 1) {
ts.endTime = timestamps[idx + 1].time;
} else {
ts.endTime = -1;
}
ts.endTime = idx < timestamps.length - 1 ? timestamps[idx + 1].time : -1;
});
const filteredContent = filteredLines.join('\n');
return { description: filteredContent, timestamps: timestamps };
return { description: filteredContent, timestamps };
}
@@ -2,8 +2,6 @@
import Player from '$lib/components/Player.svelte';
let { data } = $props();
let playerElement: HTMLMediaElement;
</script>
<Player bind:playerElement isEmbed={true} {data} />
<Player isEmbed={true} {data} />
+1 -1
View File
@@ -3,7 +3,7 @@ import os
import re
from datetime import datetime
LATEST_VERSION = "1.9.16"
LATEST_VERSION = "1.9.17"
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")