woocommerce / woocommerce

A customizable, open-source ecommerce platform built on WordPress. Build any commerce solution you can imagine.
https://woocommerce.com
9.41k stars 10.76k forks source link

Downloading audio files on iOS, if the download is "forced" the audio is mistaken for a Live Broadcast #19640

Closed fhaps closed 5 months ago

fhaps commented 6 years ago

Prerequisites

Steps to reproduce the issue

  1. Create a Downloadable product with an audio file
  2. Go to WooCommerce > Settings > Products > Downloadable products and make sure that the file download method is set to Force downloads.
  3. Process an order of your Downloadable product.
  4. From iOS, login to your account in the site, go to My Account > Downloads and try to open the audio file.

Expected/actual behavior

When I follow those steps, the audio opens in a new window and appears to be a "Live Broadcast". This means that I cannot fast forward or rewind the audio. After a minute or so, the audio stops and restarts. It never finishes all the way to the end.

I was expecting to be able to control the timer in the audio, and for the audio to play completely and not stop half way through.

Image Link: http://cld.wthms.co/ev5XiK

Isolating the problem

julesmeditation commented 6 years ago

I have the same issue. Any solutions or work arounds? Thanks so much

mikejolley commented 6 years ago

Not sure how to handle this one yet.

Related posts: https://stackoverflow.com/questions/45864283/force-ask-for-video-download-on-iphone-using-php / https://stackoverflow.com/questions/5924061/using-php-to-output-an-mp4-video

madeincosmos commented 6 years ago

Also reported in 1089130-zen

peterfabian commented 6 years ago

@tiagonoronha: had a look at this one as Mike asked me to help a bit with this one.

Random remarks from looking around today:

When using Force download method:

mikejolley commented 6 years ago

See https://github.com/woocommerce/woocommerce/pull/19984

harishankerr commented 5 years ago

I ran into a similar issue related to this problem. Here, the customer has dropbox download links for their audio files. If the download method was set to Force Downloads or X-Accel-Redirect/X-Sendfile (from WooCommerce > Settings > Products > Downloadable Products), the file simply would not download, and it would go into broadcast mode, just like what was mentioned in this GitHub issue: woocommerce/woocommerce#19640

The setting page I was referring to:

http://cld.wthms.co/b4MweS Full Size: http://cld.wthms.co/b4MweS

Here's what the download would look like:

http://cld.wthms.co/MUUobv Full Size: http://cld.wthms.co/MUUobv

The download link you can see in the screenshot (which I have purposely obfuscated) is the direct download link from the WooCommerce site. As you can see, iOS sees this as a Live Broadcast, and does not allow you to fast forward or rewind the track. There are no timings, and there isn't a seek button either.

Now, if I set this to Redirect Only, within: WooCommerce > Settings > Products > Downloadable Products, everything works as expected when one tries to download an audio file in iOS. However, the dropbox link shows up as the URL, and not the WooCommerce link (as expected):

http://cld.wthms.co/fqzs9p Full Size: http://cld.wthms.co/fqzs9p

On that note, my hypothesis, at this point is that this is an extension of the WooCommerce core issue (woocommerce/woocommerce#19640) , and not an Amazon S3 Issue (as I was able to replicate the same problem with a Dropbox link).

I can confirm that this PR: woocommerce/woocommerce#19984 (which originally fixed woocommerce/woocommerce#19640) has fixed the problem from direct download links (tested this a couple of times in my local test site). But while trying to download products using external links in the download field, the problem reappears.

Can y'all look into this once again? CC: @peterfabian @kloon @mikejolley

jessepearson commented 5 years ago

Reopened since there was no reply.

peterfabian commented 5 years ago

Hi,

Looking at this again.

Using Dropbox share link

If I use the dropbox share link, which looks sth like https://www.dropbox.com/s/hash-code/file-name.mp3, then:

WC Force downloads

Using the dropbox share link and download settings set to Force downloads:

WC X-Accel-Redirect/X-Sendfile

WC Redirect only

Using direct download link from Dropbox

If I use the dropbox direct download link, which looks sth like https://uc.....18.dl.dropboxusercontent.com/cd/0/get/Aa....ISrQ/file?dl=1#, then:

WC Force downloads

WC X-Accel-Redirect/X-Sendfile

WC Redirect only

This was all carried out on Pressable, where the web server is Nginx. I think it might behave differently on Apache webserver.

I can try to find a hosting solution having Apache webserver and see if I can replicate the problem with live broadcast, but not sure. File downloads on iOS are a bit peculiar...

peterfabian commented 5 years ago

In addition to the above, I noticed that dropbox has a query parameter dl which affects how the file is served:

@harishankerr , do you know which format of links were used in your testing?

  1. share link with dl set to 0, i.e. https://www.dropbox.com/s/hagal9612b53k21/Dee_Yan-Key_-_02_-_The_Grim_Reaper.mp3?dl=0
  2. share link with dl set to 1, i.e. https://www.dropbox.com/s/hagal9612b53k21/Dee_Yan-Key_-_02_-_The_Grim_Reaper.mp3?dl=1
  3. direct download link (that expires quickly), i.e. https://uc1d9bb8a41383ae5294bedceac3.dl.dropboxusercontent.com/cd/0/get/AbNpp2qRW5EXwdQ6PwuC133g_zxEyZngPoO1b4a6l5w5RlILML9Bw3Kzgb9VrW2C2es6k44M7xvD-CTXMYlcgm7_S9eQx31BUkiFH6fFBv49ow/file?dl=1#

I can see jurassic.ninja uses Apache, so testing over there now.

Force downloads:

On desktop

X-Accel-Redirect/X-Sendfile

On desktop

To me, it seems that the problem here is that we can't really seek in remote files. Theoretically, we could implement something by using stream_get_contents function, but that means we need to read the file in php and send it to the user in chunks, which defeats the purpose of having files in dropbox (as each user download would download the file to the WC server and send it out to the user, at which point it can be quite network intensive for the WC server, it would basically act as a middle man asking for data from dropbox and sending it off to the user).

I'm not even sure there is an optimal solution here.

rrennick commented 5 years ago

I'm not even sure there is an optimal solution here.

@peterfabian Would a HTTP HEAD and looking at the headers help here?

peterfabian commented 5 years ago

@rrennick Can you maybe elaborate please?

I did a bit of experimentation over the last 2 days and was able to get the size of the download from the remote source by reading the HTTP header (which previously just failed, so even desktop browsers could not time the download). However, the problem is that when iPhone downloads a video/audio, it sends out multiple range requests, e.g. for the file referenced above:

  1. Normal HTTP GET request
  2. HTTP RANGE request for offset 0, length 2 (bytes)
  3. HTTP RANGE request for offset 0, length 65536
  4. HTTP RANGE request for offset 131072, length 65536
  5. HTTP RANGE request for offset 65536, length 65536
  6. HTTP RANGE request for offset 196608, length 7522608

When looking at dropbox download, there are two redirects and the request ends up with direct URL of type 3. (from my above comment), e.g.

  1. HTTP/1.1 301 Moved Permanently
    Server: nginx
    Date: Tue, 12 Feb 2019 17:59:26 GMT
    Content-Type: text/html; charset=utf-8
    Connection: keep-alive
    Cache-Control: no-cache
    Content-Security-Policy: sandbox
    Location: /s/dl/hagal9612b53k21/Dee_Yan-Key_-_02_-_The_Grim_Reaper.mp3
    Pragma: no-cache
    Referrer-Policy: origin-when-cross-origin
    Set-Cookie: locale=en; Domain=dropbox.com; expires=Sun, 11 Feb 2024 17:59:26 GMT; Path=/; secure
    Set-Cookie: gvc=NTY1NzU5NjM1NjExMjUyNzI5Nzk5MzUwNDQ0ODU0NDA5MTI3OA%3D%3D; expires=Sun, 11 Feb 2024 17:59:26 GMT; httponly; Path=/; secure
    Set-Cookie: flash=; Domain=dropbox.com; expires=Tue, 12 Feb 2019 17:59:26 GMT; Path=/; secure
    Set-Cookie: puc=; expires=Tue, 12 Feb 2019 17:59:26 GMT; httponly; Path=/; secure
    Set-Cookie: bang=; Domain=dropbox.com; expires=Tue, 12 Feb 2019 17:59:26 GMT; Path=/; secure
    Set-Cookie: t=s88nN4WhauUNTvI83VZ1mIEm; Domain=dropbox.com; expires=Fri, 11 Feb 2022 17:59:26 GMT; httponly; Path=/; secure
    Set-Cookie: __Host-js_csrf=s88nN4WhauUNTvI83VZ1mIEm; expires=Fri, 11 Feb 2022 17:59:26 GMT; Path=/; secure
    X-Content-Type-Options: nosniff
    X-Dropbox-Request-Id: c3bcd153d8c8084be5375f72cf4b6ac1
    X-Frame-Options: DENY
    X-Robots-Tag: noindex, nofollow, noimageindex
    X-Xss-Protection: 1; mode=block
    Strict-Transport-Security: max-age=15552000; includeSubDomains

    then

  2. HTTP/1.1 302 Found
    Server: nginx
    Date: Tue, 12 Feb 2019 17:59:31 GMT
    Content-Type: text/html; charset=utf-8
    Connection: keep-alive
    Cache-Control: no-cache
    Content-Security-Policy: sandbox
    Location: https://uced21ace8ac83c3b15782880ca8.dl.dropboxusercontent.com/cd/0/get/AbPbRgswRYhNskxUkxHw98H8CmSYV1nsjL--hfP6C1eng_pbLv5bEsbMuvs7WGdFvoGMQU-L0NTFWXnAPz-7djWI2EFNa-ovKJFfmePKRBl4rw/file?dl=1#
    Pragma: no-cache
    Referrer-Policy: origin-when-cross-origin
    Set-Cookie: locale=en; Domain=dropbox.com; expires=Sun, 11 Feb 2024 17:59:31 GMT; Path=/; secure
    Set-Cookie: gvc=NDM1NTQxNjkxMzcyNzA3NjUxMTg3MzgzNDYxNTU4NzAxOTQxOTE%3D; expires=Sun, 11 Feb 2024 17:59:31 GMT; httponly; Path=/; secure
    Set-Cookie: flash=; Domain=dropbox.com; expires=Tue, 12 Feb 2019 17:59:31 GMT; Path=/; secure
    Set-Cookie: puc=; expires=Tue, 12 Feb 2019 17:59:31 GMT; httponly; Path=/; secure
    Set-Cookie: bang=; Domain=dropbox.com; expires=Tue, 12 Feb 2019 17:59:31 GMT; Path=/; secure
    Set-Cookie: t=p6FsPLBQjKTLZS_mU9RQvWPs; Domain=dropbox.com; expires=Fri, 11 Feb 2022 17:59:31 GMT; httponly; Path=/; secure
    Set-Cookie: __Host-js_csrf=p6FsPLBQjKTLZS_mU9RQvWPs; expires=Fri, 11 Feb 2022 17:59:31 GMT; Path=/; secure
    X-Content-Type-Options: nosniff
    X-Dropbox-Request-Id: a11f11eb3fa3d43c7cc59e91265e5772
    X-Frame-Options: DENY
    X-Xss-Protection: 1; mode=block
    Strict-Transport-Security: max-age=15552000; includeSubDomains

    and finally 3.

    HTTP/1.1 200 OK
    Server: nginx
    Date: Tue, 12 Feb 2019 17:59:31 GMT
    Content-Type: application/binary
    Content-Length: 7719216
    Connection: keep-alive
    x-content-security-policy: sandbox
    content-disposition: attachment; filename="Dee_Yan-Key_-_02_-_The_Grim_Reaper.mp3"; filename*=UTF-8''Dee_Yan-Key_-_02_-_The_Grim_Reaper.mp3
    x-robots-tag: noindex, nofollow, noimageindex
    content-security-policy: referrer no-referrer; sandbox;
    x-content-type-options: nosniff
    accept-ranges: bytes
    etag: 3825d
    x-dropbox-request-id: 46701202965e80c0be451b4de905c2d5
    pragma: public
    cache-control: max-age=60
    referrer-policy: no-referrer
    x-webkit-csp: sandbox
    Vary: Origin
    X-Server-Response-Time: 478
    Strict-Transport-Security: max-age=15552000; includeSubDomains

this seems to advertise that it supports range requests (accept-ranges: bytes), so my hope was that redirecting user to this URL would fix the problem. However, navigating to the direct download URL on iPhone does not play the mp3 file at all.

Thus, my conclusion was that if dropbox links can only play the mp3 via their UI on iPhone and it's not really possible to download the mp3, then I'm not sure we have a chance to implement this as a middle man between the user and dropbox in any other way than downloading the file and serving it to the user, which is suboptimal.

Now I did a bit more googling and found this thread: https://www.dropboxforum.com/t5/Photos-and-videos/unable-to-play-back-Dropbox-MP3-files-in-iPhone-web-browser/td-p/244705

So it seems like there's a trick with dropbox to use parameter raw=1, i.e. * https://www.dropbox.com/s/hagal9612b53k21/Dee_Yan-Key_-_02_-_The_Grim_Reaper.mp3?raw=1 which plays fine on iphones.

Therefore, one thing that would work is to set File download method to Redirect only and use the raw=1 URL. That, however, reveals the direct dropbox link (which expires quite quickly), but also original dropbox URL (if the user checks Inspector/network redirects), which can be a problem for some people.

I'm trying to implement Force download (i.e. to read the file from dropbox in chunks using stream_get_contents with correct offset and length as requested by iPhone and then sending it to the user from WC server), but it seems to be failing for some reason. Currently first 2 requests succeed, then the communication stops in the middle of request no 3.

rrennick commented 5 years ago

@rrennick Can you maybe elaborate please?

I was thinking you might be able to read the headers, get the content type, etc. to determine whether the download settings would work for that URL. If not, warn the shop manager/admin that it might not work according to their settings.

joshrives commented 4 years ago

I have this same issue happening with files located in AWS. To reproduce, visit this link in iOS on Safari and click the purple download button. Safari offers a prompt to View or Download the file. Click view and somewhere between 90 and 120 seconds, the audio will restart at the beginning.

Clicking on Download in the Safari prompt downloads the entire file as expected. Visiting the file directly in AWS also streams completely as expected.

In Woocommerce, the File Download Method is set to Force Downloads, but I also tried setting it to X-Accel-Redirect and it did not seem to resolve the issue.

This is happening on our staging server as well as production. The link above is from staging so I am happy to test any solutions there if needed.

ralucaStan commented 5 months ago

this was closed via https://github.com/woocommerce/woocommerce/pull/19984