mirror of
https://github.com/defnull/fediwall.git
synced 2025-02-16 17:10:50 +01:00
Fallback to card preview image on posts without media.
Peertube posts have no media attachments but a preview card that shows an embedded video player in an iframe. Unfortunately this does not really support the small form-factor of a fediwall card and looks kinda ugly. Autoplay also won't work. So we just display the card preview image (if present) as a link you can click on to view the full video.
This commit is contained in:
parent
4d77c6cf8a
commit
088f0fe094
@ -43,12 +43,15 @@ const onMediaLoad = inject('fixLayout', () => undefined)
|
|||||||
<slot name="topleft"></slot>
|
<slot name="topleft"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div v-if="config.showMedia" class="wall-media mb-3" ref="mediaElement">
|
<div v-if="config.showMedia && media" class="wall-media mb-3" ref="mediaElement">
|
||||||
<img v-if="media?.type === 'image'" :src="media.url" :alt="media.alt" :title="media.alt" @load="onMediaLoad">
|
<img v-if="media.type === 'image'" :src="media.url" :alt="media.alt" :title="media.alt" @load="onMediaLoad">
|
||||||
<video v-else-if="media?.type === 'video'" muted loop :autoplay="playVideo"
|
<video v-else-if="media.type === 'video'" muted loop :autoplay="playVideo"
|
||||||
:poster="media.preview" :alt="media.alt" :title="media.alt" @loadedmetadata="onMediaLoad">
|
:poster="media.preview" :alt="media.alt" :title="media.alt" @loadedmetadata="onMediaLoad">
|
||||||
<source v-if="playVideo" :src="media.url">
|
<source v-if="playVideo" :src="media.url">
|
||||||
</video>
|
</video>
|
||||||
|
<a v-else-if="media.type==='card'" :href="media.url" target="_blank">
|
||||||
|
<img :src="media.preview" :alt="media.alt" :title="media.alt" @load="onMediaLoad">
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="config.showText" class="card-text" v-dompurify-html="post.content"></p>
|
<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" :title="post.date.toLocaleString()"
|
<p class="card-text text-end text-break"><a :href="post.url" target="_blank" :title="post.date.toLocaleString()"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { Config, MastodonAccount, MastodonStatus, Post, PostMedia } from "@/types";
|
import type { Config, MastodonAccount, MastodonStatus, Post, PostMedia } from "@/types";
|
||||||
import { regexEscape } from "@/utils";
|
import { notBlank, regexEscape } from "@/utils";
|
||||||
import { replaceInText } from '@/utils'
|
import { replaceInText } from '@/utils'
|
||||||
import type { faTags } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,13 +235,42 @@ const filterStatus = (cfg: Config, status: MastodonStatus) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip posts that would show up empty
|
// Skip posts that would show up empty
|
||||||
if (!cfg.showText && !status.media_attachments?.length) return false;
|
if (!cfg.showText && findMedia(status).length == 0) return false;
|
||||||
if (!cfg.showMedia && !status.content.trim()) return false;
|
if (!cfg.showMedia && !status.content.trim()) return false;
|
||||||
|
|
||||||
// Accept anything else
|
// Accept anything else
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findMedia(status: MastodonStatus) {
|
||||||
|
const media: PostMedia[] = []
|
||||||
|
|
||||||
|
status.media_attachments?.map((m): PostMedia | undefined => {
|
||||||
|
const url = m.url;
|
||||||
|
const alt = m.description ?? undefined
|
||||||
|
const preview = m.preview_url ?? undefined
|
||||||
|
switch (m.type) {
|
||||||
|
case "image":
|
||||||
|
return { type: "image", url, href:url, preview, alt }
|
||||||
|
case "video":
|
||||||
|
case "gifv":
|
||||||
|
return { type: "video", url, href:url, preview, alt }
|
||||||
|
case "audio":
|
||||||
|
case "unknown":
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}).filter(m=>!!m).forEach(m=>media.push(m))
|
||||||
|
|
||||||
|
// Fall back to preview card images if no media is attached (e.g. for peertube posts)
|
||||||
|
if(media.length == 0 && status.card) {
|
||||||
|
const card = status.card
|
||||||
|
if(notBlank(card.image) && notBlank(card.url))
|
||||||
|
media.push({type:"card", url: card.url, preview:card.image, alt: status.card.description})
|
||||||
|
}
|
||||||
|
|
||||||
|
return media
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a mastodon status object to a Post.
|
* Convert a mastodon status object to a Post.
|
||||||
*/
|
*/
|
||||||
@ -282,18 +310,7 @@ const statusToWallPost = (cfg: Config, status: MastodonStatus): Post => {
|
|||||||
const profile = status.account.acct
|
const profile = status.account.acct
|
||||||
const content = replaceEmojis(status.content, status.emojis)
|
const content = replaceEmojis(status.content, status.emojis)
|
||||||
|
|
||||||
const media = status.media_attachments?.map((m): PostMedia | undefined => {
|
const media = findMedia(status)
|
||||||
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 {
|
return {
|
||||||
id: status.uri,
|
id: status.uri,
|
||||||
|
28
src/types.ts
28
src/types.ts
@ -45,10 +45,11 @@ export type Post = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type PostMedia = {
|
export type PostMedia = {
|
||||||
type: "image" | "video"
|
type: "image" | "video" | "card"
|
||||||
url: string,
|
url: string
|
||||||
preview: string
|
preview?: string
|
||||||
alt?: string
|
alt?: string
|
||||||
|
size?: [number,number]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -67,6 +68,7 @@ export type MastodonStatus = {
|
|||||||
in_reply_to_id?: string | null;
|
in_reply_to_id?: string | null;
|
||||||
language?: string | null;
|
language?: string | null;
|
||||||
media_attachments: Array<MastodonMediaAttachment>;
|
media_attachments: Array<MastodonMediaAttachment>;
|
||||||
|
card?: MastodonCard,
|
||||||
reblog?: MastodonStatus | null;
|
reblog?: MastodonStatus | null;
|
||||||
sensitive: boolean;
|
sensitive: boolean;
|
||||||
spoiler_text?: string | null;
|
spoiler_text?: string | null;
|
||||||
@ -114,3 +116,23 @@ export type MastodonMediaAttachment = {
|
|||||||
preview_url: string;
|
preview_url: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MastodonCard = {
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
language: string;
|
||||||
|
type: string;
|
||||||
|
author_name: string;
|
||||||
|
author_url: string;
|
||||||
|
provider_name: string;
|
||||||
|
provider_url: string;
|
||||||
|
html: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
image: string;
|
||||||
|
image_description: string;
|
||||||
|
embed_url: string;
|
||||||
|
blurhash: string | null;
|
||||||
|
published_at?: any
|
||||||
|
}
|
@ -9,6 +9,10 @@ export function isString(test: any) {
|
|||||||
return typeof test === 'string' || test instanceof String
|
return typeof test === 'string' || test instanceof String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function notBlank(test?: string) {
|
||||||
|
return test && test.trim().length > 0
|
||||||
|
}
|
||||||
|
|
||||||
export function arrayUnique<T>(array: T[]) {
|
export function arrayUnique<T>(array: T[]) {
|
||||||
return array.filter((v, i, a) => a.indexOf(v) === i)
|
return array.filter((v, i, a) => a.indexOf(v) === i)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user