Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added rudimentry embeded video support #56

Open
wants to merge 1 commit into
base: video-support
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/lib/Video.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<style lang="scss">
video {
max-width: 100%;
max-height: 95vh;
}
.video-mode-full:not(.video-full) video {
max-width: 20rem;
}
.error {
background: var(--sx-gray-transparent);
height: 100%;
width: 100%;
}
.show-nsfw {
background: var(--sx-red-transparent-faint);
color: var(--sx-red-400);
margin: 0;
}
.video-mode-thumbnail.blur:not(:hover) video {
filter: blur(10px);
}
video {
width: 100%;
}
</style>

{#if error}
<div
class="error align-items-center justify-content-center f-row"
title="Video load failed"
style={loadingHeight ? `height: ${loadingHeight};` : ''}
>
<Icon icon="bug" />
</div>
{:else if nsfw && $nsfwImageHandling === 'HIDE' && !showAnyway}
<button class="img show-nsfw" on:click|stopPropagation={() => (showAnyway = true)}>Show NSFW</button>
{:else if valid}
<!-- svelte-ignore a11y-media-has-caption -->
<video
class="video-mode-{mode} {full ? 'video-full' : ''}"
class:blur={nsfw && $nsfwImageHandling === 'BLUR'}
on:error={videoLoadError}
controls
>
<source src={src} />
<a href="{link}">{linkTitle}</a>
</video>
{/if}

<script lang="ts">
import { Icon } from 'sheodox-ui';
import { getSettingsContext } from './settings-context';
export let src: string;
export let link: string;
export let linkTitle: string;
export let mode: 'thumbnail' | 'full' = 'full';

export let full = mode === 'full';
export let nsfw = false;
// if the video is loading for the first time, this is a placeholder size we could use
export let loadingHeight: string | undefined = undefined;
const { nsfwImageHandling } = getSettingsContext();

$: valid = src.startsWith('https://') || src.startsWith('http://');

let error = false,
showAnyway = false;

function videoLoadError() {
error = true;
}
</script>
18 changes: 15 additions & 3 deletions src/lib/feeds/posts/PostContent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<div class="embed-content f-column gap-2">
<PostEmbed {postView} />
{#if hasBody}
{#if postAssertions.has.body}
<PostBody {postView} />
{/if}
{#if probablyImage && postView.post.url}
Expand All @@ -17,17 +17,29 @@
in the feed height, so images can't be lazy -->
<Image src={postView.post.url} lazy={false} />
{/if}
{#if postAssertions.has.video}
<!-- not passing nsfw, it's handled by not showing the post contents
by default when necessary, or the user has to click twice to see.
also don't lazy load, as if it's expanded in the feed it has repercussions
in the feed height, so images can't be lazy -->
<Video
src={postAssertions.videoSrc || ''}
linkTitle={postView.post.embed_title || postView.post.name}
link={postView.post.url || ''}
/>
{/if}
</div>

<script lang="ts">
import type { PostView } from 'lemmy-js-client';
import PostBody from './previews/PostBody.svelte';
import Image from '$lib/Image.svelte';
import Video from '$lib/Video.svelte';
import PostEmbed from './PostEmbed.svelte';
import { hasImageExtension } from './post-utils';
import { hasImageExtension, makePostAssertions } from './post-utils';

export let postView: PostView;

$: probablyImage = hasImageExtension(postView.post.url || '');
$: hasBody = !!postView.post.body;
$: postAssertions = makePostAssertions(postView);
</script>
23 changes: 22 additions & 1 deletion src/lib/feeds/posts/post-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,26 @@ export function hasImageExtension(url?: string) {
}
}

export function hasVideoExtension(url?: string) {
if (!isValidUrl(url)) {
return false;
}

try {
const u = new URL(url);
return /\.(mp4|mpv|mov)$/.test(u.pathname);
} catch (e) {
return false;
}
}

export interface PostAssertions {
imageSrc?: string;
videoSrc?: string;
// existence of certain types of embedded content
has: {
image: boolean;
video: boolean;
body: boolean;
embed: boolean;
any: boolean;
Expand All @@ -53,19 +68,25 @@ export interface PostAssertions {

export function makePostAssertions(pv: PostView, myUserId?: number): PostAssertions {
let imageSrc = pv.post.thumbnail_url;
let videoSrc = pv.post.embed_video_url;

if (!imageSrc && hasImageExtension(pv.post.url)) {
imageSrc = pv.post.url;
}
if (!videoSrc && hasVideoExtension(pv.post.url)) {
videoSrc = pv.post.url;
}

const hasParts = {
image: !!imageSrc,
video: !!videoSrc,
body: !!pv.post.body?.trim(),
embed: !!pv.post.embed_title
embed: !!(pv.post.embed_title || videoSrc)
};

return {
imageSrc,
videoSrc,
// assertions about embedded contents
has: {
...hasParts,
Expand Down
7 changes: 7 additions & 0 deletions src/lib/feeds/posts/previews/CardPostPreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@
</div>
{/if}

{#if postAssertions.has.video}
<div class="card-image">
<CardPostVideo {postView} />
</div>
{/if}

{#if postView.post.embed_description || (postView.post.embed_title && mode === 'list')}
<div class="px-2">
<PostEmbed {postView} reflectRead preview />
Expand Down Expand Up @@ -80,6 +86,7 @@
import { Stack } from 'sheodox-ui';
import PostTitle from '../PostTitle.svelte';
import CardPostImage from './CardPostImage.svelte';
import CardPostVideo from './CardPostVideo.svelte';
import PostTime from '../PostTime.svelte';
import { createEventDispatcher } from 'svelte';
import type { PostView } from 'lemmy-js-client';
Expand Down
48 changes: 48 additions & 0 deletions src/lib/feeds/posts/previews/CardPostVideo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<style lang="scss">
.card-video-placeholder {
height: 20rem;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.nsfw {
background: var(--sx-red-transparent-faint);
button {
height: 100%;
width: 100%;
margin: 0;
}
}
</style>

<div class="card-video" class:nsfw={postView.post.nsfw}>
{#if postView.post.nsfw && $nsfwImageHandling === 'HIDE' && !showAnyway}
<div class="card-video-placeholder">
<button class="tertiary" on:click|stopPropagation={() => (showAnyway = true)}> Show NSFW </button>
</div>
{:else if postAssertions.has.video}
<!-- not passing nsfw, this component handles it otherwise we'd have nested buttons -->
<Video
src={postAssertions.videoSrc || ''}
full
loadingHeight="20rem"
linkTitle={postView.post.embed_title || postView.post.name}
link={postView.post.url || ''}
/>
{/if}
</div>

<script lang="ts">
import type { PostView } from 'lemmy-js-client';
import Video from '$lib/Video.svelte';
import { getSettingsContext } from '$lib/settings-context';
import { makePostAssertions } from '../post-utils';

export let postView: PostView;

const { nsfwImageHandling } = getSettingsContext();

let showAnyway = false;
$: postAssertions = makePostAssertions(postView);
</script>