Add support for video media attachments.

Videos are only downloaded if autoplay is enabled and the video is visible on screen.
Disabling autoplay shows preview images instead.
This commit is contained in:
Marcel Hellkamp 2023-07-28 18:43:52 +02:00
parent 2c6d99e8ec
commit d7fde80d06
4 changed files with 58 additions and 16 deletions

View File

@ -197,11 +197,13 @@ const privacyLink = computed(() => {
</header>
<main>
<div v-if="filteredPosts.length === 0 && updateInProgress">Loading first posts ...</div>
<div v-if="config === undefined">Initialiting ...</div>
<div v-else-if="filteredPosts.length === 0 && updateInProgress">Loading first posts ...</div>
<div v-else-if="filteredPosts.length === 0">Nothing there yet ...</div>
<div v-else v-masonry transition-duration="1s" item-selector=".wall-item" percent-position="true" id="wall">
<Card v-masonry-tile class="wall-item secret-hover" v-for="post in filteredPosts" :key="post.id"
:post="post">
:post="post" :config="config">
<template v-slot:topleft>
<div class="dropdown secret">
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="dropdown"
@ -216,6 +218,7 @@ const privacyLink = computed(() => {
</ul>
</div>
</template>
</Card>
</div>
</main>

View File

@ -1,11 +1,12 @@
<script setup lang="ts">
import { useIntervalFn } from '@vueuse/core'
import { ref } from 'vue';
import { useElementVisibility, useIntervalFn } from '@vueuse/core'
import { computed, ref } from 'vue';
import moment from 'moment'
import { type Post } from '@/types';
import { type Config, type Post } from '@/types';
const props = defineProps<{
post: Post
config: Config,
post: Post,
}>()
const timeAgo = ref(moment(props.post.date).fromNow())
@ -14,6 +15,16 @@ useIntervalFn(() => {
timeAgo.value = moment(props.post.date).fromNow()
}, 1000)
const media = computed(() => {
return props.post.media[0]
})
const videoElement = ref(null)
const videoIsVisible = useElementVisibility(videoElement)
const playVideo = computed(() => {
return media.value?.type === "video" && props.config.playVideos && videoIsVisible.value
})
</script>
<template>
@ -29,8 +40,14 @@ useIntervalFn(() => {
<slot name="topleft"></slot>
</div>
<div class="card-body">
<img v-if="post.media" :src="post.media" class="wall-media mb-3">
<p class="card-text" v-dompurify-html="post.content"></p>
<div v-if="config.showMedia" class="wall-media mb-3">
<img v-if="media?.type === 'image'" :src="media.url" :alt="media.alt">
<video v-else-if="media?.type === 'video'" ref="videoElement"
muted loop :autoplay="playVideo" :poster="media.preview" :alt="media.alt" >
<source v-if="playVideo" :src="media.url">
</video>
</div>
<p v-if="config.showText" class="card-text" v-dompurify-html="post.content"></p>
<p class="card-text text-end text-break"><a :href="post.url" target="_blank"
alt="${post.date}" class="text-decoration-none text-muted"><small>{{ timeAgo }}</small></a></p>
</div>
@ -56,11 +73,17 @@ useIntervalFn(() => {
height: 2em;
}
.wall-item .wall-media {
.wall-media {
}
.wall-media img, .wall-media video {
width: 100%;
max-height: 1wh;
object-fit: cover;
border-radius: 5px;
}
.wall-item .invisible {
font-size: 0 !important;
line-height: 0 !important;

View File

@ -1,4 +1,4 @@
import type { Config, MastodonAccount, MastodonStatus, Post } from "@/types";
import type { Config, MastodonAccount, MastodonStatus, Post, PostMedia } from "@/types";
import { regexEscape } from "@/utils";
@ -211,10 +211,18 @@ const statusToWallPost = (status: MastodonStatus): Post => {
if (status.reblog)
status = status.reblog
let media;
const image = status.media_attachments?.find((m: any) => m.type == "image")
if (image)
media = image.url
const media = status.media_attachments?.map((m) : PostMedia|undefined => {
switch (m.type) {
case "image":
return { type: "image", url: m.url, preview: m.preview_url, alt: m.description ?? undefined }
case "video":
case "gifv":
return { type: "video", url: m.url, preview: m.preview_url, alt: m.description ?? undefined }
case "audio":
case "unknown":
return
}
}).filter((m): m is PostMedia => m !== undefined)
return {
id: status.uri,

View File

@ -38,10 +38,18 @@ export type Post = {
url?: string;
};
media?: string;
media: Array<PostMedia>;
pinned?: boolean;
};
export type PostMedia = {
type: "image" | "video"
url: string,
preview: string
alt?: string
}
/**
* Mastodon types. We only model what is important for us
@ -98,7 +106,7 @@ export type MastodonTag = {
export type MastodonMediaAttachment = {
id: string;
type: 'audio' | 'video' | 'gifv' | 'unknown';
type: 'image' | 'audio' | 'video' | 'gifv' | 'unknown';
blurhash?: string | null;
description?: string | null;
preview_url: string;