Refactor images to use an Image type to supply alt text

This commit is contained in:
Cynthia Foxwell 2025-04-04 17:11:40 -06:00
parent f2cac5190a
commit 8042c65ce3
No known key found for this signature in database
11 changed files with 54 additions and 26 deletions

View File

@ -250,7 +250,9 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
let name = jsCard{"name"}.getStr
if "poll" in name:
if "image" in name:
result.photos.add jsCard{"binding_values", "image_large"}.getImageVal
result.photos.add Image(
url: jsCard{"binding_values", "image_large"}.getImageVal
)
result.poll = some parsePoll(jsCard)
elif name == "amplify":
@ -264,7 +266,10 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
for m in jsMedia:
case m{"type"}.getStr
of "photo":
result.photos.add m{"media_url_https"}.getImageStr
result.photos.add Image(
url: m{"media_url_https"}.getImageStr,
description: m{"ext_alt_text"}.getStr,
)
of "video":
result.video = some(parseVideo(m))
with user, m{"additional_media_info", "source_user"}:
@ -418,7 +423,7 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail =
for tweet in js:
let
t = parseTweet(tweet, js{"tweet_card"})
url = if t.photos.len > 0: t.photos[0]
url = if t.photos.len > 0: t.photos[0].url
elif t.video.isSome: get(t.video).thumb
elif t.gif.isSome: get(t.gif).thumb
elif t.card.isSome: get(t.card).image

View File

@ -45,8 +45,8 @@ proc createActivityPubRouter*(cfg: Config) =
var media: seq[JsonNode] = @[]
if tweet.photos.len > 0:
for url in tweet.photos:
let image = getUrlPrefix(cfg) & getPicUrl(url)
for imageObj in tweet.photos:
let image = getUrlPrefix(cfg) & getPicUrl(imageObj.url)
var mediaObj = newJObject()
mediaObj["id"] = %"150745989836308480" # idk if discord even parses this snowflake, but its my user id why not
@ -56,7 +56,7 @@ proc createActivityPubRouter*(cfg: Config) =
mediaObj["remote_url"] = %image
mediaObj["preview_remote_url"] = %image
mediaObj["text_url"] = newJNull()
mediaObj["description"] = newJNull() # FIXME this requires refactoring images
mediaObj["description"] = %imageObj.description
# FIXME but this probably isnt used by discord
mediaObj["meta"] = newJObject()

View File

@ -123,7 +123,7 @@ proc createStatusRouter*(cfg: Config) =
if tweet.video.isSome():
let videoObj = get(tweet.video)
images = @[videoObj.thumb]
images = @[Image(url:videoObj.thumb)]
let vars = videoObj.variants.filterIt(it.contentType == mp4)
# idk why this wont sort when it sorts everywhere else
@ -131,7 +131,7 @@ proc createStatusRouter*(cfg: Config) =
video = vars[^1].url
elif tweet.gif.isSome():
let gif = get(tweet.gif)
images = @[gif.thumb]
images = @[Image(url:gif.thumb)]
video = getUrlPrefix(cfg) & getPicUrl(gif.url)
#elif tweet.card.isSome():
# let card = tweet.card.get()

View File

@ -96,7 +96,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
let pHtml = renderProfile(profile, cfg, prefs, getPath())
result = renderMain(pHtml, request, cfg, prefs, pageTitle(u), pageDesc(u),
rss=rss, images = @[u.getUserPic("_400x400")],
rss=rss, images = @[Image(url: u.getUserPic("_400x400"))],
banner=u.banner)
template respTimeline*(timeline: typed) =

View File

@ -48,6 +48,19 @@
margin: 0;
max-height: 530px;
}
.alt {
position: relative;
bottom: 15px;
left: 4px;
padding: 4px;
background: #101010;
color: white;
border-radius: 4px;
pointer-events: none;
font-size: 10px;
font-weight: 600;
}
}
.gallery-gif video {

View File

@ -123,6 +123,10 @@ type
playbackType*: VideoType
variants*: seq[VideoVariant]
Image* = object
url*: string
description*: string
QueryKind* = enum
posts, replies, media, users, tweets, userList, favorites
@ -228,7 +232,7 @@ type
poll*: Option[Poll]
gif*: Option[Gif]
video*: Option[Video]
photos*: seq[string]
photos*: seq[Image]
birdwatch*: Option[BirdwatchNote]
Tweets* = seq[Tweet]

View File

@ -15,7 +15,7 @@ proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string =
let vidUrl = vars.sortedByIt(it.bitrate)[^1].url
let prefs = Prefs(hlsPlayback: true, mp4Playback: true)
let node = buildHtml(html(lang="en")):
renderHead(prefs, cfg, req, video=vidUrl, images=(@[thumb]))
renderHead(prefs, cfg, req, video=vidUrl, images=(@[Image(url:thumb)]))
body:
tdiv(class="embed-video"):

View File

@ -37,7 +37,7 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode =
icon "cog", title="Preferences", href=("/settings?referer=" & encodeUrl(path))
proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
video=""; images: seq[string] = @[]; banner=""; ogTitle="";
video=""; images: seq[Image] = @[]; banner=""; ogTitle="";
rss=""; canonical=""; avatar=""; context=""; contextUrl="";
id=""; time: Option[DateTime] = none(DateTime)): VNode =
var theme = prefs.theme.toTheme
@ -120,13 +120,16 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
link(rel="preload", type="image/png", href=bannerUrl, `as`="image")
if images.len > 0:
for url in images:
let preloadUrl = if "400x400" in url: getPicUrl(url)
for imageObj in images:
let
url = imageObj.url
preloadUrl = if "400x400" in url: getPicUrl(url)
else: getSmallPic(url)
link(rel="preload", type="image/png", href=preloadUrl, `as`="image")
let image = getUrlPrefix(cfg) & getPicUrl(url)
meta(property="og:image", content=image)
meta(property="og:image:alt", content=imageObj.description)
if video.len == 0:
meta(property="twitter:image:src", content=image)
meta(property="twitter:card", content="summary_large_image")
@ -172,7 +175,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
titleText=""; desc=""; ogTitle=""; rss=""; video="";
images: seq[string] = @[]; banner=""; avatar=""; context="";
images: seq[Image] = @[]; banner=""; avatar=""; context="";
contextUrl=""; id=""; time: Option[DateTime] = none(DateTime)
): string =

View File

@ -38,10 +38,10 @@ proc getActivityStream*(tweet: Tweet, cfg: Config, prefs: Prefs): JsonNode =
var media: seq[JsonNode] = @[]
if tweet.photos.len > 0:
for url in tweet.photos:
for imageObj in tweet.photos:
let
image = getUrlPrefix(cfg) & getPicUrl(url)
splitUrl = url.split('.')
image = getUrlPrefix(cfg) & getPicUrl(imageObj.url)
splitUrl = imageObj.url.split('.')
var filetype = splitUrl[^1]
if filetype == "jpg":
filetype = "jpeg"
@ -50,7 +50,7 @@ proc getActivityStream*(tweet: Tweet, cfg: Config, prefs: Prefs): JsonNode =
mediaObj["type"] = %"Document"
mediaObj["mediaType"] = %("image/" & filetype)
mediaObj["url"] = %image
mediaObj["name"] = newJNull() # FIXME a11y
mediaObj["name"] = %imageObj.description
media.add(mediaObj)

View File

@ -89,9 +89,9 @@ proc genDate*(pref, state: string): VNode =
input(name=pref, `type`="date", value=state)
icon "calendar"
proc genImg*(url: string; class=""): VNode =
proc genImg*(url: string; alt=""; class=""): VNode =
buildHtml():
img(src=getPicUrl(url), class=class, alt="")
img(src=getPicUrl(url), class=class, alt=alt)
proc getTabClass*(query: Query; tab: QueryKind): string =
if query.kind == tab: "tab-item active"

View File

@ -49,12 +49,15 @@ proc renderAlbum(tweet: Tweet): VNode =
let margin = if i > 0: ".25em" else: ""
tdiv(class="gallery-row", style={marginTop: margin}):
for photo in photos:
tdiv(class="attachment image"):
tdiv(class="attachment image", title=photo.description):
let
named = "name=" in photo
small = if named: photo else: photo & smallWebp
a(href=getOrigPicUrl(photo), class="still-image", target="_blank"):
genImg(small)
url = photo.url
named = "name=" in url
small = if named: url else: url & smallWebp
a(href=getOrigPicUrl(url), class="still-image", target="_blank", data-caption=photo.description):
genImg(small, alt=photo.description)
if photo.description.len > 0:
span(class="alt"): text "ALT"
proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool =
case playbackType