Improved timestamp handling
This commit is contained in:
@@ -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">
|
||||
|
||||
Generated
+2
-2
@@ -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",
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.9.16",
|
||||
"version": "1.9.17",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -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('');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user