Moved to Melt's avatar builder for image loading
This commit is contained in:
@@ -4,10 +4,12 @@
|
||||
import type { Image } from '$lib/api/model';
|
||||
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
|
||||
import { isYTBackend, truncate } from '$lib/misc';
|
||||
import { invidiousAuthStore, interfaceLowBandwidthMode, isAndroidTvStore } from '$lib/store';
|
||||
import { invidiousAuthStore, isAndroidTvStore } from '$lib/store';
|
||||
import { _ } from '$lib/i18n';
|
||||
import { localDb } from '$lib/dexie';
|
||||
import { onMount } from 'svelte';
|
||||
import { Avatar } from 'melt/builders';
|
||||
import { mergeAttrs } from 'melt';
|
||||
|
||||
let {
|
||||
channel,
|
||||
@@ -49,19 +51,21 @@
|
||||
favoritedChannel = true;
|
||||
}
|
||||
}
|
||||
|
||||
const avatar = new Avatar({ src: proxyGoogleImage(getBestThumbnail(channel.authorThumbnails)) });
|
||||
console.log(avatar.src);
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<a href={resolve(`/channel/[authorId]`, { authorId: channel.authorId })}>
|
||||
<nav style="gap: 0.5em;">
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<img
|
||||
loading="lazy"
|
||||
class="circle large"
|
||||
src={proxyGoogleImage(getBestThumbnail(channel.authorThumbnails))}
|
||||
alt="Channel profile"
|
||||
/>
|
||||
{/if}
|
||||
<img class="circle large" {...avatar.image} alt="Channel profile" />
|
||||
<button
|
||||
class="large secondary-container"
|
||||
{...mergeAttrs(avatar.fallback, {
|
||||
style: 'text-transform: uppercase;border-radius: 2.5rem !important;'
|
||||
})}>{channel.author[0]}</button
|
||||
>
|
||||
<div>
|
||||
<p style="margin: 0;" class="bold">
|
||||
{$isAndroidTvStore ? channel.author : truncate(channel.author, 16)}
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
interfaceDefaultPage,
|
||||
interfaceDisableAutoUpdate,
|
||||
interfaceForceCase,
|
||||
interfaceLowBandwidthMode,
|
||||
interfaceRegionStore,
|
||||
interfaceSearchHistoryEnabled,
|
||||
interfaceSearchSuggestionsStore,
|
||||
@@ -280,23 +279,6 @@
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.lowBandwidthMode')}</div>
|
||||
</div>
|
||||
<label class="switch" tabindex="0">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$interfaceLowBandwidthMode}
|
||||
onclick={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
|
||||
role="switch"
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { getBestThumbnail, insecureRequestImageHandler, proxyGoogleImage } from '$lib/images';
|
||||
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
|
||||
import { cleanNumber } from '$lib/numbers';
|
||||
import { onMount } from 'svelte';
|
||||
import { _ } from '$lib/i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { Channel } from '$lib/api/model';
|
||||
import { truncate } from '$lib/misc';
|
||||
import { interfaceLowBandwidthMode } from '$lib/store';
|
||||
import { Avatar } from 'melt/builders';
|
||||
import { mergeAttrs } from 'melt';
|
||||
|
||||
interface Props {
|
||||
channel: Channel;
|
||||
@@ -15,18 +14,7 @@
|
||||
|
||||
let { channel }: Props = $props();
|
||||
|
||||
let channelPfp: HTMLImageElement | undefined = $state();
|
||||
|
||||
onMount(async () => {
|
||||
if (get(interfaceLowBandwidthMode)) return;
|
||||
|
||||
const img = await insecureRequestImageHandler(
|
||||
proxyGoogleImage(getBestThumbnail(channel.authorThumbnails))
|
||||
);
|
||||
img.onload = () => {
|
||||
channelPfp = img;
|
||||
};
|
||||
});
|
||||
const avatar = new Avatar({ src: proxyGoogleImage(getBestThumbnail(channel.authorThumbnails)) });
|
||||
</script>
|
||||
|
||||
<a
|
||||
@@ -35,21 +23,20 @@
|
||||
style="min-width: 100%;min-height: 100%;"
|
||||
>
|
||||
<div class="padding">
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<div class="center-align">
|
||||
{#if !channelPfp}
|
||||
<progress class="circle"></progress>
|
||||
{:else}
|
||||
<img
|
||||
loading="lazy"
|
||||
class="circle"
|
||||
style="width: 90px;height: 90px;"
|
||||
src={channelPfp.src}
|
||||
alt={channel.author}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="center-align">
|
||||
<img
|
||||
{...avatar.image}
|
||||
class="circle"
|
||||
style="width: 90px;height: 90px;"
|
||||
alt={channel.author}
|
||||
/>
|
||||
<button
|
||||
class="secondary-container large"
|
||||
{...mergeAttrs(avatar.fallback, {
|
||||
style: 'text-transform: uppercase;border-radius: 2.5rem !important;'
|
||||
})}>{channel.author[0]}</button
|
||||
>
|
||||
</div>
|
||||
<h5 class="center-align">{truncate(channel.author, 14)}</h5>
|
||||
<h6 style="margin-top: 0;" class="center-align grey-text medium-text">
|
||||
{cleanNumber(channel.subCount)}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { getBestThumbnail, insecureRequestImageHandler } from '$lib/images';
|
||||
import { getBestThumbnail } from '$lib/images';
|
||||
import { resolve } from '$app/paths';
|
||||
import { letterCase } from '$lib/letterCasing';
|
||||
import { onMount } from 'svelte';
|
||||
import { _ } from '$lib/i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { Playlist, PlaylistPage } from '$lib/api/model';
|
||||
import { truncate } from '$lib/misc';
|
||||
import { interfaceLowBandwidthMode } from '$lib/store';
|
||||
import { Avatar } from 'melt/builders';
|
||||
import { mergeAttrs } from 'melt';
|
||||
|
||||
interface Props {
|
||||
playlist: Playlist | PlaylistPage;
|
||||
@@ -20,21 +19,14 @@
|
||||
|
||||
const playlistLink = resolve('/playlist/[playlistId]', { playlistId: playlist.playlistId });
|
||||
|
||||
onMount(async () => {
|
||||
if (get(interfaceLowBandwidthMode)) return;
|
||||
let thumbnailSrc = '';
|
||||
if (playlist.videos && playlist.videos.length > 0) {
|
||||
thumbnailSrc = getBestThumbnail(playlist.videos[0].videoThumbnails) || '';
|
||||
} else if (playlist.playlistThumbnail) {
|
||||
thumbnailSrc = playlist.playlistThumbnail;
|
||||
}
|
||||
|
||||
let imgSrc;
|
||||
if (playlist.videos && playlist.videos.length > 0) {
|
||||
imgSrc = getBestThumbnail(playlist.videos[0].videoThumbnails) || '';
|
||||
} else if (playlist.playlistThumbnail) {
|
||||
imgSrc = playlist.playlistThumbnail;
|
||||
} else {
|
||||
imgSrc = '';
|
||||
return;
|
||||
}
|
||||
|
||||
img = await insecureRequestImageHandler(imgSrc);
|
||||
});
|
||||
const thumbnail = new Avatar({ src: thumbnailSrc });
|
||||
</script>
|
||||
|
||||
<a
|
||||
@@ -43,17 +35,15 @@
|
||||
style="width: 100%; overflow: hidden;min-height:100px;"
|
||||
class="wave"
|
||||
>
|
||||
{#if !$interfaceLowBandwidthMode && img}
|
||||
<img
|
||||
loading="lazy"
|
||||
class="responsive"
|
||||
style="max-width: 100%;height: 100%;"
|
||||
src={img.src}
|
||||
alt="Thumbnail for playlist"
|
||||
/>
|
||||
{:else}
|
||||
<h6 style="margin: 3em 0;">No image</h6>
|
||||
{/if}
|
||||
<img
|
||||
class="responsive"
|
||||
{...mergeAttrs(thumbnail.image, {
|
||||
style: 'max-width: 100%;height: 100%;'
|
||||
})}
|
||||
alt="Thumbnail for playlist"
|
||||
/>
|
||||
<div {...thumbnail.fallback} class="secondary-container responsive" style="height: 200px;"></div>
|
||||
|
||||
<div class="absolute right bottom small-margin black white-text small-text thumbnail-corner">
|
||||
{playlist.videoCount}
|
||||
{$_('videos')}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { getBestThumbnail, insecureRequestImageHandler } from '$lib/images';
|
||||
import { getBestThumbnail } from '$lib/images';
|
||||
import { letterCase } from '$lib/letterCasing';
|
||||
import { cleanNumber, videoLength } from '$lib/numbers';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { _ } from '$lib/i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { getDeArrow, getThumbnail } from '$lib/api';
|
||||
import { Avatar } from 'melt/builders';
|
||||
import type {
|
||||
Notification,
|
||||
PlaylistPageVideo,
|
||||
@@ -18,7 +19,6 @@
|
||||
import type { PlayerEvents } from '$lib/player';
|
||||
import {
|
||||
deArrowEnabledStore,
|
||||
interfaceLowBandwidthMode,
|
||||
isAndroidTvStore,
|
||||
playerSavePlaybackPositionStore,
|
||||
playerState,
|
||||
@@ -39,8 +39,6 @@
|
||||
|
||||
let { video = $bindable(), playlistId = '', sideways = $bindable(false) }: Props = $props();
|
||||
|
||||
let placeholderHeight: number = $state(0);
|
||||
|
||||
let watchUrl = createVideoUrl(video.videoId, playlistId);
|
||||
|
||||
let beenWatched: boolean = $state(false);
|
||||
@@ -51,8 +49,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
let thumbnail: HTMLImageElement | undefined = $state();
|
||||
|
||||
let progress: string | undefined = $state();
|
||||
if (get(playerSavePlaybackPositionStore)) {
|
||||
try {
|
||||
@@ -64,6 +60,39 @@
|
||||
progress = undefined;
|
||||
}
|
||||
|
||||
let thumbnailSrc = $state(
|
||||
'thumbnail' in video ? video.thumbnail : (getBestThumbnail(video.videoThumbnails) as string)
|
||||
);
|
||||
|
||||
if (get(deArrowEnabledStore)) {
|
||||
try {
|
||||
getDeArrow(video.videoId, { priority: 'low' }).then(async (deArrow) => {
|
||||
for (const title of deArrow.titles) {
|
||||
if (title.locked || title.votes > 0) {
|
||||
video.title = title.title.replace('>', '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const thumbnail of deArrow.thumbnails) {
|
||||
if (thumbnail.locked || thumbnail.original || thumbnail.votes > 0) {
|
||||
if (thumbnail.timestamp !== null) {
|
||||
thumbnailSrc = await getThumbnail(video.videoId, thumbnail.timestamp, {
|
||||
priority: 'low'
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
// Continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
const thumbnail = new Avatar({ src: () => thumbnailSrc });
|
||||
let thumbnailHTMLElement: HTMLImageElement | undefined = $state();
|
||||
|
||||
let startedSideways = sideways === true;
|
||||
function disableSideways() {
|
||||
if (!startedSideways) return;
|
||||
@@ -78,16 +107,8 @@
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
calcThumbnailPlaceholderHeight();
|
||||
|
||||
// Check if sideways should be enabled or disabled.
|
||||
disableSideways();
|
||||
|
||||
addEventListener('resize', () => {
|
||||
disableSideways();
|
||||
calcThumbnailPlaceholderHeight();
|
||||
});
|
||||
|
||||
checkIfWatched();
|
||||
|
||||
if (
|
||||
@@ -101,39 +122,6 @@
|
||||
checkIfWatched();
|
||||
}
|
||||
});
|
||||
|
||||
if (get(interfaceLowBandwidthMode)) return;
|
||||
|
||||
let imageSrc =
|
||||
'thumbnail' in video ? video.thumbnail : (getBestThumbnail(video.videoThumbnails) as string);
|
||||
|
||||
if (get(deArrowEnabledStore)) {
|
||||
try {
|
||||
const deArrow = await getDeArrow(video.videoId);
|
||||
for (const title of deArrow.titles) {
|
||||
if (title.locked || title.votes > 0) {
|
||||
video.title = title.title.replace('>', '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const thumbnail of deArrow.thumbnails) {
|
||||
if (thumbnail.locked || thumbnail.original || thumbnail.votes > 0) {
|
||||
if (thumbnail.timestamp !== null) {
|
||||
imageSrc = await getThumbnail(video.videoId, thumbnail.timestamp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
const img = await insecureRequestImageHandler(imageSrc);
|
||||
img.onload = () => {
|
||||
thumbnail = img;
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -159,26 +147,6 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function calcThumbnailPlaceholderHeight() {
|
||||
if ($isAndroidTvStore) {
|
||||
placeholderHeight = 100;
|
||||
return;
|
||||
}
|
||||
if (!sideways) {
|
||||
if (innerWidth < 1000) {
|
||||
if (innerWidth < 600) {
|
||||
placeholderHeight = innerWidth / 2;
|
||||
} else {
|
||||
placeholderHeight = innerWidth / 4;
|
||||
}
|
||||
} else {
|
||||
placeholderHeight = innerWidth / 12;
|
||||
}
|
||||
} else {
|
||||
placeholderHeight = 100;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class:sideways-root={sideways} tabindex="0" role="button">
|
||||
@@ -191,32 +159,28 @@
|
||||
data-sveltekit-preload-data="off"
|
||||
onclick={onVideoSelected}
|
||||
>
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<div class="thumbnail-image">
|
||||
{#if !thumbnail}
|
||||
<div
|
||||
class="secondary-container"
|
||||
style="width: 100%;height: {placeholderHeight}px;"
|
||||
></div>
|
||||
{:else}
|
||||
<div class:crop={thumbnail.height > 300}>
|
||||
<img
|
||||
class="responsive"
|
||||
class:watched={beenWatched}
|
||||
loading="lazy"
|
||||
src={thumbnail.src}
|
||||
alt="Thumbnail for video"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if beenWatched}
|
||||
<div class="chip surface-container-highest">
|
||||
<i>check</i>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="thumbnail-image">
|
||||
<div class:crop={thumbnailHTMLElement ? thumbnailHTMLElement.height > 300 : false}>
|
||||
<img
|
||||
class="responsive"
|
||||
class:watched={beenWatched}
|
||||
{...thumbnail.image}
|
||||
bind:this={thumbnailHTMLElement}
|
||||
alt="Thumbnail for video"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
{...thumbnail.fallback}
|
||||
class="secondary-container responsive"
|
||||
style="height: 200px;"
|
||||
></div>
|
||||
|
||||
{#if beenWatched}
|
||||
<div class="chip surface-container-highest">
|
||||
<i>check</i>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if progress}
|
||||
<progress
|
||||
class="absolute right bottom"
|
||||
@@ -227,15 +191,11 @@
|
||||
{/if}
|
||||
{#if !('liveVideo' in video) || !video.liveVideo}
|
||||
{#if video.lengthSeconds !== 0}
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<div
|
||||
class="absolute right bottom small-margin black white-text small-text thumbnail-corner"
|
||||
>
|
||||
{videoLength(video.lengthSeconds)}
|
||||
</div>
|
||||
{:else}
|
||||
<h3>{videoLength(video.lengthSeconds)}</h3>
|
||||
{/if}
|
||||
<div
|
||||
class="absolute right bottom small-margin black white-text small-text thumbnail-corner"
|
||||
>
|
||||
{videoLength(video.lengthSeconds)}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if video.lengthSeconds !== 0}
|
||||
<div class="absolute right bottom small-margin red white-text small-text thumbnail-corner">
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
import { resolve } from '$app/paths';
|
||||
import { getComments } from '$lib/api';
|
||||
import { type Comment, type Comments } from '$lib/api/model';
|
||||
import { getBestThumbnail, insecureRequestImageHandler, proxyGoogleImage } from '$lib/images';
|
||||
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
|
||||
import { numberWithCommas } from '$lib/numbers';
|
||||
import { interfaceLowBandwidthMode } from '$lib/store';
|
||||
import { onMount } from 'svelte';
|
||||
import CommentSelf from './Comment.svelte';
|
||||
import { truncate } from '$lib/misc';
|
||||
import { _ } from '$lib/i18n';
|
||||
import { extractActualLink } from '$lib/description';
|
||||
import { Avatar } from 'melt/builders';
|
||||
import { mergeAttrs } from 'melt';
|
||||
|
||||
interface Props {
|
||||
comment: Comment;
|
||||
@@ -71,59 +71,49 @@
|
||||
return doc.documentElement.outerHTML;
|
||||
}
|
||||
|
||||
let userPfp = $state('');
|
||||
onMount(async () => {
|
||||
if ($interfaceLowBandwidthMode) return;
|
||||
const img = await insecureRequestImageHandler(
|
||||
proxyGoogleImage(getBestThumbnail(comment.authorThumbnails))
|
||||
);
|
||||
|
||||
img.onload = () => {
|
||||
userPfp = img.src;
|
||||
};
|
||||
});
|
||||
const avatar = new Avatar({ src: proxyGoogleImage(getBestThumbnail(comment.authorThumbnails)) });
|
||||
</script>
|
||||
|
||||
<article class="comment" class:border={!isSubComp}>
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<div class="comment-header">
|
||||
{#if userPfp}
|
||||
<img loading="lazy" class="circle small" src={userPfp} alt="comment profile" />
|
||||
{:else}
|
||||
<progress class="circle"></progress>
|
||||
{/if}
|
||||
<div class="comment-info">
|
||||
<a href={resolve(`/channel/[authorId]`, { authorId: comment.authorId })} class="author">
|
||||
<span class="bold" class:channel-owner={comment.authorIsChannelOwner}>
|
||||
{truncate(comment.author, 12)}
|
||||
</span>
|
||||
<span class="secondary-text">{comment.publishedText}</span>
|
||||
</a>
|
||||
<p class="no-margin">
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html parseComment(comment.contentHtml)}
|
||||
<!-- Comment comes directly from YT so is already sanitized -->
|
||||
</p>
|
||||
<div class="comment-actions">
|
||||
<p class="no-margin no-padding"><i>thumb_up</i> {numberWithCommas(comment.likeCount)}</p>
|
||||
{#if comment.replies && !replies}
|
||||
<button
|
||||
onclick={async () => loadReplies(comment.replies.continuation)}
|
||||
class="transparent replies"
|
||||
>
|
||||
<i>expand_more</i>
|
||||
<span>{comment.replies.replyCount} {replyText}</span>
|
||||
</button>
|
||||
{:else if replies}
|
||||
<button onclick={() => (replies = undefined)} class="transparent replies">
|
||||
<i>expand_less</i>
|
||||
<span>Hide {replyText}</span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="comment-header">
|
||||
<img {...avatar.image} loading="lazy" class="circle small" alt="comment profile" />
|
||||
<button
|
||||
class="secondary-container"
|
||||
{...mergeAttrs(avatar.fallback, {
|
||||
style: 'text-transform: uppercase;border-radius: 2.5rem !important;'
|
||||
})}>{comment.author[1]}</button
|
||||
>
|
||||
<div class="comment-info">
|
||||
<a href={resolve(`/channel/[authorId]`, { authorId: comment.authorId })} class="author">
|
||||
<span class="bold" class:channel-owner={comment.authorIsChannelOwner}>
|
||||
{truncate(comment.author, 12)}
|
||||
</span>
|
||||
<span class="secondary-text">{comment.publishedText}</span>
|
||||
</a>
|
||||
<p class="no-margin">
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html parseComment(comment.contentHtml)}
|
||||
<!-- Comment comes directly from YT so is already sanitized -->
|
||||
</p>
|
||||
<div class="comment-actions">
|
||||
<p class="no-margin no-padding"><i>thumb_up</i> {numberWithCommas(comment.likeCount)}</p>
|
||||
{#if comment.replies && !replies}
|
||||
<button
|
||||
onclick={async () => loadReplies(comment.replies.continuation)}
|
||||
class="transparent replies"
|
||||
>
|
||||
<i>expand_more</i>
|
||||
<span>{comment.replies.replyCount} {replyText}</span>
|
||||
</button>
|
||||
{:else if replies}
|
||||
<button onclick={() => (replies = undefined)} class="transparent replies">
|
||||
<i>expand_less</i>
|
||||
<span>Hide {replyText}</span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if replies}
|
||||
{#each replies.comments as reply (reply)}
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
interfaceDefaultPage,
|
||||
interfaceDisplayThumbnailAvatars,
|
||||
interfaceForceCase,
|
||||
interfaceLowBandwidthMode,
|
||||
interfaceRegionStore,
|
||||
interfaceSearchSuggestionsStore,
|
||||
playerAlwaysLoopStore,
|
||||
@@ -203,11 +202,6 @@ export const persistedStores: PersistedStore<any>[] = [
|
||||
store: showWarningStore,
|
||||
schema: zBoolean
|
||||
},
|
||||
{
|
||||
name: 'lowBandwidthMode',
|
||||
store: interfaceLowBandwidthMode,
|
||||
schema: zBoolean
|
||||
},
|
||||
{
|
||||
name: 'displayThumbnailAvatars',
|
||||
store: interfaceDisplayThumbnailAvatars,
|
||||
|
||||
@@ -27,23 +27,6 @@ export class ImageCache {
|
||||
}
|
||||
}
|
||||
|
||||
export async function insecureRequestImageHandler(source: string): Promise<HTMLImageElement> {
|
||||
const img = new Image();
|
||||
if (get(interfaceAllowInsecureRequests)) {
|
||||
const imgResp = await fetch(source);
|
||||
if (!imgResp.ok) {
|
||||
img.src = '';
|
||||
return img;
|
||||
}
|
||||
|
||||
img.src = URL.createObjectURL(await imgResp.blob());
|
||||
} else {
|
||||
img.src = source;
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
export function getBestThumbnail(
|
||||
images: Image[] | null,
|
||||
maxWidthDimension: number = 480,
|
||||
|
||||
@@ -234,11 +234,6 @@ export const interfaceAutoExpandChapters: Writable<boolean> = persist(
|
||||
'autoExpandChapters'
|
||||
);
|
||||
export const interfaceAmoledTheme = persist(writable(false), createStorage(), 'amoledTheme');
|
||||
export const interfaceLowBandwidthMode = persist(
|
||||
writable(false),
|
||||
createStorage(),
|
||||
'lowBandwidthMode'
|
||||
);
|
||||
export const interfaceDisplayThumbnailAvatars = persist(
|
||||
writable(false),
|
||||
createStorage(),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import PageLoading from '$lib/components/PageLoading.svelte';
|
||||
import { proxyGoogleImage } from '$lib/images';
|
||||
import { cleanNumber } from '$lib/numbers';
|
||||
import { channelCacheStore, interfaceLowBandwidthMode, isAndroidTvStore } from '$lib/store';
|
||||
import { channelCacheStore, isAndroidTvStore } from '$lib/store';
|
||||
import { _ } from '$lib/i18n';
|
||||
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
|
||||
import ItemsList from '$lib/components/layout/ItemsList.svelte';
|
||||
@@ -14,6 +14,7 @@
|
||||
import { isYTBackend } from '$lib/misc';
|
||||
import Share from '$lib/components/Share.svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import { Avatar } from 'melt/builders';
|
||||
|
||||
let tab: ChannelContentTypes = $state('videos');
|
||||
|
||||
@@ -83,17 +84,14 @@
|
||||
displayContent = undefined;
|
||||
displayContent = await getChannelContent(page.params.slug, { type: tab });
|
||||
}
|
||||
|
||||
const banner = new Avatar({
|
||||
src: proxyGoogleImage($channelCacheStore[page.params.slug].channel.authorBanners[0].url)
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="padding">
|
||||
{#if $channelCacheStore[page.params.slug].channel.authorBanners.length > 0 && !$interfaceLowBandwidthMode}
|
||||
<img
|
||||
loading="lazy"
|
||||
src={proxyGoogleImage($channelCacheStore[page.params.slug].channel.authorBanners[0].url)}
|
||||
width="100%"
|
||||
alt="Channel banner"
|
||||
/>
|
||||
{/if}
|
||||
<img {...banner.image} width="100%" alt="Channel banner" />
|
||||
<div class="description">
|
||||
<div>
|
||||
<Author
|
||||
|
||||
Reference in New Issue
Block a user