diff --git a/materialious/electron/src/setup.ts b/materialious/electron/src/setup.ts
index e9089d23..9edbbe96 100644
--- a/materialious/electron/src/setup.ts
+++ b/materialious/electron/src/setup.ts
@@ -190,6 +190,8 @@ export class ElectronCapacitorApp {
globalShortcut.register('Control+Shift+I', () => {
this.MainWindow.webContents.toggleDevTools();
});
+ this.MainWindow.webContents.toggleDevTools();
+
// Security
this.MainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
diff --git a/materialious/package-lock.json b/materialious/package-lock.json
index 68dd8163..91874a56 100644
--- a/materialious/package-lock.json
+++ b/materialious/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "materialious",
- "version": "1.13.0",
+ "version": "1.13.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "materialious",
- "version": "1.13.0",
+ "version": "1.13.2",
"hasInstallScript": true,
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
@@ -24,6 +24,7 @@
"@macfja/svelte-persistent-store": "^2.4.2",
"beercss": "^3.13.1",
"bgutils-js": "^3.2.0",
+ "buffer": "^6.0.3",
"capacitor-music-controls-plugin": "^6.1.0",
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
"dayjs": "^1.11.19",
@@ -42,7 +43,8 @@
"shaka-player": "^4.16.11",
"sponsorblock-api": "^0.2.4",
"svelte-infinite-loading": "^1.4.0",
- "youtubei.js": "^16.0.1"
+ "youtubei.js": "^16.0.1",
+ "zod": "^4.3.5"
},
"devDependencies": {
"@capacitor/assets": "^3.0.5",
@@ -4951,6 +4953,30 @@
"readable-stream": "^3.4.0"
}
},
+ "node_modules/bl/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -5045,9 +5071,9 @@
}
},
"node_modules/buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
@@ -5065,7 +5091,7 @@
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
+ "ieee754": "^1.2.1"
}
},
"node_modules/buffer-crc32": {
@@ -14035,6 +14061,15 @@
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
"license": "MIT"
+ },
+ "node_modules/zod": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
+ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
}
}
diff --git a/materialious/package.json b/materialious/package.json
index ac476f97..9bda8ea2 100644
--- a/materialious/package.json
+++ b/materialious/package.json
@@ -57,6 +57,7 @@
"@macfja/svelte-persistent-store": "^2.4.2",
"beercss": "^3.13.1",
"bgutils-js": "^3.2.0",
+ "buffer": "^6.0.3",
"capacitor-music-controls-plugin": "^6.1.0",
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
"dayjs": "^1.11.19",
@@ -75,6 +76,7 @@
"shaka-player": "^4.16.11",
"sponsorblock-api": "^0.2.4",
"svelte-infinite-loading": "^1.4.0",
- "youtubei.js": "^16.0.1"
+ "youtubei.js": "^16.0.1",
+ "zod": "^4.3.5"
}
-}
\ No newline at end of file
+}
diff --git a/materialious/src/lib/components/ShareVideo.svelte b/materialious/src/lib/components/ShareVideo.svelte
index a5c2e5cb..9ec2c67e 100644
--- a/materialious/src/lib/components/ShareVideo.svelte
+++ b/materialious/src/lib/components/ShareVideo.svelte
@@ -1,13 +1,12 @@
@@ -46,9 +42,9 @@
role="presentation"
onclick={async () => {
if (Capacitor.isNativePlatform()) {
- shareUrl(`${get(instanceStore)}/watch/${video.videoId}`);
+ shareVideo(`${get(instanceStore)}/watch/${video.videoId}`);
} else {
- shareUrl(
+ shareVideo(
`${location.origin}${resolve('/watch/[videoId]', { videoId: video.videoId })}`,
'time'
);
@@ -62,7 +58,7 @@
class="row"
role="presentation"
onclick={async () => {
- shareUrl(`https://redirect.invidious.io/watch?v=${video.videoId}`);
+ shareVideo(`https://redirect.invidious.io/watch?v=${video.videoId}`);
}}
>
{$_('player.share.invidiousRedirect')}
@@ -72,7 +68,7 @@
class="row"
role="presentation"
onclick={async () => {
- shareUrl(`https://www.youtube.com/watch?v=${video.videoId}`);
+ shareVideo(`https://www.youtube.com/watch?v=${video.videoId}`);
}}
>
{$_('player.share.youtubeLink')}
diff --git a/materialious/src/lib/components/SyncParty.svelte b/materialious/src/lib/components/SyncParty.svelte
index b7e6848f..2945215f 100644
--- a/materialious/src/lib/components/SyncParty.svelte
+++ b/materialious/src/lib/components/SyncParty.svelte
@@ -2,15 +2,34 @@
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/stores';
- import { peerJs, removeWindowQueryFlag, setWindowQueryFlag } from '$lib/misc';
+ import {
+ determinePeerJsInstance,
+ peerJs,
+ removeWindowQueryFlag,
+ setWindowQueryFlag,
+ shareURL
+ } from '$lib/misc';
import type { PlayerEvents } from '$lib/player';
- import { Clipboard } from '@capacitor/clipboard';
import ui from 'beercss';
import type { DataConnection } from 'peerjs';
import { onDestroy, onMount } from 'svelte';
import { _ } from '$lib/i18n';
import { get } from 'svelte/store';
import { syncPartyConnectionsStore, syncPartyPeerStore } from '../store';
+ import z from 'zod';
+ import { Buffer } from 'buffer';
+ import { Capacitor } from '@capacitor/core';
+
+ const SyncInfo = z.object({
+ host: z.string(),
+ path: z.string(),
+ port: z.number(),
+ syncId: z.string()
+ });
+
+ let syncPartyManualInput: string = $state('');
+
+ const syncOrigin = Capacitor.isNativePlatform() ? 'https://sync.materialio.us' : location.origin;
function changeVideoEvent(conn: DataConnection) {
conn.on('data', (data) => {
@@ -37,6 +56,16 @@
});
}
+ function createShareBase64(): string {
+ return Buffer.from(
+ JSON.stringify({
+ ...determinePeerJsInstance(),
+ syncId: $syncPartyPeerStore?.id
+ }),
+ 'utf-8'
+ ).toString('base64');
+ }
+
async function startWatchSync() {
if ($syncPartyPeerStore) {
$syncPartyPeerStore.destroy();
@@ -89,24 +118,36 @@
}
});
+ async function joinSyncParty(syncInfo: string) {
+ const parseInfo = SyncInfo.safeParse(
+ JSON.parse(Buffer.from(syncInfo, 'base64').toString('utf-8'))
+ );
+
+ if (parseInfo.error) {
+ return;
+ }
+
+ $syncPartyPeerStore = await peerJs(crypto.randomUUID(), parseInfo.data);
+ $syncPartyPeerStore.on('open', () => {
+ if (!$syncPartyPeerStore) return;
+
+ const conn = $syncPartyPeerStore.connect(parseInfo.data.syncId);
+ syncPartyConnectionsStore.set([...($syncPartyConnectionsStore || []), conn]);
+
+ changeVideoEvent(conn);
+ });
+
+ $syncPartyPeerStore.on('disconnected', () => {
+ syncPartyPeerStore.set(null);
+ syncPartyConnectionsStore.set(null);
+ });
+ }
+
onMount(async () => {
const currentUrl = get(page).url;
- const syncId = currentUrl.searchParams.get('sync');
- if (syncId) {
- $syncPartyPeerStore = await peerJs(crypto.randomUUID());
- $syncPartyPeerStore.on('open', () => {
- if (!$syncPartyPeerStore) return;
-
- const conn = $syncPartyPeerStore.connect(syncId);
- syncPartyConnectionsStore.set([...($syncPartyConnectionsStore || []), conn]);
-
- changeVideoEvent(conn);
- });
-
- $syncPartyPeerStore.on('disconnected', () => {
- syncPartyPeerStore.set(null);
- syncPartyConnectionsStore.set(null);
- });
+ const syncInfo = currentUrl.searchParams.get('sync');
+ if (syncInfo) {
+ await joinSyncParty(syncInfo);
}
});
@@ -118,25 +159,23 @@
});
-