blender-studio/static_assets/viewsets.py

118 lines
3.9 KiB
Python

"""API viewsets for static assets."""
from rest_framework import filters
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from common.storage import get_s3_post_url_and_fields
from common.upload_paths import get_upload_to_hashed_path
from static_assets.serializers import StaticAssetSerializer, UploadSerializer
import common.mixins
import static_assets.models
class UploadViewSet(ViewSet):
"""Get an upload URL: can be used to upload large files directly to Studio's storage.
Example usage:
```python
import os.path
import requests
api_url = 'https://studio.blender.org/api'
api_token = 'YOUR_API_TOKEN'
headers = {'Authorization': f'Token {api_token}', 'Content-Type': 'application/json'}
path_to_source_file = 'path/to/file.mp4'
path_to_thumbnail_file = 'path/to/image.png'
def upload_file(path_to_file):
# Get an upload URL first
response = requests.post(
f'{api_url}/upload/',
json={'original_filename': os.path.basename(path_to_file)},
headers=headers,
)
response.raise_for_status()
response_json = response.json()
upload_fields = response_json['fields']
upload_url = response_json['url']
# Upload file to the upload_url
with open(path_to_file, 'rb') as f:
files = {'file': (os.path.basename(path_to_file), f)}
response = requests.post(upload_url, data=upload_fields, files=files)
# If upload is successful, HTTP response.status_code 204 is returned
response.raise_for_status()
print(f'Uploaded to {upload_fields["key"]}')
return upload_fields['key']
# Upload both source and thumbnail files
source_upload_path = upload_file(path_to_source_file)
thumbnail_upload_path = upload_file(path_to_thumbnail_file)
# Now, use the resulting upload paths to create a static asset
response = requests.post(
f'{api_url}/static-asset/',
json={
'source_path': source_upload_path,
'thumbnail_path': thumbnail_upload_path,
},
headers=headers,
)
response.raise_for_status()
static_asset_id = response.json()['id']
print(f'Created a static asset, ID: {static_asset_id}')
```
"""
# Use StaticAsset to make this (model-less) view set use model permissions
queryset = static_assets.models.StaticAsset.objects.none()
serializer_class = UploadSerializer
def get_view_name(self):
"""Override name of this viewset."""
return 'Get upload URL'
def create(self, request, *args, **kwargs): # noqa: D102
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
path = get_upload_to_hashed_path(None, data['original_filename'])
return Response(
self.serializer_class(data={**data, **get_s3_post_url_and_fields(path)}).initial_data
)
class StaticAssetViewSet(common.mixins.SetModifiedByViewMixin, viewsets.ModelViewSet):
"""List, create, update or search static assets.
To create or update static assets files,
use [Get Upload URL](/api/upload/) to upload the files first.
"""
queryset = static_assets.models.StaticAsset.objects.all()
serializer_class = StaticAssetSerializer
search_fields = [
'source',
'original_filename',
'user__full_name',
'user__email',
'author__full_name',
'author__email',
'source_type',
'section__name',
'assets__name',
]
filter_backends = (filters.SearchFilter,)
def perform_create(self, serializer):
"""Set additional fields before saving."""
serializer.validated_data['user'] = self.request.user
super().perform_create(serializer)