Merge pull request #796 from Materialious/update/svelte-5
Update/svelte 5
This commit is contained in:
Generated
+22
@@ -124,6 +124,17 @@
|
||||
<option name="screenX" value="2220" />
|
||||
<option name="screenY" value="1080" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="dm2q" />
|
||||
<option name="id" value="dm2q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S23 Plus" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -267,6 +278,17 @@
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="motorola" />
|
||||
<option name="codename" value="maui" />
|
||||
<option name="id" value="maui" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="moto g play - 2023" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="31" />
|
||||
<option name="brand" value="google" />
|
||||
|
||||
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 86
|
||||
versionName "1.7.4"
|
||||
versionCode 87
|
||||
versionName "1.7.5"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
Generated
+6
-6
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.7.4",
|
||||
"version": "1.7.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Materialious",
|
||||
"version": "1.7.4",
|
||||
"version": "1.7.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.0",
|
||||
@@ -19,7 +19,7 @@
|
||||
"jsonfile": "^6.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^34.0.1",
|
||||
"electron": "^34.0.2",
|
||||
"electron-builder": "^25.1.8",
|
||||
"electron-rebuild": "^3.2.9",
|
||||
"typescript": "^5.7.3"
|
||||
@@ -2358,9 +2358,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "34.0.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-34.0.1.tgz",
|
||||
"integrity": "sha512-aArw5tAM80i3CKwEREnyZSM1SkARf5Jd1yBMTIdOL4pB1M+p/oDeyWSFI9Dl+vujyfJKiK4SS5+j19wna1onMw==",
|
||||
"version": "34.0.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-34.0.2.tgz",
|
||||
"integrity": "sha512-u3F+DSUlg9NaGS+9qnYmSRN8VjAnc3LJDDk1ye1uISJnh4gjG76y3681qLowsPMx4obvCP2eBINnmbLo0yT5WA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.7.4",
|
||||
"version": "1.7.5",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
@@ -32,7 +32,7 @@
|
||||
"jsonfile": "^6.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^34.0.1",
|
||||
"electron": "^34.0.2",
|
||||
"electron-builder": "^25.1.8",
|
||||
"electron-rebuild": "^3.2.9",
|
||||
"typescript": "^5.7.3"
|
||||
@@ -41,4 +41,4 @@
|
||||
"capacitor",
|
||||
"electron"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2402
-1985
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.7.4",
|
||||
"version": "1.7.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -15,8 +15,8 @@
|
||||
"@capacitor/assets": "^3.0.5",
|
||||
"@sveltejs/adapter-auto": "^3.2.5",
|
||||
"@sveltejs/adapter-static": "^3.0.5",
|
||||
"@sveltejs/kit": "^2.9.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@sveltejs/kit": "^2.17.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@types/eslint": "^8.56.7",
|
||||
"@types/event-source-polyfill": "^1.0.5",
|
||||
"@types/he": "^1.2.3",
|
||||
@@ -28,16 +28,16 @@
|
||||
"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": "^4.2.19",
|
||||
"svelte-check": "^3.8.6",
|
||||
"svelte": "^5.19.7",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tslib": "^2.7.0",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.4.9"
|
||||
"vite": "^6.0.11"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
@@ -68,11 +68,11 @@
|
||||
"mousetrap": "^1.6.5",
|
||||
"peerjs": "^1.5.4",
|
||||
"sponsorblock-api": "^0.2.4",
|
||||
"svelte-i18n": "^4.0.0",
|
||||
"svelte-i18n": "^4.0.1",
|
||||
"svelte-infinite-loading": "^1.4.0",
|
||||
"svelte-persisted-store": "^0.12.0",
|
||||
"terser": "^5.34.1",
|
||||
"vidstack": "^1.12.9",
|
||||
"vidstack": "^1.12.12",
|
||||
"youtubei.js": "^12.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 | undefined = $state();
|
||||
|
||||
const playlistLink = `/playlist/${playlist.playlistId}`;
|
||||
|
||||
@@ -49,7 +53,7 @@
|
||||
{#if playlist.videoCount > 0 && !$interfaceLowBandwidthMode}
|
||||
{#if loading}
|
||||
<progress class="circle"></progress>
|
||||
{:else if img.src !== ''}
|
||||
{:else if img && img.src !== ''}
|
||||
<img
|
||||
class="responsive"
|
||||
style="max-width: 100%;height: 100%;"
|
||||
|
||||
@@ -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(0);
|
||||
|
||||
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,20 +59,20 @@
|
||||
}
|
||||
});
|
||||
|
||||
let loading = true;
|
||||
let loaded = false;
|
||||
let loading = $state(true);
|
||||
let loaded = $state(false);
|
||||
|
||||
let img: HTMLImageElement;
|
||||
let img: HTMLImageElement | undefined = $state();
|
||||
|
||||
let progress: string | null;
|
||||
let progress: string | undefined = $state();
|
||||
if (get(playerSavePlaybackPositionStore)) {
|
||||
try {
|
||||
progress = localStorage.getItem(`v_${video.videoId}`);
|
||||
progress = localStorage.getItem(`v_${video.videoId}`) ?? undefined;
|
||||
} catch {
|
||||
progress = null;
|
||||
progress = undefined;
|
||||
}
|
||||
} else {
|
||||
progress = null;
|
||||
progress = undefined;
|
||||
}
|
||||
|
||||
let startedSideways = sideways === true;
|
||||
@@ -86,11 +90,11 @@
|
||||
|
||||
try {
|
||||
const channel = await getChannel(video.authorId, { priority: 'low' });
|
||||
const img = new Image();
|
||||
img.src = proxyGoogleImage(getBestThumbnail(channel.authorThumbnails, 75, 75));
|
||||
const loadedPfp = new Image();
|
||||
loadedPfp.src = proxyGoogleImage(getBestThumbnail(channel.authorThumbnails, 75, 75));
|
||||
|
||||
img.onload = () => {
|
||||
authorImg = img;
|
||||
loadedPfp.onload = () => {
|
||||
authorImg = loadedPfp;
|
||||
};
|
||||
} catch {}
|
||||
}
|
||||
@@ -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}
|
||||
@@ -293,7 +297,7 @@
|
||||
style="width: 100%;height: {placeholderHeight}px;"
|
||||
></div>
|
||||
{:else if loaded}
|
||||
{#if showVideoPreview && videoPreview}
|
||||
{#if showVideoPreview && videoPreview && img}
|
||||
<div style="max-width: 100%; max-height: {imgHeight}px;">
|
||||
<video
|
||||
id="video-preview"
|
||||
@@ -311,7 +315,7 @@
|
||||
>
|
||||
</video>
|
||||
</div>
|
||||
{:else}
|
||||
{:else if img}
|
||||
<img class="responsive" src={img.src} alt="Thumbnail for video" />
|
||||
{/if}
|
||||
{:else}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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,16 @@
|
||||
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 +62,7 @@
|
||||
displayContent = await getChannelContent(data.channel.authorId, { type: tab });
|
||||
}
|
||||
|
||||
let channelPfp: string;
|
||||
let channelPfp: string | undefined = $state();
|
||||
onMount(async () => {
|
||||
displayContent = await getChannelContent(data.channel.authorId, { type: 'videos' });
|
||||
|
||||
@@ -118,7 +119,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 +139,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 +160,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 +180,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,19 @@
|
||||
<script lang="ts">
|
||||
import { getHashtag } from '$lib/api';
|
||||
import type { Video } from '$lib/api/model.js';
|
||||
import VideoList from '$lib/components/VideoList.svelte';
|
||||
import { activePageStore } from '$lib/store';
|
||||
import { onMount } from 'svelte';
|
||||
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
|
||||
|
||||
export let data;
|
||||
let { data } = $props();
|
||||
|
||||
let currentPage = 1;
|
||||
$: videos = [...data.hashTagVideos.results];
|
||||
let videos: Video[] = $state([]);
|
||||
|
||||
onMount(() => {
|
||||
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,20 @@
|
||||
<script lang="ts">
|
||||
import { getFeed } from '$lib/api/index';
|
||||
import type { Notification, Video } from '$lib/api/model.js';
|
||||
import VideoList from '$lib/components/VideoList.svelte';
|
||||
import { activePageStore } from '$lib/store';
|
||||
import { onMount } from 'svelte';
|
||||
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: (Notification | Video)[] = $state([]);
|
||||
|
||||
onMount(() => {
|
||||
videos = [...data.feed.notifications, ...data.feed.videos];
|
||||
});
|
||||
|
||||
activePageStore.set('subscriptions');
|
||||
|
||||
@@ -32,6 +38,6 @@
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<VideoList bind:videos />
|
||||
<VideoList {videos} />
|
||||
|
||||
<InfiniteLoading on:infinite={loadMore} />
|
||||
|
||||
@@ -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 audioMode = $state(get(playerListenByDefaultStore));
|
||||
let player: MediaPlayerElement;
|
||||
|
||||
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 | undefined = $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,7 +2,7 @@
|
||||
import Player from '$lib/components/Player.svelte';
|
||||
import type { MediaPlayerElement } from 'vidstack/elements';
|
||||
|
||||
export let data;
|
||||
let { data } = $props();
|
||||
|
||||
let player: MediaPlayerElement;
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
|
||||
LATEST_VERSION = "1.7.4"
|
||||
LATEST_VERSION = "1.7.5"
|
||||
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
|
||||
|
||||
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")
|
||||
|
||||
Reference in New Issue
Block a user