Forums

http headers (content-range) wrong for static audio files (Django)(Safari)

Hello! I hope I find you well. I'm having a bit of difficulty configuring my Django site to serve audio correctly.

As it happens, I'm getting a response status 200 for the initial response, which is byte range 0-1. On my local Django server, I get 206 and appropriate Content-Range & Content-Length values.
Chrome and Firefox deal with this with no problem and manage to display an audio player with seeking, but Safari chokes and displays the audio as live streaming. I need to support Safari, so unfortunately I need to find a fix for this.

I did have the same problem locally as well until I found a very helpful Middleware class. For some reason this doesn't seem to have any effect on PythonAnywhere and I am very stuck trying to debug.

If anyone has any idea how to proceed, I'd be much obliged for some help. :-)

Are you serving the audio via our static files system? That would normally be the correct thing to do for static content like that, but unfortunately our system doesn't support the specific content-range and length headers that Safari needs. The best workaround is to serve the audio via a URL that is handled by your Django code, and then you can set the headers just like you did when you were running your site on your own machine.

Thank you! That explains a lot. Sorry for my ignorance here, but can you give me an example for serving it out like that? There is the static helper for urlpatterns but that only works in Debug mode.

I think the best way to do this would be to put the audio files in a separate audio directory inside your Django BASE_DIR (so, the same directory as manage.py). Then, set up a URL like this:

path('audio/<str:filename>', audio_view)

...in one of your modules, and define the audio_view like this:

@csrf_exempt
def audio_view(request, filename):
    response = FileResponse(open(os.path.join(settings.BASE_DIR, "audio", filename), "rb"))
    return response

...then combined with that middleware you linked to, it should work fine. You'd then need to use URLs like /audio/something.mp3 in your templates to reference the audio files.

I appreciate your help. The support around here is exemplary.
Now, I should have mentioned that these files are FileFields in a Django model so unless I've missed something; they are inextricably tied to the MEDIA_ROOT, which is handled by the PythonAnywhere static files system by necessity.
I've looked for a method to point to an alternative folder like the one you've suggested… I even tried setting upload_to='../audio/' on the FileField but I was sadly thwarted by Django labeling that as a SuspiciousFileOperation

I feel like I'm tantalizingly close to a solution here but I just can't see it.


Edit: Whoops, it looks like emoji break this whole comment box.
I meant to add an aside about this older topic I found in which you mentioned raising a ticket about having these headers implemented — is that still on the table?

It looks like you can still keep them somewhere in the subdirectory under MEDIA_ROOT, open them in the view and serve them from Django.

You're right! Thanks so much, everything is working great now.
In service to any future visitors to this thread, I'd like to put together the code that I used:


# urls.py 
path('clips/<str:filename>', StaticAudioView)

# views.py
@csrf_exempt
def StaticAudioView(request, filename):
  response = FileResponse(open(os.path.join(settings.MEDIA_ROOT, "audio/", filename), "rb"))
  return response

And I added a method to my model just to get a url without 'media/'. I'm using Django Rest Framework so I added that to my serializer.

# models.py
audioClip = models.FileField(
  upload_to='audio/',
)

def get_static_audio_url(self):
  fullUrl = self.audioClip.url
  return ''.join(fullUrl.split('media/'))

# serializers.py
static_audio_url = serializers.SerializerMethodField()
def get_static_audio_url(self, obj):
    return obj.get_static_audio_url()

Good to see that you sorted it out.