Multi-platform: support multiple files per version #201

Merged
Oleg-Komarov merged 43 commits from multi-os into main 2024-07-09 16:27:46 +02:00
5 changed files with 37 additions and 60 deletions
Showing only changes of commit d55c3c9115 - Show all commits

View File

@ -773,36 +773,22 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
permissions.append({'slug': slug, 'reason': reason, 'name': all_permission_names[slug]})
return permissions
def _get_download_name(self, platform=None) -> str:
def _get_download_name(self, file) -> str:
"""Return a file name for downloads."""
parts = [self.extension.type_slug_singular, self.extension.slug, f'v{self.version}']
if platform:
parts.append(platform)
if platforms := file.get_platforms():
parts.extend(platforms)
return f'{"-".join(parts)}.zip'
def get_download_url(self, platform=None, append_repository_and_compatibility=True) -> str:
filename = self._get_download_name(platform=platform)
if platform:
download_url = reverse(
'extensions:version-platform-download',
kwargs={
'type_slug': self.extension.type_slug,
'slug': self.extension.slug,
'version': self.version,
'platform': platform,
'filename': filename,
},
)
else:
download_url = reverse(
'extensions:version-download',
kwargs={
'type_slug': self.extension.type_slug,
'slug': self.extension.slug,
'version': self.version,
'filename': filename,
},
)
def get_download_url(self, file, append_repository_and_compatibility=True) -> str:
filename = self._get_download_name(file)
download_url = reverse(
'extensions:download',
kwargs={
'filehash': file.hash,
'filename': filename,
},
)
if append_repository_and_compatibility:
params = {
'repository': '/api/v1/extensions/',
@ -810,10 +796,8 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
}
if self.blender_version_max:
params['blender_version_max'] = self.blender_version_max
if platform:
params['platforms'] = platform
elif platforms := self.platforms.all():
params['platforms'] = ','.join([p.slug for p in platforms])
if platforms := file.get_platforms():
params['platforms'] = ','.join(platforms)
Oleg-Komarov marked this conversation as resolved Outdated

Would it make sense to use file hash in the URL?
This would allow skipping the file-selecting logic in the public view altogether by using the hash to retrieve the file directly.

Would it make sense to use file hash in the URL? This would allow skipping the file-selecting logic in the `public` view altogether by using the hash to retrieve the file directly.
query_string = urlencode(params)
download_url += f'?{query_string}'
return download_url
@ -824,9 +808,9 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
file = files[0]
return [
{
'name': self._get_download_name(platform=None),
'name': self._get_download_name(file),
'size': file.size_bytes,
'url': self.get_download_url(platform=None),
'url': self.get_download_url(file),
}
]
platform2file = {}
@ -841,10 +825,10 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
platform2file[platform] = file
return [
{
'name': self._get_download_name(platform=p),
'name': self._get_download_name(file),
'platform': p,
'size': file.size_bytes,
'url': self.get_download_url(platform=p),
'url': self.get_download_url(file),
}
for p, file in platform2file.items()
]
@ -853,15 +837,12 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
build_list = []
for file in self.files.all():
platforms = file.get_platforms() or []
platform = len(platforms) and platforms[0] or None
# if file has multiple platforms, picking the first one should still produce a correct
# download_url
build_list.append(
{
'name': self._get_download_name(platform=platform),
'name': self._get_download_name(file),
'platforms': platforms,
'size': file.size_bytes,
'url': self.get_download_url(platform=platform),
'url': self.get_download_url(file),
}
)
return build_list

View File

@ -28,6 +28,7 @@ urlpatterns = [
path('search/', public.SearchView.as_view(), name='search'),
path('tag/<slug:tag_slug>/', public.SearchView.as_view(), name='by-tag'),
path('team/<slug:team_slug>/', public.SearchView.as_view(), name='by-team'),
path('download/<filehash>/<filename>', public.file_download, name='download'),
re_path(
rf'^(?P<type_slug>{EXTENSION_SLUGS_PATH})/',
include(
@ -91,11 +92,6 @@ urlpatterns = [
public.extension_version_download,
name='version-download',
),
path(
'<slug:slug>/<version>/<platform>/download/<filename>',
public.extension_version_platform_download,
name='version-platform-download',
),
path('<slug:slug>/versions/', manage.VersionsView.as_view(), name='versions'),
],
),

View File

@ -93,7 +93,7 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
'archive_size': file.size_bytes,
'archive_url': self.request.build_absolute_uri(
matching_version.get_download_url(
platform=self.platform,
file,
append_repository_and_compatibility=False,
)
),

View File

@ -4,21 +4,19 @@ import logging
from django.contrib.auth import get_user_model
from django.db import connection
from django.db.models import Count, Q
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView
from extensions.models import Extension, Version, Tag
from constants.base import (
EXTENSION_TYPE_SLUGS,
EXTENSION_TYPE_PLURAL,
EXTENSION_TYPE_CHOICES,
)
from stats.models import ExtensionView
from extensions.models import Extension, Version, Tag
from files.models import File
from stats.models import ExtensionDownload, ExtensionView, VersionDownload
import ratings.models
from stats.models import ExtensionDownload, VersionDownload
import teams.models
User = get_user_model()
@ -53,14 +51,16 @@ class HomeView(ListedExtensionsView):
def extension_version_download(request, type_slug, slug, version, filename):
"""A backward-compatible shortcut for the method below.
"""A backward-compatible url for downloads.
No platform is specified, assuming only a single file for a given version.
Assuming only a single file for a given version.
"""
return extension_version_platform_download(request, type_slug, slug, version, None, filename)
extension_version = get_object_or_404(Version, extension__slug=slug, version=version)
file = extension_version.files.first()
return file_download(request, file.hash, filename)
def extension_version_platform_download(request, type_slug, slug, version, platform, filename):
def file_download(request, filehash, filename):
"""Download an extension version and count downloads.
This method processes urls constructed by Version.get_download_list.
@ -70,13 +70,13 @@ def extension_version_platform_download(request, type_slug, slug, version, platf
drag&drop.
Also see $arg_filename usage in playbooks/templates/nginx/http.conf
"""
extension_version = get_object_or_404(Version, extension__slug=slug, version=version)
# TODO check file status
file = get_object_or_404(File, hash=filehash, version__isnull=False)
extension_version = file.version.first()
ExtensionDownload.create_from_request(request, object_id=extension_version.extension_id)
VersionDownload.create_from_request(request, object_id=extension_version.pk)
if file := extension_version.get_file_for_platform(platform):
url = file.source.url
return redirect(f'{url}?filename={filename}')
raise Http404()
url = file.source.url
return redirect(f'{url}?filename={filename}')
class SearchView(ListedExtensionsView):

View File

@ -82,7 +82,7 @@ class TestTasks(TestCase):
self.assertEqual(response.status_code, 200)
response = self.client.get(version.extension.get_versions_url())
self.assertEqual(response.status_code, 200)
response = self.client.get(version.get_download_url())
response = self.client.get(version.get_download_url(version.files.first()))
self.assertEqual(response.status_code, 302)
self.assertEqual(
response['Location'],