Improved link handling
This commit is contained in:
@@ -14,7 +14,6 @@ dependencies {
|
||||
implementation project(':capacitor-clipboard')
|
||||
implementation project(':capacitor-screen-orientation')
|
||||
implementation project(':capacitor-status-bar')
|
||||
implementation project(':capgo-inappbrowser')
|
||||
implementation project(':hugotomazi-capacitor-navigation-bar')
|
||||
implementation project(':capacitor-nodejs')
|
||||
|
||||
|
||||
@@ -17,9 +17,6 @@ project(':capacitor-screen-orientation').projectDir = new File('../node_modules/
|
||||
include ':capacitor-status-bar'
|
||||
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
||||
|
||||
include ':capgo-inappbrowser'
|
||||
project(':capgo-inappbrowser').projectDir = new File('../node_modules/@capgo/inappbrowser/android')
|
||||
|
||||
include ':hugotomazi-capacitor-navigation-bar'
|
||||
project(':hugotomazi-capacitor-navigation-bar').projectDir = new File('../node_modules/@hugotomazi/capacitor-navigation-bar/android')
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.7.19",
|
||||
"version": "1.7.20",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Materialious",
|
||||
"version": "1.7.19",
|
||||
"version": "1.7.20",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.0",
|
||||
|
||||
@@ -196,7 +196,6 @@ export class ElectronCapacitorApp {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Link electron plugins into the system.
|
||||
setupCapacitorElectronPlugins();
|
||||
|
||||
|
||||
@@ -72,4 +72,4 @@
|
||||
"terser": "^5.34.1",
|
||||
"youtubei.js": "^13.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import '$lib/css/shaka-player-theme.css';
|
||||
import { getBestThumbnail } from '$lib/images';
|
||||
import { padTime, videoLength } from '$lib/time';
|
||||
import { type PhasedDescription } from '$lib/timestamps';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
|
||||
import { StatusBar, Style } from '@capacitor/status-bar';
|
||||
@@ -19,7 +20,6 @@
|
||||
import { get } from 'svelte/store';
|
||||
import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from '../api';
|
||||
import type { VideoPlay } from '../api/model';
|
||||
import { type PhasedDescription } from '../misc';
|
||||
import {
|
||||
authStore,
|
||||
instanceStore,
|
||||
|
||||
@@ -9,11 +9,6 @@ export function truncate(value: string, maxLength: number = 50): string {
|
||||
return value.length > maxLength ? `${value.substring(0, maxLength)}...` : value;
|
||||
}
|
||||
|
||||
export interface PhasedDescription {
|
||||
description: string;
|
||||
timestamps: { title: string; time: number; timePretty: string; }[];
|
||||
}
|
||||
|
||||
export function decodeHtmlCharCodes(str: string): string {
|
||||
const { decode } = he;
|
||||
return decode(str);
|
||||
|
||||
@@ -1,33 +1,69 @@
|
||||
import { type PhasedDescription, decodeHtmlCharCodes } from "./misc";
|
||||
import { decodeHtmlCharCodes } from "./misc";
|
||||
import { convertToSeconds } from "./time";
|
||||
|
||||
export function phaseDescription(content: string, usingYoutubeJs: boolean = false): PhasedDescription {
|
||||
export interface PhasedDescription {
|
||||
description: string;
|
||||
timestamps: { title: string; time: number; timePretty: string; }[];
|
||||
}
|
||||
|
||||
export function extractActualLink(url: string): string {
|
||||
const urlParams = new URLSearchParams(url.split('?')[1]);
|
||||
const actualLink = urlParams.get('q');
|
||||
if (actualLink) {
|
||||
return decodeURIComponent(actualLink);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export function processYoutubeLink(line: string): string {
|
||||
// Regex to match the <a> tag and extract the href (YouTube redirect link)
|
||||
const urlRegex = /<a href="https:\/\/www\.youtube\.com\/redirect\?([^"]+)"/;
|
||||
|
||||
const urlMatch = urlRegex.exec(line);
|
||||
|
||||
if (urlMatch) {
|
||||
// Extract the YouTube redirect URL and get the actual URL from the `q` parameter
|
||||
const redirectUrl = urlMatch[0]; // the full redirect URL with the `q` parameter
|
||||
const actualUrl = extractActualLink(redirectUrl);
|
||||
return line.replace(urlRegex, `<a href="${actualUrl}"`);
|
||||
} else {
|
||||
// If no match found, just return the original line
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
export function phaseDescription(videoId: string, content: string, fallbackPatch?: 'youtubejs' | 'piped'): PhasedDescription {
|
||||
const timestamps: { title: string; time: number; timePretty: string; }[] = [];
|
||||
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 = /&(?:\S*?&)?t=(\d+)\s*s.*?<span[^>]*>([^<]*)<\/span>.*?>(.*?)<\/span>/;
|
||||
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'
|
||||
);
|
||||
|
||||
let filteredLines: string[] = [];
|
||||
lines.forEach((line) => {
|
||||
const urlMatch = urlRegex.exec(line);
|
||||
// Use appropriate regex based on the `usingYoutubeJs` flag
|
||||
const timestampMatch = (usingYoutubeJs ? timestampRegexYtJs : timestampRegexInvidious).exec(usingYoutubeJs ? line + '</span>' : line);
|
||||
const timestampMatch = (fallbackPatch === 'youtubejs' ? timestampRegexYtJs : timestampRegexInvidious).exec(fallbackPatch === 'youtubejs' ? line + '</span>' : line);
|
||||
|
||||
if (urlMatch !== null && timestampMatch === null) {
|
||||
// If line contains a URL but not a timestamp, modify the URL
|
||||
const modifiedLine = line.replace(
|
||||
const modifiedLine = processYoutubeLink(line).replace(
|
||||
/<a href="([^"]+)"/,
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer" class="link"'
|
||||
);
|
||||
console.log(modifiedLine);
|
||||
filteredLines.push(modifiedLine);
|
||||
} else if (timestampMatch !== null) {
|
||||
// If line contains a timestamp, extract details and push into timestamps array
|
||||
const time = usingYoutubeJs ? timestampMatch[1] : timestampMatch[2];
|
||||
const timestamp = usingYoutubeJs ? timestampMatch[2] : timestampMatch[3];
|
||||
const title = usingYoutubeJs ? timestampMatch[3] || '' : timestampMatch[4] || '';
|
||||
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] || '';
|
||||
|
||||
timestamps.push({
|
||||
time: convertToSeconds(time),
|
||||
// Remove any HTML in the timestamp title.
|
||||
|
||||
@@ -153,6 +153,8 @@
|
||||
onMount(async () => {
|
||||
ui();
|
||||
|
||||
document.addEventListener('click', linkClickOverwrite);
|
||||
|
||||
scrollableRoot = document.querySelector('.root');
|
||||
|
||||
loadSettingsFromEnv();
|
||||
@@ -183,6 +185,21 @@
|
||||
}
|
||||
});
|
||||
|
||||
function linkClickOverwrite(event: MouseEvent) {
|
||||
// Handles opening links in browser for android.
|
||||
|
||||
if (Capacitor.getPlatform() !== 'android') return;
|
||||
|
||||
const link = (event.target as HTMLElement).closest('a');
|
||||
|
||||
if (link && link.href) {
|
||||
if (link.href && link.href.startsWith('http') && link.target === '_blank') {
|
||||
event.preventDefault();
|
||||
Browser.open({ url: link.href });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let webManifestLink = $derived(pwaInfo ? pwaInfo.webManifest.linkTag : '');
|
||||
</script>
|
||||
|
||||
|
||||
@@ -137,21 +137,24 @@
|
||||
<span>{$_('player.share.title')}</span>
|
||||
<menu class="no-wrap mobile">
|
||||
{#if !Capacitor.isNativePlatform()}
|
||||
<a
|
||||
href="#share"
|
||||
<button
|
||||
class="row"
|
||||
onclick={async () => {
|
||||
await Clipboard.write({ string: location.href });
|
||||
}}>{$_('player.share.materialiousLink')}</a
|
||||
}}>{$_('player.share.materialiousLink')}</button
|
||||
>
|
||||
{/if}
|
||||
<!--Ugly hack to get pass svelte error-->
|
||||
{#if true}
|
||||
<button
|
||||
class="row"
|
||||
onclick={async () => {
|
||||
await Clipboard.write({
|
||||
string: `https://www.youtube.com/channel/${data.channel.authorId}`
|
||||
});
|
||||
}}>{$_('player.share.youtubeLink')}</button
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="#share"
|
||||
onclick={async () => {
|
||||
await Clipboard.write({
|
||||
string: `https://www.youtube.com/channel/${data.channel.authorId}`
|
||||
});
|
||||
}}>{$_('player.share.youtubeLink')}</a
|
||||
>
|
||||
</menu>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function load({ params, url }) {
|
||||
|
||||
return {
|
||||
video: video,
|
||||
content: phaseDescription(video.descriptionHtml, video.fallbackPatch === 'youtubejs'),
|
||||
content: phaseDescription(video.videoId, video.descriptionHtml, video.fallbackPatch),
|
||||
playlistId: url.searchParams.get('playlist'),
|
||||
streamed: {
|
||||
personalPlaylists: personalPlaylists,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getVideo } from '$lib/api/index';
|
||||
import type { PhasedDescription } from '$lib/misc';
|
||||
import { playerProxyVideosStore } from '$lib/store';
|
||||
import type { PhasedDescription } from '$lib/timestamps';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user