Support image alt text

Fixes #559
This commit is contained in:
Zed
2026-02-10 22:42:06 +01:00
parent 40b1ba4e4e
commit 1c06a67afd
9 changed files with 60 additions and 19 deletions

View File

@@ -139,7 +139,10 @@ proc parseLegacyMediaEntities(js: JsonNode; result: var Tweet) =
for m in jsMedia:
case m.getTypeName:
of "photo":
result.photos.add m{"media_url_https"}.getImageStr
result.photos.add Photo(
url: m{"media_url_https"}.getImageStr,
altText: m{"ext_alt_text"}.getStr
)
of "video":
result.video = some(parseVideo(m))
with user, m{"additional_media_info", "source_user"}:
@@ -165,7 +168,10 @@ proc parseMediaEntities(js: JsonNode; result: var Tweet) =
with mediaInfo, mediaEntity{"media_results", "result", "media_info"}:
case mediaInfo.getTypeName
of "ApiImage":
result.photos.add mediaInfo{"original_img_url"}.getImageStr
result.photos.add Photo(
url: mediaInfo{"original_img_url"}.getImageStr,
altText: mediaInfo{"alt_text"}.getStr
)
of "ApiVideo":
let status = mediaEntity{"media_results", "result", "media_availability_v2", "status"}
result.video = some Video(
@@ -332,11 +338,13 @@ 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 Photo(
url: jsCard{"binding_values", "image_large"}.getImageVal
)
result.poll = some parsePoll(jsCard)
elif name == "amplify":
result.video = some(parsePromoVideo(jsCard{"binding_values"}))
result.video = some parsePromoVideo(jsCard{"binding_values"})
else:
result.card = some parseCard(jsCard, js{"entities", "urls"})

View File

@@ -333,7 +333,7 @@ proc expandNoteTweetEntities*(tweet: Tweet; js: JsonNode) =
proc extractGalleryPhoto*(t: Tweet): GalleryPhoto =
let url =
if t.photos.len > 0: t.photos[0]
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

@@ -44,7 +44,7 @@ proc createStatusRouter*(cfg: Config) =
desc = conv.tweet.text
var
images = conv.tweet.photos
images = conv.tweet.photos.mapIt(it.url)
video = ""
if conv.tweet.video.isSome():

View File

@@ -4,7 +4,6 @@
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
overflow: hidden;
flex-grow: 1;
max-height: 379.5px;
@@ -13,7 +12,7 @@
.still-image {
width: 100%;
display: flex;
align-self: center;
}
}
@@ -58,7 +57,6 @@
.still-image {
max-height: 379.5px;
max-width: 533px;
justify-content: center;
img {
object-fit: cover;
@@ -69,8 +67,37 @@
}
}
.alt-text {
margin: 0px;
padding: 11px 7px;
box-sizing: border-box;
position: absolute;
bottom: 10px;
left: 10px;
width: 2.98em;
max-height: 25px;
white-space: pre;
overflow: hidden;
border-radius: 10px;
color: var(--fg_color);
font-size: 12px;
font-weight: bold;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(12px);
}
.alt-text:hover {
padding: 7px;
width: Min(230px, calc(100% - 10px * 2));
max-height: calc(100% - 10px);
line-height: 1.2em;
white-space: pre-wrap;
transition-duration: 0.4s;
transition-property: max-height;
}
.image {
display: inline-block;
display: flex;
}
// .single-image {

View File

@@ -137,6 +137,10 @@ type
url*: string
thumb*: string
Photo* = object
url*: string
altText*: string
GalleryPhoto* = object
url*: string
tweetId*: string
@@ -217,7 +221,7 @@ type
poll*: Option[Poll]
gif*: Option[Gif]
video*: Option[Video]
photos*: seq[string]
photos*: seq[Photo]
Tweets* = seq[Tweet]

View File

@@ -50,7 +50,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
buildHtml(head):
link(rel="stylesheet", type="text/css", href="/css/style.css?v=23")
link(rel="stylesheet", type="text/css", href="/css/style.css?v=24")
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=4")
if theme.len > 0:

View File

@@ -97,9 +97,9 @@ proc genNumberInput*(pref, label, state, placeholder: string; class=""; autofocu
label(`for`=pref): text label
input(name=pref, `type`="number", placeholder=p, value=state, autofocus=(autofocus and state.len == 0), min=min, step="1")
proc genImg*(url: string; class=""): VNode =
proc genImg*(url: string; class=""; alt=""): VNode =
buildHtml():
img(src=getPicUrl(url), class=class, alt="", loading="lazy")
img(src=getPicUrl(url), class=class, alt=alt, loading="lazy")
proc getTabClass*(query: Query; tab: QueryKind): string =
if query.kind == tab: "tab-item active"

View File

@@ -56,7 +56,7 @@ Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)}
<p>${text.replace("\n", "<br>\n")}</p>
#if tweet.photos.len > 0:
# for photo in tweet.photos:
<img src="${urlPrefix}${getPicUrl(photo)}" style="max-width:250px;" />
<img src="${urlPrefix}${getPicUrl(photo.url)}" style="max-width:250px;" />
# end for
#elif tweet.video.isSome:
<a href="${urlPrefix}${tweet.getLink}">

View File

@@ -50,10 +50,12 @@ proc renderAlbum(tweet: Tweet): VNode =
for photo in photos:
tdiv(class="attachment image"):
let
named = "name=" in photo
small = if named: photo else: photo & smallWebp
a(href=getOrigPicUrl(photo), class="still-image", target="_blank"):
genImg(small)
named = "name=" in photo.url
small = if named: photo.url else: photo.url & smallWebp
a(href=getOrigPicUrl(photo.url), class="still-image", target="_blank"):
genImg(small, alt=photo.altText)
if photo.altText.len > 0:
p(class="alt-text"): text "ALT " & photo.altText
proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool =
case playbackType