This commit is contained in:
WardPearce
2025-02-05 18:23:33 +13:00
parent 3f4378a51d
commit f688103338
36 changed files with 394 additions and 545 deletions
+57 -294
View File
@@ -59,13 +59,13 @@
"buffer": "^6.0.3",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.44.1",
"eslint-plugin-svelte": "^2.45.1",
"i": "^0.3.7",
"jsdom": "^25.0.1",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7",
"svelte": "^5.19.7",
"svelte-check": "^3.8.6",
"svelte-check": "^4.0.0",
"tslib": "^2.7.0",
"typescript": "^5.6.3",
"vite": "^6.0.11"
@@ -3891,13 +3891,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/pug": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -4289,20 +4282,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@@ -4705,19 +4684,6 @@
"node": ">=0.6"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -4863,16 +4829,6 @@
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-crc32": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -5028,41 +4984,19 @@
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 8.10.0"
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/chownr": {
@@ -6019,16 +5953,6 @@
"node": ">=0.4.0"
}
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
@@ -8209,19 +8133,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-boolean-object": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
@@ -9710,19 +9621,6 @@
"node": ">=8"
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
@@ -9961,16 +9859,6 @@
"node": ">=10"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -10991,16 +10879,17 @@
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/redent": {
@@ -11730,86 +11619,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/sander": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
"integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es6-promise": "^3.1.2",
"graceful-fs": "^4.1.3",
"mkdirp": "^0.5.1",
"rimraf": "^2.5.2"
}
},
"node_modules/sander/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/sander/node_modules/es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
"dev": true,
"license": "MIT"
},
"node_modules/sander/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/sander/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/sander/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
@@ -12200,22 +12009,6 @@
"license": "MIT",
"peer": true
},
"node_modules/sorcery": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz",
"integrity": "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.14",
"buffer-crc32": "^1.0.0",
"minimist": "^1.2.0",
"sander": "^0.5.0"
},
"bin": {
"sorcery": "bin/sorcery"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -12597,24 +12390,57 @@
}
},
"node_modules/svelte-check": {
"version": "3.8.6",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz",
"integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==",
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.4.tgz",
"integrity": "sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
"chokidar": "^3.4.1",
"@jridgewell/trace-mapping": "^0.3.25",
"chokidar": "^4.0.1",
"fdir": "^6.2.0",
"picocolors": "^1.0.0",
"sade": "^1.7.4",
"svelte-preprocess": "^5.1.3",
"typescript": "^5.0.3"
"sade": "^1.7.4"
},
"bin": {
"svelte-check": "bin/svelte-check"
},
"engines": {
"node": ">= 18.0.0"
},
"peerDependencies": {
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
"svelte": "^4.0.0 || ^5.0.0-next.0",
"typescript": ">=5.0.0"
}
},
"node_modules/svelte-check/node_modules/fdir": {
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/svelte-check/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/svelte-eslint-parser": {
@@ -12687,69 +12513,6 @@
"svelte": "^3.48.0 || ^4 || ^5"
}
},
"node_modules/svelte-preprocess": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",
"integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@types/pug": "^2.0.6",
"detect-indent": "^6.1.0",
"magic-string": "^0.30.5",
"sorcery": "^0.11.0",
"strip-indent": "^3.0.0"
},
"engines": {
"node": ">= 16.0.0"
},
"peerDependencies": {
"@babel/core": "^7.10.2",
"coffeescript": "^2.5.1",
"less": "^3.11.3 || ^4.0.0",
"postcss": "^7 || ^8",
"postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
"pug": "^3.0.0",
"sass": "^1.26.8",
"stylus": "^0.55.0",
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"coffeescript": {
"optional": true
},
"less": {
"optional": true
},
"postcss": {
"optional": true
},
"postcss-load-config": {
"optional": true
},
"pug": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+2 -2
View File
@@ -28,13 +28,13 @@
"buffer": "^6.0.3",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.44.1",
"eslint-plugin-svelte": "^2.45.1",
"i": "^0.3.7",
"jsdom": "^25.0.1",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7",
"svelte": "^5.19.7",
"svelte-check": "^3.8.6",
"svelte-check": "^4.0.0",
"tslib": "^2.7.0",
"typescript": "^5.6.3",
"vite": "^6.0.11"
@@ -8,9 +8,13 @@
import { truncate } from '../misc';
import { interfaceLowBandwidthMode } from '../store';
export let channel: Channel;
interface Props {
channel: Channel;
}
let channelPfp: HTMLImageElement | undefined;
let { channel }: Props = $props();
let channelPfp: HTMLImageElement | undefined = $state();
onMount(async () => {
if (get(interfaceLowBandwidthMode)) return;
+11 -6
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import Comment_1 from './Comment.svelte';
import { getComments } from '$lib/api';
import { type Comment, type Comments } from '$lib/api/model';
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
@@ -6,10 +7,14 @@
import { numberWithCommas } from '$lib/time';
import { _ } from 'svelte-i18n';
export let comment: Comment;
export let videoId: string;
interface Props {
comment: Comment;
videoId: string;
}
let replies: Comments | undefined = undefined;
let { comment, videoId }: Props = $props();
let replies: Comments | undefined = $state(undefined);
async function loadReplies(continuation: string) {
try {
@@ -79,20 +84,20 @@
{#if replies}
{#each replies.comments as reply}
<svelte:self comment={reply} {videoId} />
<Comment_1 comment={reply} {videoId} />
{/each}
{/if}
{#if comment.replies && !replies}
<button
on:click={async () => loadReplies(comment.replies.continuation)}
onclick={async () => loadReplies(comment.replies.continuation)}
class="transparent replies"
>
<i class="primary-text">expand_more</i>
<span class="primary-text">{comment.replies.replyCount} {$_('replies')}</span>
</button>
{:else if replies}
<button on:click={() => (replies = undefined)} class="transparent replies">
<button onclick={() => (replies = undefined)} class="transparent replies">
<i class="primary-text">expand_less</i>
<span class="primary-text">{$_('hideReplies')}</span>
</button>
@@ -1,7 +1,12 @@
<script lang="ts">
import { onMount } from 'svelte';
interface Props {
children?: import('svelte').Snippet;
}
let largeCol = '2';
let { children }: Props = $props();
let largeCol = $state('2');
function checkWidth() {
if (innerWidth <= 1750) {
@@ -19,5 +24,5 @@
</script>
<div class="s12 m6 l{largeCol}">
<slot />
{@render children?.()}
</div>
@@ -4,7 +4,11 @@
import type { HashTag } from '../api/model';
import { truncate } from '../misc';
export let hashtag: HashTag;
interface Props {
hashtag: HashTag;
}
let { hashtag }: Props = $props();
let channelPfp: HTMLImageElement | undefined;
</script>
+6 -2
View File
@@ -4,9 +4,13 @@
import { darkModeStore, themeColorStore } from '../store';
import { getDynamicTheme } from '../theme';
export let classes = '';
interface Props {
classes?: string;
}
let fillColor = '';
let { classes = '' }: Props = $props();
let fillColor = $state('');
async function setFill() {
fillColor = (await getDynamicTheme())['--primary'];
@@ -4,7 +4,7 @@
import { proxyVideoUrl } from '../misc';
import { miniPlayerSrcStore, playerProxyVideosStore } from '../store';
let currentTime: number = 0;
let currentTime: number = $state(0);
function setTime() {
const player = (document.getElementById('video') as HTMLVideoElement) || null;
@@ -30,7 +30,7 @@
{#if $miniPlayerSrcStore && $miniPlayerSrcStore.video.formatStreams.length > 0}
<nav class="bottom no-padding">
<article class="surface-bright">
<button on:click={() => miniPlayerSrcStore.set(null)} class="s circle transparent no-margin">
<button onclick={() => miniPlayerSrcStore.set(null)} class="s circle transparent no-margin">
<i>close</i>
</button>
<div class="flex-container">
@@ -39,7 +39,7 @@
{$miniPlayerSrcStore.video.title}
</p>
<button
on:click={() => miniPlayerSrcStore.set(null)}
onclick={() => miniPlayerSrcStore.set(null)}
class="m l circle transparent no-margin"
>
<i>close</i>
@@ -49,14 +49,14 @@
></progress>
</div>
<video
on:click={() => {
onclick={() => {
goto(`/watch/${$miniPlayerSrcStore.video.videoId}?time=${currentTime}`);
miniPlayerSrcStore.set(null);
}}
id="video"
style="cursor: pointer;"
bind:currentTime
on:loadedmetadata={setTime}
onloadedmetadata={setTime}
controls={false}
autoplay
src={get(playerProxyVideosStore)
+20 -9
View File
@@ -41,16 +41,27 @@
} from '../store';
import { getDynamicTheme, setStatusBarColor } from '../theme';
export let data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
export let audioMode = false;
export let player: MediaPlayerElement;
export let isSyncing: boolean = false;
export let isEmbed: boolean = false;
export let segments: Segment[] = [];
interface Props {
data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
audioMode?: boolean;
player: MediaPlayerElement;
isSyncing?: boolean;
isEmbed?: boolean;
segments?: Segment[];
}
let src: PlayerSrc = [];
let snackBarAlert = '';
let playerIsLive = false;
let {
data,
audioMode = false,
player = $bindable(),
isSyncing = false,
isEmbed = false,
segments = $bindable([])
}: Props = $props();
let src: PlayerSrc = $state([]);
let snackBarAlert = $state('');
let playerIsLive = $state(false);
let playerPosSet = false;
let userVideoSpeed = 1;
@@ -8,12 +8,16 @@
import { truncate } from '../misc';
import { interfaceLowBandwidthMode } from '../store';
export let playlist: Playlist;
export let disabled: boolean = false;
interface Props {
playlist: Playlist;
disabled?: boolean;
}
let loading = true;
let { playlist, disabled = false }: Props = $props();
let img: HTMLImageElement;
let loading = $state(true);
let img: HTMLImageElement = $state();
const playlistLink = `/playlist/${playlist.playlistId}`;
+14 -12
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { goto } from '$app/navigation';
import Mousetrap from 'mousetrap';
import { createEventDispatcher, onMount } from 'svelte';
@@ -12,14 +14,14 @@
const dispatch = createEventDispatcher();
let searchSuggestions = false;
let searchSuggestions = $state(false);
interfaceSearchSuggestionsStore.subscribe((value) => (searchSuggestions = value));
let search: string;
let suggestionsForSearch: string[] = [];
let selectedSuggestionIndex: number = -1;
let search: string = $state();
let suggestionsForSearch: string[] = $state([]);
let selectedSuggestionIndex: number = $state(-1);
let showSearchBox = false;
let showSearchBox = $state(false);
let debounceTimer: NodeJS.Timeout;
function debouncedSearch(event: any) {
@@ -97,29 +99,29 @@
});
</script>
<form on:submit|preventDefault={handleSubmit}>
<form onsubmit={preventDefault(handleSubmit)}>
<div class="field prefix round fill no-margin search">
<i class="front">search</i>
<input
id="search-box"
placeholder={$_('searchPlaceholder')}
bind:value={search}
on:click={async () => {
onclick={async () => {
showSearchBox = true;
}}
/>
{#if showSearchBox}
<menu class="min suggestions-container">
<div class="field large prefix suffix no-margin fixed">
<i class="front" on:click={() => dispatch('searchCancelled')}>arrow_back</i>
<i class="front" onclick={() => dispatch('searchCancelled')}>arrow_back</i>
<input
placeholder={$_('searchPlaceholder')}
type="text"
autofocus
required
bind:value={search}
on:keydown={handleKeyDown}
on:keyup={(event) => {
onkeydown={handleKeyDown}
onkeyup={(event) => {
if (event.key === 'Enter') {
handleSubmit();
} else {
@@ -127,12 +129,12 @@
}
}}
/>
<i class="front" on:click={resetSearch}>close</i>
<i class="front" onclick={resetSearch}>close</i>
</div>
{#if searchSuggestions}
{#each suggestionsForSearch as suggestion, index}
<a
on:click={() => {
onclick={() => {
search = suggestion;
handleSubmit();
}}
@@ -1,15 +1,17 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import { synciousInstanceStore, synciousStore } from '../../store';
let synciousInstance = get(synciousInstanceStore);
let synciousInstance = $state(get(synciousInstanceStore));
</script>
<form
on:submit|preventDefault={() =>
synciousInstanceStore.set(ensureNoTrailingSlash(synciousInstance))}
onsubmit={preventDefault(() =>
synciousInstanceStore.set(ensureNoTrailingSlash(synciousInstance)))}
>
<nav>
<div class="field label border max">
@@ -29,7 +31,7 @@
<label class="switch">
<input
bind:checked={$synciousStore}
on:click={() => synciousStore.set(!$synciousStore)}
onclick={() => synciousStore.set(!$synciousStore)}
type="checkbox"
/>
<span></span>
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
@@ -9,11 +11,11 @@
deArrowTitlesOnly
} from '../../store';
let deArrowUrl = get(deArrowInstanceStore);
let deArrowThumbnailUrl = get(deArrowThumbnailInstanceStore);
let deArrowUrl = $state(get(deArrowInstanceStore));
let deArrowThumbnailUrl = $state(get(deArrowThumbnailInstanceStore));
</script>
<form on:submit|preventDefault={() => deArrowInstanceStore.set(ensureNoTrailingSlash(deArrowUrl))}>
<form onsubmit={preventDefault(() => deArrowInstanceStore.set(ensureNoTrailingSlash(deArrowUrl)))}>
<nav>
<div class="field label border max">
<input bind:value={deArrowUrl} name="dearrow-instance" type="text" />
@@ -25,7 +27,7 @@
</nav>
</form>
<form on:submit|preventDefault={() => deArrowThumbnailInstanceStore.set(deArrowThumbnailUrl)}>
<form onsubmit={preventDefault(() => deArrowThumbnailInstanceStore.set(deArrowThumbnailUrl))}>
<nav>
<div class="field label border max">
<input bind:value={deArrowThumbnailUrl} name="dearrow-thumbnail-instance" type="text" />
@@ -44,7 +46,7 @@
<label class="switch">
<input
bind:checked={$deArrowTitlesOnly}
on:click={() => deArrowTitlesOnly.set(!$deArrowTitlesOnly)}
onclick={() => deArrowTitlesOnly.set(!$deArrowTitlesOnly)}
type="checkbox"
/>
<span></span>
@@ -58,7 +60,7 @@
<label class="switch">
<input
bind:checked={$deArrowEnabledStore}
on:click={() => deArrowEnabledStore.set(!$deArrowEnabledStore)}
onclick={() => deArrowEnabledStore.set(!$deArrowEnabledStore)}
type="checkbox"
/>
<span></span>
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { goto } from '$app/navigation';
import { bookmarkletSaveToUrl } from '$lib/externalSettings';
import { letterCase, titleCases } from '$lib/letterCasing';
@@ -30,10 +32,10 @@
themeColorStore
} from '../../store';
let invidiousInstance = get(instanceStore);
let region = get(interfaceRegionStore);
let forceCase = get(interfaceForceCase);
let defaultPage = get(interfaceDefaultPage);
let invidiousInstance = $state(get(instanceStore));
let region = $state(get(interfaceRegionStore));
let forceCase = $state(get(interfaceForceCase));
let defaultPage = $state(get(interfaceDefaultPage));
async function setColor(color: any) {
const target = color.target;
@@ -58,12 +60,12 @@
{#if Capacitor.isNativePlatform()}
<form
on:submit|preventDefault={() => {
onsubmit={preventDefault(() => {
instanceStore.set(ensureNoTrailingSlash(invidiousInstance));
authStore.set(null);
goto('/', { replaceState: true });
ui('#dialog-settings');
}}
})}
>
<nav>
<div class="field label border max">
@@ -79,7 +81,7 @@
<div class="margin"></div>
<button on:click={toggleDarkMode} class="no-margin">
<button onclick={toggleDarkMode} class="no-margin">
{#if !$darkModeStore}
<i>dark_mode</i>
<span>{$_('layout.theme.darkMode')}</span>
@@ -91,7 +93,7 @@
<button>
<i>palette</i>
<span>{$_('layout.theme.color')}</span>
<input on:change={setColor} type="color" />
<input onchange={setColor} type="color" />
</button>
<div class="margin"></div>
@@ -106,7 +108,7 @@
<input
type="checkbox"
bind:checked={$interfaceAmoledTheme}
on:click={() => interfaceAmoledTheme.set(!$interfaceAmoledTheme)}
onclick={() => interfaceAmoledTheme.set(!$interfaceAmoledTheme)}
/>
<span></span>
</label>
@@ -123,7 +125,7 @@
<input
type="checkbox"
bind:checked={$interfaceDisplayThumbnailAvatars}
on:click={() => interfaceDisplayThumbnailAvatars.set(!$interfaceDisplayThumbnailAvatars)}
onclick={() => interfaceDisplayThumbnailAvatars.set(!$interfaceDisplayThumbnailAvatars)}
/>
<span></span>
</label>
@@ -139,7 +141,7 @@
<input
type="checkbox"
bind:checked={$interfaceSearchHistoryEnabled}
on:click={() => {
onclick={() => {
interfaceSearchHistoryEnabled.set(!$interfaceSearchHistoryEnabled);
searchHistoryStore.set([]);
}}
@@ -158,7 +160,7 @@
<input
type="checkbox"
bind:checked={$interfaceLowBandwidthMode}
on:click={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
onclick={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
/>
<span></span>
</label>
@@ -174,7 +176,7 @@
<input
type="checkbox"
bind:checked={$interfaceSearchSuggestionsStore}
on:click={() => interfaceSearchSuggestionsStore.set(!$interfaceSearchSuggestionsStore)}
onclick={() => interfaceSearchSuggestionsStore.set(!$interfaceSearchSuggestionsStore)}
/>
<span></span>
</label>
@@ -190,7 +192,7 @@
<input
type="checkbox"
bind:checked={$interfacePreviewVideoOnHoverStore}
on:click={() => interfacePreviewVideoOnHoverStore.set(!$interfacePreviewVideoOnHoverStore)}
onclick={() => interfacePreviewVideoOnHoverStore.set(!$interfacePreviewVideoOnHoverStore)}
/>
<span></span>
</label>
@@ -206,7 +208,7 @@
<input
type="checkbox"
bind:checked={$interfaceAutoExpandDesc}
on:click={() => interfaceAutoExpandDesc.set(!$interfaceAutoExpandDesc)}
onclick={() => interfaceAutoExpandDesc.set(!$interfaceAutoExpandDesc)}
/>
<span></span>
</label>
@@ -222,7 +224,7 @@
<input
type="checkbox"
bind:checked={$interfaceAutoExpandComments}
on:click={() => interfaceAutoExpandComments.set(!$interfaceAutoExpandComments)}
onclick={() => interfaceAutoExpandComments.set(!$interfaceAutoExpandComments)}
/>
<span></span>
</label>
@@ -230,7 +232,7 @@
</div>
<div class="field label suffix border">
<select name="region" bind:value={region} on:change={() => interfaceRegionStore.set(region)}>
<select name="region" bind:value={region} onchange={() => interfaceRegionStore.set(region)}>
{#each iso31661 as region}
<option selected={$interfaceRegionStore === region.alpha2} value={region.alpha2}
>{region.alpha2} - {region.name}</option
@@ -242,7 +244,7 @@
</div>
<div class="field label suffix border">
<select name="case" bind:value={forceCase} on:change={() => interfaceForceCase.set(forceCase)}>
<select name="case" bind:value={forceCase} onchange={() => interfaceForceCase.set(forceCase)}>
<option selected={$interfaceForceCase === null} value={null}>Default</option>
{#each titleCases as caseType}
<option selected={$interfaceForceCase === caseType} value={caseType}
@@ -258,7 +260,7 @@
<select
name="defaultPage"
bind:value={defaultPage}
on:change={() => interfaceDefaultPage.set(defaultPage)}
onchange={() => interfaceDefaultPage.set(defaultPage)}
>
{#each getPages() as page}
{#if !page.requiresAuth || get(authStore)}
@@ -276,7 +278,7 @@
<h6>{$_('layout.bookmarklet')}</h6>
<button
class="no-margin"
on:click={async () => await Clipboard.write({ string: bookmarkletSaveToUrl() })}
onclick={async () => await Clipboard.write({ string: bookmarkletSaveToUrl() })}
>{$_('copyUrl')}</button
>
</div>
@@ -21,7 +21,7 @@
silenceSkipperStore
} from '../../store';
let defaultLanguage = get(playerDefaultLanguage);
let defaultLanguage = $state(get(playerDefaultLanguage));
const languageNames = ISO6391.getAllCodes().map((code) =>
ISO6391.getName(code).toLocaleLowerCase()
@@ -33,7 +33,7 @@
<select
name="case"
bind:value={defaultLanguage}
on:change={() => playerDefaultLanguage.set(defaultLanguage)}
onchange={() => playerDefaultLanguage.set(defaultLanguage)}
>
{#each languageNames as language}
<option selected={$playerDefaultLanguage === language} value={language}
@@ -54,7 +54,7 @@
<input
type="checkbox"
bind:checked={$playerMiniPlayerStore}
on:click={() => playerMiniPlayerStore.set(!$playerMiniPlayerStore)}
onclick={() => playerMiniPlayerStore.set(!$playerMiniPlayerStore)}
/>
<span></span>
</label>
@@ -70,7 +70,7 @@
<input
type="checkbox"
bind:checked={$playerAutoPlayStore}
on:click={() => playerAutoPlayStore.set(!$playerAutoPlayStore)}
onclick={() => playerAutoPlayStore.set(!$playerAutoPlayStore)}
/>
<span></span>
</label>
@@ -86,7 +86,7 @@
<input
type="checkbox"
bind:checked={$playerAlwaysLoopStore}
on:click={() => playerAlwaysLoopStore.set(!$playerAlwaysLoopStore)}
onclick={() => playerAlwaysLoopStore.set(!$playerAlwaysLoopStore)}
/>
<span></span>
</label>
@@ -103,7 +103,7 @@
<input
type="checkbox"
bind:checked={$playerAndroidBgPlayer}
on:click={() => playerAndroidBgPlayer.set(!$playerAndroidBgPlayer)}
onclick={() => playerAndroidBgPlayer.set(!$playerAndroidBgPlayer)}
/>
<span></span>
</label>
@@ -119,7 +119,7 @@
<input
type="checkbox"
bind:checked={$playerAndroidLockOrientation}
on:click={() => playerAndroidLockOrientation.set(!$playerAndroidLockOrientation)}
onclick={() => playerAndroidLockOrientation.set(!$playerAndroidLockOrientation)}
/>
<span></span>
</label>
@@ -137,7 +137,7 @@
<input
type="checkbox"
bind:checked={$playerProxyVideosStore}
on:click={() => playerProxyVideosStore.set(!$playerProxyVideosStore)}
onclick={() => playerProxyVideosStore.set(!$playerProxyVideosStore)}
/>
<span></span>
</label>
@@ -154,7 +154,7 @@
<input
type="checkbox"
bind:checked={$playerSavePlaybackPositionStore}
on:click={() => playerSavePlaybackPositionStore.set(!$playerSavePlaybackPositionStore)}
onclick={() => playerSavePlaybackPositionStore.set(!$playerSavePlaybackPositionStore)}
/>
<span></span>
</label>
@@ -170,7 +170,7 @@
<input
type="checkbox"
bind:checked={$playerListenByDefaultStore}
on:click={() => playerListenByDefaultStore.set(!$playerListenByDefaultStore)}
onclick={() => playerListenByDefaultStore.set(!$playerListenByDefaultStore)}
/>
<span></span>
</label>
@@ -186,7 +186,7 @@
<input
type="checkbox"
bind:checked={$playerTheatreModeByDefaultStore}
on:click={() => playerTheatreModeByDefaultStore.set(!$playerTheatreModeByDefaultStore)}
onclick={() => playerTheatreModeByDefaultStore.set(!$playerTheatreModeByDefaultStore)}
/>
<span></span>
</label>
@@ -202,7 +202,7 @@
<input
type="checkbox"
bind:checked={$playerAutoplayNextByDefaultStore}
on:click={() => playerAutoplayNextByDefaultStore.set(!$playerAutoplayNextByDefaultStore)}
onclick={() => playerAutoplayNextByDefaultStore.set(!$playerAutoplayNextByDefaultStore)}
/>
<span></span>
</label>
@@ -218,7 +218,7 @@
<input
type="checkbox"
bind:checked={$silenceSkipperStore}
on:click={() => silenceSkipperStore.set(!$silenceSkipperStore)}
onclick={() => silenceSkipperStore.set(!$silenceSkipperStore)}
/>
<span></span>
</label>
@@ -235,7 +235,7 @@
<input
type="checkbox"
bind:checked={$playerYouTubeJsAlways}
on:click={() => playerYouTubeJsAlways.set(!$playerYouTubeJsAlways)}
onclick={() => playerYouTubeJsAlways.set(!$playerYouTubeJsAlways)}
/>
<span></span>
</label>
@@ -251,7 +251,7 @@
<input
type="checkbox"
bind:checked={$playerYouTubeJsFallback}
on:click={() => playerYouTubeJsFallback.set(!$playerYouTubeJsFallback)}
onclick={() => playerYouTubeJsFallback.set(!$playerYouTubeJsFallback)}
/>
<span></span>
</label>
@@ -1,15 +1,17 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import { returnYTDislikesInstanceStore, returnYtDislikesStore } from '../../store';
let returnYTInstance = get(returnYTDislikesInstanceStore);
let returnYTInstance = $state(get(returnYTDislikesInstanceStore));
</script>
<form
on:submit|preventDefault={() =>
returnYTDislikesInstanceStore.set(ensureNoTrailingSlash(returnYTInstance))}
onsubmit={preventDefault(() =>
returnYTDislikesInstanceStore.set(ensureNoTrailingSlash(returnYTInstance)))}
>
<nav>
<div class="field label border max">
@@ -29,7 +31,7 @@
<label class="switch">
<input
bind:checked={$returnYtDislikesStore}
on:click={() => returnYtDislikesStore.set(!$returnYtDislikesStore)}
onclick={() => returnYtDislikesStore.set(!$returnYtDislikesStore)}
type="checkbox"
/>
<span></span>
@@ -68,9 +68,9 @@
| 'ryd'
| 'api extended'
| 'sponsorblock'
| 'dearrow' = 'interface';
| 'dearrow' = $state('interface');
let dialogType = '';
let dialogType = $state('');
function checkWidth() {
if (innerWidth <= 1320) {
@@ -104,31 +104,31 @@
<div>
<nav class="tabbed small">
<a class:active={activeTab === 'interface'} on:click={() => (activeTab = 'interface')}>
<a class:active={activeTab === 'interface'} onclick={() => (activeTab = 'interface')}>
<i>grid_view</i>
<span>{$_('layout.interface')}</span>
</a>
<a class:active={activeTab === 'player'} on:click={() => (activeTab = 'player')}>
<a class:active={activeTab === 'player'} onclick={() => (activeTab = 'player')}>
<i>smart_display</i>
<span>{$_('layout.player.title')}</span>
</a>
<a class:active={activeTab === 'data'} on:click={() => (activeTab = 'data')}>
<a class:active={activeTab === 'data'} onclick={() => (activeTab = 'data')}>
<i>save</i>
<span>{$_('layout.dataPreferences.dataPreferences')}</span>
</a>
<a class:active={activeTab === 'ryd'} on:click={() => (activeTab = 'ryd')}>
<a class:active={activeTab === 'ryd'} onclick={() => (activeTab = 'ryd')}>
<i>thumb_down</i>
<span>RYD</span>
</a>
<a class:active={activeTab === 'api extended'} on:click={() => (activeTab = 'api extended')}>
<a class:active={activeTab === 'api extended'} onclick={() => (activeTab = 'api extended')}>
<i>sync</i>
<span>API Extended</span>
</a>
<a class:active={activeTab === 'sponsorblock'} on:click={() => (activeTab = 'sponsorblock')}>
<a class:active={activeTab === 'sponsorblock'} onclick={() => (activeTab = 'sponsorblock')}>
<i>block</i>
<span>Sponsorblock</span>
</a>
<a class:active={activeTab === 'dearrow'} on:click={() => (activeTab = 'dearrow')}>
<a class:active={activeTab === 'dearrow'} onclick={() => (activeTab = 'dearrow')}>
<i>keyboard_double_arrow_down</i>
<span>{$_('layout.deArrow.title')}</span>
</a>
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
@@ -10,10 +12,10 @@
sponsorBlockUrlStore
} from '../../store';
let sponsorCategoriesList: string[] = [];
let sponsorCategoriesList: string[] = $state([]);
sponsorBlockCategoriesStore.subscribe((value) => (sponsorCategoriesList = value));
let sponsorBlockInstance = get(sponsorBlockUrlStore);
let sponsorBlockInstance = $state(get(sponsorBlockUrlStore));
const sponsorCategories = [
{ name: $_('layout.sponsors.sponsor'), category: 'sponsor' },
@@ -36,8 +38,8 @@
</script>
<form
on:submit|preventDefault={() =>
sponsorBlockUrlStore.set(ensureNoTrailingSlash(sponsorBlockInstance))}
onsubmit={preventDefault(() =>
sponsorBlockUrlStore.set(ensureNoTrailingSlash(sponsorBlockInstance)))}
>
<nav>
<div class="field label border max">
@@ -57,7 +59,7 @@
<label class="switch">
<input
bind:checked={$sponsorBlockStore}
on:click={() => sponsorBlockStore.set(!$sponsorBlockStore)}
onclick={() => sponsorBlockStore.set(!$sponsorBlockStore)}
type="checkbox"
/>
<span></span>
@@ -71,7 +73,7 @@
<label class="switch">
<input
bind:checked={$sponsorBlockDisplayToastStore}
on:click={() => sponsorBlockDisplayToastStore.set(!$sponsorBlockDisplayToastStore)}
onclick={() => sponsorBlockDisplayToastStore.set(!$sponsorBlockDisplayToastStore)}
type="checkbox"
/>
<span></span>
@@ -85,7 +87,7 @@
<label class="switch">
<input
bind:checked={$sponsorBlockTimelineStore}
on:click={() => sponsorBlockTimelineStore.set(!$sponsorBlockTimelineStore)}
onclick={() => sponsorBlockTimelineStore.set(!$sponsorBlockTimelineStore)}
type="checkbox"
/>
<span></span>
@@ -106,7 +108,7 @@
<input
type="checkbox"
checked={sponsorCategoriesList.includes(sponsor.category)}
on:click={() => toggleSponsor(sponsor.category)}
onclick={() => toggleSponsor(sponsor.category)}
/>
<span></span>
</label>
@@ -4,14 +4,18 @@
import { _ } from 'svelte-i18n';
import type { Notification, PlaylistPageVideo, Video, VideoBase } from '../api/model';
export let video: VideoBase | Video | Notification | PlaylistPageVideo;
interface Props {
video: VideoBase | Video | Notification | PlaylistPageVideo;
}
let { video }: Props = $props();
</script>
{#if !Capacitor.isNativePlatform()}
<a
class="row"
href="#copy"
on:click={async () =>
onclick={async () =>
await Clipboard.write({ string: `${location.origin}/watch/${video.videoId}` })}
>
<div class="min">{$_('player.share.materialiousLink')}</div></a
@@ -20,14 +24,14 @@
<a
href="#copy"
class="row"
on:click={async () =>
onclick={async () =>
await Clipboard.write({ string: `https://redirect.invidious.io/watch?v=${video.videoId}` })}
>
<div class="min">{$_('player.share.invidiousRedirect')}</div></a
><a
class="row"
href="#copy"
on:click={async () =>
onclick={async () =>
await Clipboard.write({ string: `https://www.youtube.com/watch?v=${video.videoId}` })}
>
<div class="min">{$_('player.share.youtubeLink')}</div></a
@@ -130,7 +130,7 @@
<label class="active" for="sync-share">Share URL</label>
</div>
<button
on:click={async () => {
onclick={async () => {
await Clipboard.write({ string: `${location.origin}?sync=${$syncPartyPeerStore?.id}` });
}}
class="square round"
@@ -142,7 +142,7 @@
<div class="space"></div>
<button
class="no-margin"
on:click={startWatchSync}
onclick={startWatchSync}
data-ui={`${$syncPartyPeerStore ? '#sync-party' : ''}`}
>{#if $syncPartyPeerStore}
{$_('layout.endSyncParty')}
@@ -26,22 +26,26 @@
const dispatch = createEventDispatcher();
export let video: VideoBase | Video | Notification | PlaylistPageVideo;
export let playlistId: string = '';
export let sideways: boolean = false;
interface Props {
video: VideoBase | Video | Notification | PlaylistPageVideo;
playlistId?: string;
sideways?: boolean;
}
let thumbnailHidden: boolean = false;
let showVideoPreview: boolean = false;
let videoPreview: VideoPlay | null = null;
let videoPreviewMuted: boolean = true;
let videoPreviewVolume: number = 0.4;
let imgHeight: number;
let { video = $bindable(), playlistId = '', sideways = $bindable(false) }: Props = $props();
let authorImg: HTMLImageElement | undefined;
let thumbnailHidden: boolean = $state(false);
let showVideoPreview: boolean = $state(false);
let videoPreview: VideoPlay | null = $state(null);
let videoPreviewMuted: boolean = $state(true);
let videoPreviewVolume: number = $state(0.4);
let imgHeight: number = $state();
let authorImg: HTMLImageElement | undefined = $state();
let proxyVideos = get(playerProxyVideosStore);
let placeholderHeight: number = 0;
let placeholderHeight: number = $state(0);
let watchUrl = new URL(`${location.origin}/watch/${video.videoId}`);
@@ -55,12 +59,12 @@
}
});
let loading = true;
let loaded = false;
let loading = $state(true);
let loaded = $state(false);
let img: HTMLImageElement;
let img: HTMLImageElement = $state();
let progress: string | null;
let progress: string | null = $state();
if (get(playerSavePlaybackPositionStore)) {
try {
progress = localStorage.getItem(`v_${video.videoId}`);
@@ -274,9 +278,9 @@
{#if !thumbnailHidden}
<div class:sideways-root={sideways}>
<div
on:mouseover={previewVideo}
on:mouseleave={() => (showVideoPreview = false)}
on:focus={() => {}}
onmouseover={previewVideo}
onmouseleave={() => (showVideoPreview = false)}
onfocus={() => {}}
id="thumbnail-container"
role="region"
>
@@ -284,7 +288,7 @@
class="wave thumbnail"
href={watchUrl.toString()}
data-sveltekit-preload-data="off"
on:click={syncChangeVideo}
onclick={syncChangeVideo}
>
{#if !$interfaceLowBandwidthMode}
{#if loading}
@@ -352,7 +356,7 @@
<button
class="no-padding"
style="position: absolute; bottom: 10px; left: 10px; width: 30px; height: 30px;"
on:click={() => {
onclick={() => {
videoPreviewMuted = !videoPreviewMuted;
}}
>
@@ -10,17 +10,21 @@
import { decodeHtmlCharCodes } from '../misc';
import { instanceStore } from '../store';
export let video: VideoPlay;
export let player: MediaPlayerElement;
interface Props {
video: VideoPlay;
player: MediaPlayerElement;
}
let url: string | null = null;
let autoScroll: boolean = true;
let { video, player = $bindable() }: Props = $props();
let transcript: ParsedCaptionsResult | null = null;
let transcriptCues: VTTCue[] = [];
let isLoading = false;
let currentTime = 0;
let search: string = '';
let url: string | null = $state(null);
let autoScroll: boolean = $state(true);
let transcript: ParsedCaptionsResult | null = $state(null);
let transcriptCues: VTTCue[] = $state([]);
let isLoading = $state(false);
let currentTime = $state(0);
let search: string = $state('');
player.addEventListener('time-update', (event: MediaTimeUpdateEvent) => {
currentTime = event.detail.currentTime;
@@ -72,7 +76,7 @@
<article class="no-elevate" style="position: sticky; top: 0; z-index: 3;">
<h6>{$_('transcript')}</h6>
<div class="field label suffix border">
<select bind:value={url} on:change={loadTranscript} name="captions">
<select bind:value={url} onchange={loadTranscript} name="captions">
<option selected={true} value={null}>{$_('selectLang')}</option>
{#each video.captions as caption}
<option value={caption.url}>{caption.label}</option>
@@ -85,7 +89,7 @@
<div class="max field round suffix prefix small no-margin surface-variant">
<i class="front">search</i><input
bind:value={search}
on:input={searchTranscript}
oninput={searchTranscript}
type="text"
placeholder={$_('searchPlaceholder')}
/>
@@ -111,7 +115,7 @@
<div
class="transcript-line"
id={`transcript-line-${cue.startTime}`}
on:click={() => (player.currentTime = cue.startTime)}
onclick={() => (player.currentTime = cue.startTime)}
class:secondary-container={currentTime >= cue.startTime && currentTime <= cue.endTime}
>
<p class="chip no-margin">{videoLength(cue.startTime)}</p>
@@ -7,11 +7,15 @@
import { authStore } from '../store';
import ContentColumn from './ContentColumn.svelte';
export let videos: VideoBase[] | Video[] | Notification[] | PlaylistPageVideo[] = [];
export let playlistId: string = '';
export let playlistAuthor: string = '';
interface Props {
videos?: VideoBase[] | Video[] | Notification[] | PlaylistPageVideo[];
playlistId?: string;
playlistAuthor?: string;
}
let hiddenVideos: string[] = [];
let { videos = [], playlistId = '', playlistAuthor = '' }: Props = $props();
let hiddenVideos: string[] = $state([]);
let auth = get(authStore);
async function removePlaylistItem(indexId: string, videoId: string) {
@@ -36,7 +40,7 @@
{#if auth && decodeURIComponent(auth.username) === playlistAuthor && 'indexId' in video}
<div class="right-align" style="margin: 1em .5em;">
<button
on:click={async () => removePlaylistItem(video.indexId, video.videoId)}
onclick={async () => removePlaylistItem(video.indexId, video.videoId)}
class="tertiary circle small"
>
<i>delete</i>
+20 -15
View File
@@ -36,18 +36,23 @@
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { pwaInfo } from 'virtual:pwa-info';
interface Props {
children?: import('svelte').Snippet;
}
let mobileSearchShow = false;
let { children }: Props = $props();
let currentPage: string | null = '';
let mobileSearchShow = $state(false);
let currentPage: string | null = $state('');
activePageStore.subscribe((page) => (currentPage = page));
let isLoggedIn = false;
let isLoggedIn = $state(false);
authStore.subscribe((value) => {
isLoggedIn = value !== null;
});
let notifications: Notification[] = [];
let notifications: Notification[] = $state([]);
interfaceAmoledTheme.subscribe(async () => {
setAmoledTheme();
@@ -178,7 +183,7 @@
}
});
$: webManifestLink = pwaInfo ? pwaInfo.webManifest.linkTag : '';
let webManifestLink = $derived(pwaInfo ? pwaInfo.webManifest.linkTag : '');
</script>
<svelte:head>
@@ -200,7 +205,7 @@
<nav class="top">
{#if !mobileSearchShow}
<button
on:click={() => (mobileSearchShow = !mobileSearchShow)}
onclick={() => (mobileSearchShow = !mobileSearchShow)}
class="transparent s circle large"
>
<i>search</i>
@@ -209,16 +214,16 @@
{#if Capacitor.getPlatform() === 'electron'}
<nav class="no-space">
<button on:click={() => window.history.back()} class="border left-round">
<button onclick={() => window.history.back()} class="border left-round">
<i>arrow_back</i>
</button>
<button on:click={() => window.history.forward()} class="border right-round">
<button onclick={() => window.history.forward()} class="border right-round">
<i>arrow_forward</i>
</button>
</nav>
{/if}
<nav on:click={() => goto('/')} style="cursor: pointer;" class="m l">
<nav onclick={() => goto('/')} style="cursor: pointer;" class="m l">
<Logo />
<h6 class="l">Materialious</h6>
</nav>
@@ -243,23 +248,23 @@
</button>
{/if}
{#if isLoggedIn}
<button class="circle large transparent" on:click={() => ui('#dialog-notifications')}
<button class="circle large transparent" onclick={() => ui('#dialog-notifications')}
><i>notifications</i>
<div class="tooltip bottom">{$_('layout.notifications')}</div>
</button>
{/if}
<button class="circle large transparent" on:click={() => ui('#dialog-settings')}>
<button class="circle large transparent" onclick={() => ui('#dialog-settings')}>
<i>settings</i>
<div class="tooltip bottom">{$_('layout.settings')}</div>
</button>
{#if !isLoggedIn}
<button on:click={login} class="circle large transparent">
<button onclick={login} class="circle large transparent">
<i>login</i>
<div class="tooltip bottom">{$_('layout.login')}</div>
</button>
{:else}
<button on:click={logout} class="circle large transparent">
<button onclick={logout} class="circle large transparent">
<i>logout</i>
<div class="tooltip bottom">{$_('layout.logout')}</div>
</button>
@@ -315,7 +320,7 @@
</p>
</div>
<nav class="right-align no-space">
<button class="transparent link" on:click={() => showWarningStore.set(false)}
<button class="transparent link" onclick={() => showWarningStore.set(false)}
>Don't show again</button
>
</nav>
@@ -324,7 +329,7 @@
{#if $navigating}
<PageLoading />
{:else}
<slot />
{@render children?.()}
{/if}
<SyncParty />
+1 -1
View File
@@ -2,7 +2,7 @@
import VideoList from '$lib/components/VideoList.svelte';
import { activePageStore } from '$lib/store';
export let data;
let { data } = $props();
activePageStore.set('home');
</script>
@@ -15,15 +15,15 @@
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
import { get } from 'svelte/store';
export let data;
let { data } = $props();
activePageStore.set(null);
let isSubscribed = false;
let isSubscribed = $state(false);
let tab: 'videos' | 'playlists' | 'streams' | 'shorts' = 'videos';
let tab: 'videos' | 'playlists' | 'streams' | 'shorts' = $state('videos');
let displayContent: ChannelContentPlaylists | ChannelContentVideos | undefined = undefined;
let displayContent: ChannelContentPlaylists | ChannelContentVideos | undefined = $state(undefined);
async function loadMore(event: InfiniteEvent) {
if (typeof displayContent === 'undefined') return;
@@ -61,7 +61,7 @@
displayContent = await getChannelContent(data.channel.authorId, { type: tab });
}
let channelPfp: string;
let channelPfp: string = $state();
onMount(async () => {
displayContent = await getChannelContent(data.channel.authorId, { type: 'videos' });
@@ -118,7 +118,7 @@
<div class="grid no-padding">
<div class="s12 m12 l5">
<button
on:click={toggleSubscribed}
onclick={toggleSubscribed}
class:inverse-surface={!isSubscribed}
class:border={isSubscribed}
>
@@ -138,14 +138,14 @@
{#if !Capacitor.isNativePlatform()}
<a
href="#share"
on:click={async () => {
onclick={async () => {
await Clipboard.write({ string: location.href });
}}>{$_('player.share.materialiousLink')}</a
>
{/if}
<a
href="#share"
on:click={async () => {
onclick={async () => {
await Clipboard.write({
string: `https://www.youtube.com/channel/${data.channel.authorId}`
});
@@ -159,19 +159,19 @@
<div class="tabs left-align scroll">
{#if data.channel.tabs.includes('videos')}
<a class:active={tab === 'videos'} on:click={() => changeTab('videos')} href={`#video`}>
<a class:active={tab === 'videos'} onclick={() => changeTab('videos')} href={`#video`}>
<i>movie</i>
<span>{$_('videoTabs.videos')}</span>
</a>
{/if}
{#if data.channel.tabs.includes('shorts')}
<a class:active={tab === 'shorts'} on:click={() => changeTab('shorts')} href={`#short`}>
<a class:active={tab === 'shorts'} onclick={() => changeTab('shorts')} href={`#short`}>
<i>smartphone</i>
<span>{$_('videoTabs.shorts')}</span>
</a>
{/if}
{#if data.channel.tabs.includes('streams')}
<a class:active={tab === 'streams'} on:click={() => changeTab('streams')} href={`#stream`}>
<a class:active={tab === 'streams'} onclick={() => changeTab('streams')} href={`#stream`}>
<i>stream</i>
<span>{$_('videoTabs.streams')}</span>
</a>
@@ -179,7 +179,7 @@
{#if data.channel.tabs.includes('playlists')}
<a
class:active={tab === 'playlists'}
on:click={() => changeTab('playlists')}
onclick={() => changeTab('playlists')}
href={`#playlist`}
>
<i>playlist_add_check</i>
@@ -1,13 +1,18 @@
<script lang="ts">
import { run } from 'svelte/legacy';
import { getHashtag } from '$lib/api';
import VideoList from '$lib/components/VideoList.svelte';
import { activePageStore } from '$lib/store';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
export let data;
let { data } = $props();
let currentPage = 1;
$: videos = [...data.hashTagVideos.results];
let videos;
run(() => {
videos = [...data.hashTagVideos.results];
});
activePageStore.set('hashtag');
@@ -12,8 +12,8 @@
activePageStore.set('history');
let history: VideoPlay[] = [];
let loaded = false;
let history: VideoPlay[] = $state([]);
let loaded = $state(false);
let currentPage = 1;
@@ -61,7 +61,7 @@
<div class="space"></div>
<nav class="right-align">
<button
on:click={async () => {
onclick={async () => {
await deleteHistory();
if (get(synciousStore)) {
@@ -10,11 +10,11 @@
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
export let data;
let { data } = $props();
activePageStore.set(null);
let videos: PlaylistPageVideo[] | undefined;
let videos: PlaylistPageVideo[] | undefined = $state();
if (data.playlist.videos.length > 0) {
videos = data.playlist.videos.sort((a: PlaylistPageVideo, b: PlaylistPageVideo) => {
return a.index < b.index ? -1 : 1;
@@ -59,7 +59,7 @@
<a
href={`/watch/${unsafeRandomItem(videos).videoId}?playlist=${data.playlist.playlistId}`}
on:click={() =>
onclick={() =>
playlistSettingsStore.set({ [data.playlist.playlistId]: { shuffle: true, loop: false } })}
class="button circle extra no-margin border"
>
@@ -91,14 +91,14 @@
{#if !Capacitor.isNativePlatform()}
<a
href="#share"
on:click={async () => {
onclick={async () => {
await Clipboard.write({ string: location.href });
}}>{$_('player.share.materialiousLink')}</a
>
{/if}
<a
href="#share"
on:click={async () => {
onclick={async () => {
await Clipboard.write({
string: `https://www.youtube.com/playlist?list=${data.playlist.playlistId}`
});
@@ -1,4 +1,6 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { deletePersonalPlaylist, getPersonalPlaylists, postPersonalPlaylist } from '$lib/api';
import ContentColumn from '$lib/components/ContentColumn.svelte';
import PlaylistThumbnail from '$lib/components/PlaylistThumbnail.svelte';
@@ -6,12 +8,12 @@
import { ui } from 'beercss';
import { _ } from 'svelte-i18n';
export let data;
let { data = $bindable() } = $props();
activePageStore.set('playlists');
let playlistPrivacy: 'public' | 'private' | 'unlisted' = 'public';
let playlistTitle: string;
let playlistTitle: string = $state();
function onPrivacyChange(event: any) {
playlistPrivacy = event.currentTarget.value;
@@ -43,7 +45,7 @@
<nav class="right-align padding">
<button
on:click={async () => await removePlaylistItem(playlist.playlistId)}
onclick={async () => await removePlaylistItem(playlist.playlistId)}
class="tertiary square round"
>
<i>delete</i>
@@ -54,7 +56,7 @@
{/each}
<ContentColumn>
<article style="height: 100%;display: flex;align-items: center;justify-content: center;">
<button on:click={() => ui('#create-playlist')} class="round extra">
<button onclick={() => ui('#create-playlist')} class="round extra">
<i>add_circle</i>
<span>{$_('playlist.createPlaylist')}</span>
</button>
@@ -64,7 +66,7 @@
</div>
<dialog id="create-playlist">
<form on:submit|preventDefault={createPlaylist}>
<form onsubmit={preventDefault(createPlaylist)}>
<div class="field label border">
<input bind:value={playlistTitle} required name="title" type="text" />
<label for="title">{$_('title')}</label>
@@ -72,21 +74,21 @@
<div class="field middle-align">
<nav>
<label class="radio">
<input checked on:change={onPrivacyChange} value="public" type="radio" name="privacy" />
<input checked onchange={onPrivacyChange} value="public" type="radio" name="privacy" />
<span>{$_('playlist.public')}</span>
</label>
<label class="radio">
<input on:change={onPrivacyChange} type="radio" value="unlisted" name="privacy" />
<input onchange={onPrivacyChange} type="radio" value="unlisted" name="privacy" />
<span>{$_('playlist.unlisted')}</span>
</label>
<label class="radio">
<input on:change={onPrivacyChange} type="radio" value="private" name="privacy" />
<input onchange={onPrivacyChange} type="radio" value="private" name="privacy" />
<span>{$_('playlist.private')}</span>
</label>
</nav>
</div>
<nav class="right-align">
<button type="button" on:click={() => ui('#create-playlist')}>{$_('cancel')}</button>
<button type="button" onclick={() => ui('#create-playlist')}>{$_('cancel')}</button>
<button type="submit">{$_('create')}</button>
</nav>
</form>
@@ -10,7 +10,7 @@
import { _ } from 'svelte-i18n';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
export let data;
let { data = $bindable() } = $props();
let currentPage = 1;
@@ -44,7 +44,7 @@
<a
class:active={data.searchType === 'all'}
href="#all"
on:click={async () => changeType('all')}
onclick={async () => changeType('all')}
>
<i>home</i>
<span>{$_('videoTabs.all')}</span>
@@ -52,7 +52,7 @@
<a
class:active={data.searchType === 'video'}
href="#videos"
on:click={async () => changeType('video')}
onclick={async () => changeType('video')}
>
<i>movie</i>
<span>{$_('videoTabs.videos')}</span>
@@ -60,7 +60,7 @@
<a
class:active={data.searchType === 'playlist'}
href="#playlists"
on:click={async () => changeType('playlist')}
onclick={async () => changeType('playlist')}
>
<i>playlist_add_check</i>
<span>{$_('videoTabs.playlists')}</span>
@@ -68,7 +68,7 @@
<a
class:active={data.searchType === 'channel'}
href="#channels"
on:click={async () => changeType('channel')}
onclick={async () => changeType('channel')}
>
<i>person</i>
<span>{$_('videoTabs.channels')}</span>
@@ -1,14 +1,19 @@
<script lang="ts">
import { run } from 'svelte/legacy';
import { getFeed } from '$lib/api/index';
import VideoList from '$lib/components/VideoList.svelte';
import { activePageStore } from '$lib/store';
import { _ } from 'svelte-i18n';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
export let data;
let { data } = $props();
let currentPage = 1;
$: videos = [...data.feed.notifications, ...data.feed.videos];
let videos;
run(() => {
videos = [...data.feed.notifications, ...data.feed.videos];
});
activePageStore.set('subscriptions');
@@ -6,11 +6,11 @@
activePageStore.set(null);
export let data;
let { data } = $props();
let subscriptions = structuredClone(data.subscriptions);
let subscriptions = $state(structuredClone(data.subscriptions));
let search: string;
let search: string = $state();
const fuse = new Fuse(subscriptions, {
keys: ['author']
@@ -39,7 +39,7 @@
<div class="max field round suffix prefix small no-margin surface-variant">
<i class="front">search</i><input
bind:value={search}
on:input={searchSubs}
oninput={searchSubs}
type="text"
placeholder={$_('searchPlaceholder')}
/>
@@ -54,7 +54,7 @@
</h6></a
>
<div class="max"></div>
<button on:click={async () => unsubscribe(sub.authorId)} class="border">Unsubscribe</button>
<button onclick={async () => unsubscribe(sub.authorId)} class="border">Unsubscribe</button>
</nav>
</article>
{/each}
@@ -2,7 +2,7 @@
import VideoList from '$lib/components/VideoList.svelte';
import { activePageStore } from '$lib/store';
export let data;
let { data } = $props();
activePageStore.set('trending');
</script>
@@ -1,4 +1,6 @@
<script lang="ts">
import { run } from 'svelte/legacy';
import { goto } from '$app/navigation';
import {
addPlaylistVideo,
@@ -44,49 +46,51 @@
import type { MediaTimeUpdateEvent } from 'vidstack';
import type { MediaPlayerElement } from 'vidstack/elements';
export let data;
let { data = $bindable() } = $props();
let comments: Comments | null = null;
let comments: Comments | null = $state(null);
data.streamed.comments?.then((streamedComments) => {
comments = streamedComments;
});
let subscribed: boolean = false;
let subscribed: boolean = $state(false);
data.streamed.subscribed.then((streamedIsSubbed) => {
subscribed = streamedIsSubbed;
});
let personalPlaylists: PlaylistPage[] | null = null;
let personalPlaylists: PlaylistPage[] | null = $state(null);
data.streamed.personalPlaylists?.then((streamPlaylists) => (personalPlaylists = streamPlaylists));
activePageStore.set(null);
let playlistVideos: PlaylistPageVideo[] = [];
let playlist: PlaylistPage | null = null;
let playlistVideos: PlaylistPageVideo[] = $state([]);
let playlist: PlaylistPage | null = $state(null);
let loopPlaylist: boolean = false;
let shufflePlaylist: boolean = false;
let loopPlaylist: boolean = $state(false);
let shufflePlaylist: boolean = $state(false);
let theatreMode = get(playerTheatreModeByDefaultStore);
let theatreMode = $state(get(playerTheatreModeByDefaultStore));
let audioMode = get(playerListenByDefaultStore);
let player: MediaPlayerElement;
let audioMode = $state(get(playerListenByDefaultStore));
let player: MediaPlayerElement = $state();
let segments: Segment[] = [];
let segments: Segment[] = $state([]);
let pauseTimerSeconds: number = -1;
let pauseTimerSeconds: number = $state(-1);
let showTranscript = false;
let showTranscript = $state(false);
let playerCurrentTime: number = 0;
let playerCurrentTime: number = $state(0);
// @ts-ignore
$: if (typeof player !== 'undefined') {
playerCurrentTime = player.currentTime;
player.addEventListener(
'time-update',
(event: MediaTimeUpdateEvent) => (playerCurrentTime = event.detail.currentTime)
);
}
run(() => {
if (typeof player !== 'undefined') {
playerCurrentTime = player.currentTime;
player.addEventListener(
'time-update',
(event: MediaTimeUpdateEvent) => (playerCurrentTime = event.detail.currentTime)
);
}
});
playlistSettingsStore.subscribe((playlistSetting) => {
if (!data.playlistId) return;
@@ -411,7 +415,7 @@
theatreMode = !theatreMode;
}
let pauseTimeout: NodeJS.Timeout;
let pauseTimeout: NodeJS.Timeout = $state();
function setPauseTimer() {
if (pauseTimeout) {
clearTimeout(pauseTimeout);
@@ -487,7 +491,7 @@
</a>
{#if $authStore}
<button
on:click={toggleSubscribed}
onclick={toggleSubscribed}
class:inverse-surface={!subscribed}
class:border={subscribed}
>
@@ -529,17 +533,17 @@
{/await}
<div>
<button on:click={() => (audioMode = !audioMode)} class:border={!audioMode}>
<button onclick={() => (audioMode = !audioMode)} class:border={!audioMode}>
<i>headphones</i>
<div class="tooltip">{$_('player.audioOnly')}</div>
</button>
<button on:click={toggleTheatreMode} class="m l" class:border={!theatreMode}>
<button onclick={toggleTheatreMode} class="m l" class:border={!theatreMode}>
<i>width_wide</i>
<div class="tooltip">{$_('player.theatreMode')}</div>
</button>
{#if data.video.lengthSeconds > 60 && !data.video.hlsUrl}
<button
on:click={() => {
onclick={() => {
if (pauseTimeout) {
clearTimeout(pauseTimeout);
}
@@ -553,7 +557,7 @@
</button>
{/if}
<button
on:click={() => ((showTranscript = !showTranscript), (theatreMode = false))}
onclick={() => ((showTranscript = !showTranscript), (theatreMode = false))}
class:border={!showTranscript}
>
<i>description</i>
@@ -578,7 +582,7 @@
{#each personalPlaylists as personalPlaylist}
<a
href="#add"
on:click={async () => await toggleVideoToPlaylist(personalPlaylist.playlistId)}
onclick={async () => await toggleVideoToPlaylist(personalPlaylist.playlistId)}
>
<nav>
<span class="max">{personalPlaylist.title}</span>
@@ -629,7 +633,7 @@
{#if data.content.timestamps.length > 0}
<h6 style="margin-bottom: .3em;">Chapters</h6>
{#each data.content.timestamps as timestamp}
<button on:click={() => (player.currentTime = timestamp.time)} class="timestamps"
<button onclick={() => (player.currentTime = timestamp.time)} class="timestamps"
>{timestamp.timePretty}
{#if !timestamp.title.startsWith('-')}
-
@@ -668,7 +672,7 @@
<Comment {comment} videoId={data.video.videoId}></Comment>
{/each}
{#if comments.continuation}
<button on:click={loadMoreComments} class="margin">{$_('loadMore')}</button>
<button onclick={loadMoreComments} class="margin">{$_('loadMore')}</button>
{/if}
</div>
</details>
@@ -696,7 +700,7 @@
<p><a href={`/channel/${playlist.authorId}`}>{playlist.author}</a></p>
<nav>
<button
on:click={() => {
onclick={() => {
if (!playlist) return;
loopPlaylist = !loopPlaylist;
@@ -713,7 +717,7 @@
</div>
</button>
<button
on:click={() => {
onclick={() => {
if (!playlist) return;
shufflePlaylist = !shufflePlaylist;
@@ -788,12 +792,12 @@
<nav class="right-align no-space">
<button
class="transparent link"
on:click={() => {
onclick={() => {
ui('#pause-timer');
pauseTimerSeconds = 0;
}}>Cancel</button
>
<button disabled={pauseTimerSeconds < 1} class="transparent link" on:click={setPauseTimer}
<button disabled={pauseTimerSeconds < 1} class="transparent link" onclick={setPauseTimer}
>Confirm</button
>
</nav>
@@ -2,9 +2,9 @@
import Player from '$lib/components/Player.svelte';
import type { MediaPlayerElement } from 'vidstack/elements';
export let data;
let { data } = $props();
let player: MediaPlayerElement;
let player: MediaPlayerElement = $state();
</script>
<Player bind:player isEmbed={true} {data} />