Fix: generate video thumbnails locally when server provides none#81
Fix: generate video thumbnails locally when server provides none#81beezly wants to merge 7 commits intoviktorstrate:mainfrom
Conversation
31947fb to
d1d8e17
Compare
viktorstrate
left a comment
There was a problem hiding this comment.
Thank you for the PR, I've left some comments.
I'm wondering how big a fraction of homeservers generate thumbnails. Is it common for videos to not have thumbnails?
A complete sidenote: It would be nice to support blurhashes, but I guess most videos without thumbnails probably also lack a bluhash since they would likely be computed based on a thumbnail to begin with.
I've implemented blurhash in my "attachments" branch, so we could definitely use it. |
When I was testing, I didn't have video thumbnails (that's why I ended up implementing it because they would all show as nil). I think it should be possible to reuse some of this when it comes to handling attachments anyway, so it will probably be needed in the future for that. |
Prevent division by zero when image metadata reports zero or missing dimensions. Without this guard, aspectRatio computes infinity or NaN, which causes SwiftUI layout issues inside NSTableView cells.
…llback - Guard against zero width/height in aspect ratio calculation (same as image fix). - Reorder modifiers: .aspectRatio() before .frame(maxHeight:) so the aspect ratio is applied before the height constraint, matching the intended sizing behaviour. - Add minHeight when thumbnailSource is nil. Without a thumbnail, MatrixImageView renders a Rectangle with no intrinsic size, causing .aspectRatio(.fit) to collapse the video to zero height.
Replace the conditional if/else if blocks for reactions and read receipts with an always-present HStack that uses frame(height: 0) and .clipped() when empty. Conditional view branches (if !reactions.isEmpty / else if) change the SwiftUI view tree structure when data loads asynchronously. Inside an NSHostingView table cell, this triggers invalidateIntrinsicContentSize during layout, contributing to an infinite constraint update loop that crashes the app. Keeping the view tree stable avoids this trigger.
NSHostingView inside NSTableView with usesAutomaticRowHeights enters an infinite layout loop: layout() flushes SwiftUI transactions, which invalidate constraints, which re-enter layout. AppKit detects this and crashes with NSGenericException. Replace the entire auto-sizing mechanism with SelfSizingHostingView: - sizingOptions = [] prevents the hosting view from participating in Auto Layout constraint solving. - invalidateIntrinsicContentSize() is overridden to NOT call super (which would re-enter the constraint system) but instead schedule a coalesced async height update via DispatchQueue.main.async. - measureHeight() uses a temporary NSHostingController.sizeThatFits() to properly measure content at the cell width, including text wrapping. - heightOfRow returns cached heights (default 60px). noteHeightOfRows is called with animation duration 0 to avoid visible row resizing. Also removes the old measurementHostingView and handleTableResize.
d1d8e17 to
060c697
Compare
… toggle - Generate thumbnails using AVAssetImageGenerator when the server provides none and the user enables 'Generate video thumbnails' in settings (off by default, downloads the video) - Decode blurhash from event metadata as a lightweight placeholder when no thumbnail or generated image is available (no download needed) - Refactor video download into shared downloadVideo() helper, reused by both playback and thumbnail generation - Add BlurHashDecoder.swift (MIT, based on woltapp/blurhash) - Add settings toggle in Appearance preferences
060c697 to
9f46a1d
Compare
|
I've added the "Generate video thumbnails" setting, with a help item explaining that it will download videos to do it. Also added the blurhash decode implementation from https://github.com/woltapp/blurhash - It's MIT licensed, so it should be fine to include so long as we keep the copyright notice. Also implemented the other suggestion about sharing loadVideo. |
|
I'll take this out of draft once 84/85 go in. |
Summary
Improve video thumbnail handling in the timeline:
blurhashstring from event metadata as a lightweight placeholder when no server thumbnail exists. No download required — uses only the metadata already in the event.AVAssetImageGenerator. Gated behind a "Generate video thumbnails" toggle in Settings → Appearance (off by default, since it downloads the full video).downloadVideo()helper reused by both playback and thumbnail generation, avoiding duplicate downloads.thumbnailViewcomputed property: Replaces inlineMatrixImageViewwith a priority chain: server thumbnail → generated thumbnail → blurhash → grey placeholder.Thumbnail priority
thumbnailSource)MatrixImageView)Depends on