Oddbean new post about | logout
 @damus @utxo the webmaster 🧑‍💻 #asknostr #nostr #damus #blossom #haven

Something strange happening with me: if i post videos to my haven blossom server, damus cannot load them on iOS (other clients working fine). But the same video uploaded to my website works fine. Is it a problem with damus? (seems so, since it works in other clients) is it a problem with haven? (also might be, since uploading to an apache webserver works) or is it the blossom implementation used by haven?

will send examples as reples.
 
 This one is on my apache server, not on blossom, and should work on damus.

https://girino.org/nostr/4d32eba13325eed7fd1926c3f9f0d6679da837c0228c8f3accd402790a776312.mp4 
 haven request is missing:

content-length
accept-ranges: bytes

it's possible ios video player requires one of these 
 haven response* 
 yes I noticed damus doesn't load any blossom video. its easy to blame damus but we use the default ios media player, so unless utxo wants videos to not work on ios at all, I suggest returning these headers and see if it fixes it. 
 So you walk into a bar, and there is a purple moose drinking water to get to the meadow, and some moose from some other forest walks in.

The entanglement explains where there are meese in various continents with most infinite connections.

••.••
Confusion entanglements non infinite life. Animals.

😵
#mainvolume 
 Or something 
 Although must be infinite mass to matter
😭 
 @utxo the webmaster 🧑‍💻 

cc @hzrd149 see blossom config feedback reqd for ios 
 I meant to say “any haven video” here not blossom 
 Ok I'll add  
 https://pkg.go.dev/net/http#ServeContent - this is what you are looking for (it will handle partial byte ranges and all mentioned headers). 
 but AFAIK, this needs to be done on khatru, because it does not expose the required objects. that's why i involved @fiatjaf in the thread. 
 Try to replace `io.Copy` with `http.ServeContent` and see what happens (you can fork Khatru and use go to replace khatru dependency in Haven, this is how I tested all of the CORS stuff before) 
 i'll try it tomorrow. thanks a lot! 
 I thought it was because you liked me. 
 I DO like you, the bug was just an excuse ;) 
 this worked wonders! I'll keep testing it and submit a pull request by tomorrow.

the changes were a little more complex than just adding a call to ServeContent (they always are) but i think i managed to get everything right. see here: https://github.com/fiatjaf/khatru/compare/master...girino:khatru:master

(there is still some debug "prints" in there, i'll remove them before the pull request) 
 Looking good! If you’re open to a small suggestion, and if fiatjaf doesn’t mind introducing some early-stage backwards-incompatible changes to khatru's Blossom’s API, I think you could simplify things by changing the return type here https://github.com/fiatjaf/khatru/blob/76ecf4f7914a93b7ec4f0cc0c823304a7402adab/blossom/server.go#L18 to `io.ReadSeeker`. This would allow you to eliminate the `io.Copy` fallback code and the `Content-Type` handling, as `http.ServeContent` also takes care of  that for you. It even handles ETags and cache-related headers, so I’d expect your patch to significantly improve things for all clients—not just iOS. Well done! 
 #nostrdam was fun.
That moment of time when the future happened.
@Tanja was there as well, along with @Ian and @nextblockcoffee 

The butterflies conga. 
At least the entanglement answer the most common question of how do you know #nostr. https://image.nostr.build/d45e839adc02c507bf50c174677d3c992cce0ac5e46513ea8b1be46dfb878f45.jpg  
 Unexpected to have the science department to hold the knife

Public transport science department of expensive fireworks by the word on the street https://image.nostr.build/e44271c4e0ecd555d2f5307a98fdb8a7b42ab5d511f59eae3863f1a88b18ee0d.jpg  
 I didn't know IOS required range requests for videos to work but i guess it makes sense

right now the blossom spec does not mention anything about content-length or accept-ranges but it might be good to add it, thoughts? 
 @utxo the webmaster 🧑‍💻 wdyt 
 If iOS requires it we basically have no choice  
 https://github.com/hzrd149/blossom-server/issues/16 @hzrd149 not sure if this is the right repo 
 I think that by specs he meant this one: https://github.com/hzrd149/blossom

But if the reference blossom server is not handling range requests / partial content the issue above is also valid. 
 Thanks, good point. Added ticket there so @hzrd149 can be reminded in more than one place 💪 
 fwiw im not sure if it is the issue, its just the biggest thing i saw that was different between the headers of the server responses. Would be good to know which header specifically is the issue, assuming both servers are returning the same data and there isn’t some other subtle issue. 
 It was indeed the issue, already fixed and tested by Girino and PR submitted to khatru upstream. (Check rest of this thread) 
 i wouldn't be so bold, since i din't mess directly with the headers. I just used a different API http.ServeContent, that handles the headers for me. It might any other header handled by this API that we were not aware of. We should conduct tests manually setting headers to check which one is the one required by iOS.

or simply leave it be, and document that videos might not work in iOS without extra headers, and let the devs fend for themselves. 
 It’s probably in the apple docs somewhere but im too lazy to find it 
 https://stackoverflow.com/questions/3397241/does-iphone-ipad-safari-require-accept-ranges-header-for-video 
 BotGPT:

“When dealing with video playback on iOS devices, especially when using third-party video storage servers, it's important to ensure that the server's response headers are properly configured to support smooth streaming and playback. Here are some key HTTP headers and their ranges that you should consider:

1. **Content-Type**: This header indicates the media type of the resource. For video files, common types include:
   - `video/mp4` for MP4 files
   - `video/quicktime` for MOV files
   - `video/x-ms-wmv` for WMV files
   - `video/webm` for WebM files

2. **Accept-Ranges**: This header should be set to `bytes` to indicate that the server supports range requests. This is crucial for seeking within the video.
   - Example: `Accept-Ranges: bytes`

3. **Content-Length**: This header specifies the total size of the video file in bytes. It is important for the client to know the size of the file for buffering and playback.
   - Example: `Content-Length: 12345678`

4. **Content-Range**: This header is used in response to a range request. It indicates the range of bytes being sent in the response and the total size of the file.
   - Example: `Content-Range: bytes 0-999/12345678`

5. **Cache-Control**: This header can be used to control caching behavior. For video content, you might want to set it to prevent caching or allow it based on your needs.
   - Example: `Cache-Control: no-cache` or `Cache-Control: public, max-age=3600`

6. **Expires**: This header can be used to specify when the content should be considered stale. It can be set to a future date to allow caching.
   - Example: `Expires: Wed, 21 Oct 2025 07:28:00 GMT`

7. **Cross-Origin Resource Sharing (CORS)**: If your video is hosted on a different domain, you may need to set CORS headers to allow access from your iOS app.
   - Example: `Access-Control-Allow-Origin: *`

8. **Content-Disposition**: This header can be used to suggest how the content should be handled (inline or as an attachment).
   - Example: `Content-Disposition: inline; filename="video.mp4"`

### Example of a Response Header for Video Playback

Here’s an example of what the response headers might look like for a video file:

```
HTTP/1.1 206 Partial Content
Content-Type: video/mp4
Accept-Ranges: bytes
Content-Range: bytes 0-999/12345678
Content-Length: 1000
Cache-Control: no-cache
Expires: Wed, 21 Oct 2025 07:28:00 GMT
Access-Control-Allow-Origin: *
```

### Summary

To ensure optimal video playback on iOS devices from a third-party video storage server, make sure to implement the above headers correctly. This will help facilitate smooth streaming, seeking, and overall better user experience.” 
 Partial content goes beyond headers. It requires additional logic to handle the correct ranges, HTTP status codes, and more. The `http.ServeContent` function took care of all this for you. No worries about being "bold" here. I’d much rather have range requests explicitly specified in the Blossom specs (it’s a crucial feature for a media server anyway) than deal with more half-baked, hardcoded headers scattered everywhere.

We already have too many incorrect or overly simplified assumptions about how HTTP works baked into most Nostr libraries, clients, and relays. Having something in the standarda is actually the right approach if we want Blossom to succeed. 
 Github issue for discussing it further https://github.com/hzrd149/blossom/issues/37 
 These are both pretty important for mobile, since sometimes we refuse to download things that are too big in low data mode, and ranges are nice for incremental downloads. Could mention it as a “SHOULD” 
 maybe an "accept-ranges: none" is enough? will try that later. 
 It probably uses ranges for incremental downloading if the connection drops 
 It’s a great video, special case when needing to adapt to the entanglement in question causing meese.

https://youtu.be/1WkvomHrfVo?si=YX6tBWwTqpvaxijw 
 I was looking into the code for haven, and this would probably need to be fixed in khatru itself, somewhere around here: https://github.com/fiatjaf/khatru/blob/master/blossom/handlers.go#L193

So i think we need to involve @fiatjaf here. 
 Can you try to PR it? Fiatjaf and I don't have iOS it's hard for us to test 
 @Anthony Accioly , you're better at this than i am. I'll try it tomorrow, but i don't think i know golang enough to deal with it. Do you have an iOS device where you can test this? 
 I unintentionally forked the conversation above. I don't have an iOS device but happy to help if you get stuck  :). 
 This is such a shameless excuse. You don't need iOS to add a Content-Length header. 
 But we can't know if this is actually causing the issue on iOS  
 This works on Damus 
 Problem solved here @utxo the webmaster 🧑‍💻. 
 @elsat