Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Unfurl image URLs #3901

Merged
merged 8 commits into from
Aug 21, 2023
Merged

feat: Unfurl image URLs #3901

merged 8 commits into from
Aug 21, 2023

Conversation

ilmotta
Copy link
Contributor

@ilmotta ilmotta commented Aug 16, 2023

Closes #3761

Summary

This PR adds support for unfurling static image URLs (not GIFs, not animated WebPs), such as https://placehold.co/[email protected]. It also compresses images before returning them as data URIs to clients.

I'll run generate after a round of review to avoid polluting the PR.

Demo

Screenshot showing the mobile app unfurling both WebP and JPEG image URLs (please, ignore UI inconsistencies).

Important

  • This PR is a potential breaking change because it adds a field Type to common.message.LinkPreview and a validation to check it exists. There's no fallback type. In the mobile client, I had to make a tiny change, which will become a PR (to be added here).
  • I'm assuming there's no data to migrate pertaining unfurled links (column user_messages.unfurled_links). This is a known pain point ever since URL unfurling (initial implementation) #3471 was opened. Given that "unfurling" is still evolving, I'd say it's wise to finally move away from the serialized column approach to a separate table. I'd be interested to work on this problem soon (and maybe finish it before the next desktop release lands).

About compression

The compression strategy implemented in this PR leverages the existing function images.CompressToFileLimits. I didn't implement a more comprehensive logic to consider the possibility of multiple image URLs being unfurled simultaneously, and the requirement that all of them fit within the target message size. For now, I believe it's good enough.

About animated WebP

Quick note about animated WebP: if you try to copy raw Giphy URLs, very often you'll get animated WebP resources, and not true GIF ones, for example https://i.giphy.com/media/IaWMz9Ln8OWvf66z6k/giphy.webp. As far as I checked, we don't support animated WebP in status-go.

The requirements for GIFs are still under discussion (see #3821). Once all requirements are clear, we can come back to animated WebP URLs.

@ilmotta ilmotta added feature E:New Unfurling API Implementation of the new unfurling API for all links labels Aug 16, 2023
@ilmotta ilmotta self-assigned this Aug 16, 2023
@status-im-auto
Copy link
Member

status-im-auto commented Aug 16, 2023

Jenkins Builds

Click to see older builds (27)
Commit #️⃣ Finished (UTC) Duration Platform Result
e579507 #1 2023-08-16 19:21:21 ~1 min ios 📄log
e579507 #1 2023-08-16 19:21:26 ~1 min linux 📄log
e579507 #1 2023-08-16 19:21:26 ~1 min android 📄log
✖️ e579507 #1 2023-08-16 19:22:50 ~2 min tests 📄log
✔️ a83f804 #2 2023-08-17 20:37:01 ~2 min linux 📦zip
✖️ a83f804 #2 2023-08-17 20:37:22 ~2 min tests 📄log
✔️ a83f804 #2 2023-08-17 20:37:36 ~2 min ios 📦zip
✔️ a83f804 #2 2023-08-17 20:38:38 ~3 min android 📦aar
ee9e510 #3 2023-08-17 20:37:24 ~18 sec linux 📄log
ee9e510 #3 2023-08-17 20:37:57 ~17 sec ios 📄log
✖️ ee9e510 #3 2023-08-17 20:38:25 ~57 sec tests 📄log
ee9e510 #3 2023-08-17 20:39:04 ~18 sec android 📄log
✔️ 57521f3 #4 2023-08-18 06:14:12 ~1 min linux 📦zip
✔️ 57521f3 #4 2023-08-18 06:14:36 ~1 min android 📦aar
✔️ 57521f3 #4 2023-08-18 06:15:18 ~2 min ios 📦zip
✔️ 113cb4f #5 2023-08-18 14:07:33 ~1 min linux 📦zip
✔️ 113cb4f #5 2023-08-18 14:07:53 ~1 min android 📦aar
✔️ 113cb4f #5 2023-08-18 14:08:52 ~2 min ios 📦zip
✔️ 113cb4f #5 2023-08-18 14:35:02 ~28 min tests 📄log
✔️ 5b83e86 #6 2023-08-21 12:43:12 ~1 min linux 📦zip
✔️ 5b83e86 #6 2023-08-21 12:43:23 ~1 min android 📦aar
✔️ 5b83e86 #6 2023-08-21 12:44:29 ~2 min ios 📦zip
✔️ 5b83e86 #6 2023-08-21 13:09:57 ~27 min tests 📄log
✔️ 9b6e2b5 #7 2023-08-21 14:18:56 ~3 min linux 📦zip
✔️ 9b6e2b5 #7 2023-08-21 14:20:17 ~4 min ios 📦zip
✔️ 9b6e2b5 #7 2023-08-21 14:20:35 ~4 min android 📦aar
✖️ 9b6e2b5 #8 2023-08-21 14:57:50 ~1 min tests 📄log
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ 655b8f2 #8 2023-08-21 15:05:30 ~1 min linux 📦zip
✔️ 655b8f2 #8 2023-08-21 15:05:37 ~1 min android 📦aar
✔️ 655b8f2 #8 2023-08-21 15:06:46 ~2 min ios 📦zip
✔️ 655b8f2 #9 2023-08-21 15:34:11 ~29 min tests 📄log
✔️ 9e5c38e #9 2023-08-21 15:13:57 ~1 min android 📦aar
✔️ 9e5c38e #9 2023-08-21 15:15:05 ~2 min linux 📦zip
✔️ 9e5c38e #9 2023-08-21 15:15:52 ~3 min ios 📦zip
✖️ 9e5c38e #10 2023-08-21 15:35:51 ~1 min tests 📄log
✖️ 9e5c38e #11 2023-08-21 15:54:21 ~1 min tests 📄log
✔️ 9e5c38e #12 2023-08-21 16:24:58 ~29 min tests 📄log

protocol/linkpreview/linkpreview.go Outdated Show resolved Hide resolved
protocol/linkpreview/linkpreview.go Outdated Show resolved Hide resolved
protocol/common/message.go Show resolved Hide resolved
@ilmotta ilmotta force-pushed the feat/link-previews-for-image-urls branch 2 times, most recently from a83f804 to ee9e510 Compare August 17, 2023 20:36
@ilmotta
Copy link
Contributor Author

ilmotta commented Aug 17, 2023

For reviewers: I couldn't find a way to satisfy the linter when using the repo's nix shell, and because that's all I can use in my system, the PR is currently blocked because I can't run a good make generate.

I tried to go back to all other revisions that changed the nixpkgs revision in shell.nix, but all of them fail with the same errors. These are the steps to reproduce:

git checkout <any rev in status-go>
nix-shell --pure
make generate
make lint
# => lots of errors about copyLocks

I talked with @cammellos about this problem, and hopefully he will be able to solve the copyLocks errors 🙏🏼 Thanks a lot for that!

It seems that if the CI was integrating code into develop using nix, this sort of problem would never happen. This is wrong, @jakubgs corrected me, and the CI is actually using Nix.

cc'ing @jakubgs as an FYI

Copy link
Collaborator

@igor-sirotin igor-sirotin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, thank you! I will take this into desktop tomorrow 👌
Just one concert about no-extension image links.

protocol/linkpreview/linkpreview_test.go Show resolved Hide resolved
func newUnfurler(logger *zap.Logger, httpClient http.Client, url *neturl.URL) Unfurler {
if isSupportedImageURL(url) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. This way we won't be able to unfurl image links that don't end with required extension, e.g. https://placekitten.com/100/200. And Discord supports such links.

I think it would be great to decide if it's an image from HTTP header Content-Type: image/jpeg. Any chance it's possible to check the header without fetching the whole image?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started coding with this idea in mind @igor-sirotin, but in the end I decided against it in this PR for simplicity sake. To cleanly implement this logic we would need to change the logic that decides which unfurler to create, because the HTTP request would always need to be performed first. And because the request would have been made already, the unfurling logic of all unfurlers would change as well because the response body would already be available.

This should all work fine, unless I face issues related to the user agent. For example, requests to unfurl OpenGraph URLs need to use specific headers, otherwise some websites stubbornly fail. Hopefully the same headers can be used for image URLs.

Since you commented about this limitation, I now have another reason to implement this. I can do it on Monday (I'll be off this Friday).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance it's possible to check the header without fetching the whole image?

If we knew all servers understood HEAD requests, we'd be able to get responses without a body. So I think the best we can do is always request upfront and use that whole information to decide which unfurler to instantiate and pass the response to other functions.

It should work fine because we always fallback to OpenGraph to unfurl, so always requesting upfront won't incur in additional requests, since we would request something anyway.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No prob!
I think it makes sense to skip it for now then and add this later. It will be a status-go update with no breaking changes for desktop/mobile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect!

@ilmotta
Copy link
Contributor Author

ilmotta commented Aug 17, 2023

I will take this into desktop tomorrow

@igor-sirotin, I can't push a clean PR due to code generation issues. If I push the results of a make generate, the PR becomes a monstrosity of 15k lines and the linter fails. See #3901 (comment)

So I think this affects the desktop integration because you won't be able to build the library pointing to this branch. This is currently preventing me from opening a good PR in status-mobile too. Let's see if tomorrow the problem can be fixed, otherwise I'll need to ask somebody else to run make generate in their machine and push to this branch 😢

@igor-sirotin
Copy link
Collaborator

I can't push a clean PR due to code generation issues
... otherwise I'll need to ask somebody else to run make generate in their machine and push to this branch 😢

@ilmotta, make generate works fine for me, so I will push the updates to this branch, not to block this PR. We kinda have a milestone today 😄

@igor-sirotin
Copy link
Collaborator

Tested in desktop, works like a charm 👍 Thank you, @ilmotta
status-im/status-desktop#11940

@ilmotta
Copy link
Contributor Author

ilmotta commented Aug 18, 2023

Tested in desktop, works like a charm 👍 Thank you, @ilmotta status-im/status-desktop#11940

Sweet! 💯

@igor-sirotin igor-sirotin force-pushed the feat/link-previews-for-image-urls branch from 57521f3 to 113cb4f Compare August 18, 2023 14:05
@igor-sirotin
Copy link
Collaborator

Took an opportunity to rebase on latest develop to fix conflicts.

@igor-sirotin igor-sirotin force-pushed the feat/link-previews-for-image-urls branch from 5b83e86 to 9b6e2b5 Compare August 21, 2023 14:15
@ilmotta ilmotta force-pushed the feat/link-previews-for-image-urls branch from 655b8f2 to 9e5c38e Compare August 21, 2023 15:12
@ilmotta ilmotta merged commit 084d4ba into develop Aug 21, 2023
@ilmotta ilmotta deleted the feat/link-previews-for-image-urls branch August 21, 2023 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
E:New Unfurling API Implementation of the new unfurling API for all links feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unfurl image URLs
4 participants