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

[Bug]: iOS Safari incorrectly interprets file download content-type headers and adds incorrect file extensions #3310

Closed
chancez opened this issue Aug 20, 2024 · 3 comments · Fixed by #3319
Labels
bug Something isn't working

Comments

@chancez
Copy link
Contributor

chancez commented Aug 20, 2024

What happened?

This isnt' a bug with audiobookshelf, but I'm filing this issue because I'd like to implement a workaround for bad iOS Safari behavior.

Basically, when downloading a book such as my-favorite-book.m4b from the "audio tracks" section of audiobookshelf, Safari on iOS will prompt if you wish to view/download my-favorite-book.m4b.mp4, adding the .mp4 extension. Based on some testing, Safari on iOS (and apparently Chrome too) will interpret the Content-Type header and add an "appropriate" file extension to the existing file name being downloaded if needed.

Audiobookshelf is correctly returning Content-Type: audio/mp4 however, Safari interprets this to mean it should be an "mp4" file, causing it to add the .mp4 extension (even though "mp4" and "m4b" are technically the same, and m4b itself is a convention that Apple itself created... 🙃).

Now, this isn't the end of the world, but it means for certain audiobook readers, it will not open the file unless it's renamed, as these apps interpret the file as a video file based on the file extension. This means you must rename the file in the iOS files app before it can be imported into the audiobook reader app.

Now, this is something I can manage, but it's a lot to ask for my wife and my mom. What I'd like, is to see if you would be willing to accept a PR to add a work-around for this behavior. I've tested, and if you use Content-Type: audio/m4b (yes: which isn't standard) it doesn't add the file extension.

So I'd like to ask:

Would you accept a PR that:

  • Unconditionally returns Content-Type: audio/m4b for m4b files
    OR:
  • Checks the user-agent to see if the browser is Safari and returns Content-Type: audio/m4b for m4b files. This is probably more complex/complicated then simply using audio/m4b though.

References:

There's a ton of references if you search "Safari unwanted file extension" in Google.

What did you expect to happen?

The file is downloaded without the .mp4 extension.

Steps to reproduce the issue

On an iPad/iPhone:

  1. Open Safari
  2. Browse to audiobookshelf
  3. Open an audiobook with an m4b file in audiobookshelf
  4. Open the "Audio Tracks" section
  5. Click on the ⋮
  6. Click download
  7. Notice the prompt to download the file adds an extra extension

Audiobookshelf version

v2.12.3

How are you running audiobookshelf?

Docker

What OS is your Audiobookshelf server hosted from?

Linux

If the issue is being seen in the UI, what browsers are you seeing the problem on?

Safari on iOS

Logs

No response

Additional Notes

I've temporarily made a work-around in my reverse proxy to simply hardcode the content-type header to audio/m4b but that will cause issues for any other file types I try to download.

@chancez chancez added the bug Something isn't working label Aug 20, 2024
@advplyr
Copy link
Owner

advplyr commented Aug 20, 2024

Yeah we can add this. We should check the user agent before setting the audio/m4b mime type.

Mime type is being set here for download library file.

// Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(libraryFile.metadata.path))
if (audioMimeType) {
res.setHeader('Content-Type', audioMimeType)
}

uaParserJS library is added also that can be used to check the user agent string. Currently that is only being used for device objects.

async getDeviceInfo(req, clientDeviceInfo = null) {
const ua = uaParserJs(req.headers['user-agent'])

@chancez
Copy link
Contributor Author

chancez commented Aug 20, 2024

Great, thanks. I'll take a look at implementing this today or tomorrow hopefully.

Copy link

Fixed in v2.13.0.

@github-actions github-actions bot removed the awaiting release Issue is resolved and will be in the next release label Aug 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
2 participants