Multi-platform: support multiple files per version #201
@ -773,33 +773,19 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
|||||||
permissions.append({'slug': slug, 'reason': reason, 'name': all_permission_names[slug]})
|
permissions.append({'slug': slug, 'reason': reason, 'name': all_permission_names[slug]})
|
||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
def _get_download_name(self, platform=None) -> str:
|
def _get_download_name(self, file) -> str:
|
||||||
"""Return a file name for downloads."""
|
"""Return a file name for downloads."""
|
||||||
parts = [self.extension.type_slug_singular, self.extension.slug, f'v{self.version}']
|
parts = [self.extension.type_slug_singular, self.extension.slug, f'v{self.version}']
|
||||||
if platform:
|
if platforms := file.get_platforms():
|
||||||
parts.append(platform)
|
parts.extend(platforms)
|
||||||
return f'{"-".join(parts)}.zip'
|
return f'{"-".join(parts)}.zip'
|
||||||
|
|
||||||
def get_download_url(self, platform=None, append_repository_and_compatibility=True) -> str:
|
def get_download_url(self, file, append_repository_and_compatibility=True) -> str:
|
||||||
filename = self._get_download_name(platform=platform)
|
filename = self._get_download_name(file)
|
||||||
if platform:
|
|
||||||
download_url = reverse(
|
download_url = reverse(
|
||||||
'extensions:version-platform-download',
|
'extensions:download',
|
||||||
kwargs={
|
kwargs={
|
||||||
'type_slug': self.extension.type_slug,
|
'filehash': file.hash,
|
||||||
'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,
|
'filename': filename,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -810,10 +796,8 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
|||||||
}
|
}
|
||||||
if self.blender_version_max:
|
if self.blender_version_max:
|
||||||
params['blender_version_max'] = self.blender_version_max
|
params['blender_version_max'] = self.blender_version_max
|
||||||
if platform:
|
if platforms := file.get_platforms():
|
||||||
params['platforms'] = platform
|
params['platforms'] = ','.join(platforms)
|
||||||
Oleg-Komarov marked this conversation as resolved
Outdated
|
|||||||
elif platforms := self.platforms.all():
|
|
||||||
params['platforms'] = ','.join([p.slug for p in platforms])
|
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
download_url += f'?{query_string}'
|
download_url += f'?{query_string}'
|
||||||
return download_url
|
return download_url
|
||||||
@ -824,9 +808,9 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
|||||||
file = files[0]
|
file = files[0]
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'name': self._get_download_name(platform=None),
|
'name': self._get_download_name(file),
|
||||||
'size': file.size_bytes,
|
'size': file.size_bytes,
|
||||||
'url': self.get_download_url(platform=None),
|
'url': self.get_download_url(file),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
platform2file = {}
|
platform2file = {}
|
||||||
@ -841,10 +825,10 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
|||||||
platform2file[platform] = file
|
platform2file[platform] = file
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'name': self._get_download_name(platform=p),
|
'name': self._get_download_name(file),
|
||||||
'platform': p,
|
'platform': p,
|
||||||
'size': file.size_bytes,
|
'size': file.size_bytes,
|
||||||
'url': self.get_download_url(platform=p),
|
'url': self.get_download_url(file),
|
||||||
}
|
}
|
||||||
for p, file in platform2file.items()
|
for p, file in platform2file.items()
|
||||||
]
|
]
|
||||||
@ -853,15 +837,12 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
|||||||
build_list = []
|
build_list = []
|
||||||
for file in self.files.all():
|
for file in self.files.all():
|
||||||
platforms = file.get_platforms() or []
|
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(
|
build_list.append(
|
||||||
{
|
{
|
||||||
'name': self._get_download_name(platform=platform),
|
'name': self._get_download_name(file),
|
||||||
'platforms': platforms,
|
'platforms': platforms,
|
||||||
'size': file.size_bytes,
|
'size': file.size_bytes,
|
||||||
'url': self.get_download_url(platform=platform),
|
'url': self.get_download_url(file),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return build_list
|
return build_list
|
||||||
|
@ -28,6 +28,7 @@ urlpatterns = [
|
|||||||
path('search/', public.SearchView.as_view(), name='search'),
|
path('search/', public.SearchView.as_view(), name='search'),
|
||||||
path('tag/<slug:tag_slug>/', public.SearchView.as_view(), name='by-tag'),
|
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('team/<slug:team_slug>/', public.SearchView.as_view(), name='by-team'),
|
||||||
|
path('download/<filehash>/<filename>', public.file_download, name='download'),
|
||||||
re_path(
|
re_path(
|
||||||
rf'^(?P<type_slug>{EXTENSION_SLUGS_PATH})/',
|
rf'^(?P<type_slug>{EXTENSION_SLUGS_PATH})/',
|
||||||
include(
|
include(
|
||||||
@ -91,11 +92,6 @@ urlpatterns = [
|
|||||||
public.extension_version_download,
|
public.extension_version_download,
|
||||||
name='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'),
|
path('<slug:slug>/versions/', manage.VersionsView.as_view(), name='versions'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -93,7 +93,7 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
|
|||||||
'archive_size': file.size_bytes,
|
'archive_size': file.size_bytes,
|
||||||
'archive_url': self.request.build_absolute_uri(
|
'archive_url': self.request.build_absolute_uri(
|
||||||
matching_version.get_download_url(
|
matching_version.get_download_url(
|
||||||
platform=self.platform,
|
file,
|
||||||
append_repository_and_compatibility=False,
|
append_repository_and_compatibility=False,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -4,21 +4,19 @@ import logging
|
|||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from django.http import Http404
|
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
|
|
||||||
from extensions.models import Extension, Version, Tag
|
|
||||||
from constants.base import (
|
from constants.base import (
|
||||||
EXTENSION_TYPE_SLUGS,
|
EXTENSION_TYPE_SLUGS,
|
||||||
EXTENSION_TYPE_PLURAL,
|
EXTENSION_TYPE_PLURAL,
|
||||||
EXTENSION_TYPE_CHOICES,
|
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
|
import ratings.models
|
||||||
|
|
||||||
from stats.models import ExtensionDownload, VersionDownload
|
|
||||||
import teams.models
|
import teams.models
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
@ -53,14 +51,16 @@ class HomeView(ListedExtensionsView):
|
|||||||
|
|
||||||
|
|
||||||
def extension_version_download(request, type_slug, slug, version, filename):
|
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.
|
"""Download an extension version and count downloads.
|
||||||
|
|
||||||
This method processes urls constructed by Version.get_download_list.
|
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.
|
drag&drop.
|
||||||
Also see $arg_filename usage in playbooks/templates/nginx/http.conf
|
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)
|
ExtensionDownload.create_from_request(request, object_id=extension_version.extension_id)
|
||||||
VersionDownload.create_from_request(request, object_id=extension_version.pk)
|
VersionDownload.create_from_request(request, object_id=extension_version.pk)
|
||||||
if file := extension_version.get_file_for_platform(platform):
|
|
||||||
url = file.source.url
|
url = file.source.url
|
||||||
return redirect(f'{url}?filename={filename}')
|
return redirect(f'{url}?filename={filename}')
|
||||||
raise Http404()
|
|
||||||
|
|
||||||
|
|
||||||
class SearchView(ListedExtensionsView):
|
class SearchView(ListedExtensionsView):
|
||||||
|
@ -82,7 +82,7 @@ class TestTasks(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
response = self.client.get(version.extension.get_versions_url())
|
response = self.client.get(version.extension.get_versions_url())
|
||||||
self.assertEqual(response.status_code, 200)
|
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.status_code, 302)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response['Location'],
|
response['Location'],
|
||||||
|
Loading…
Reference in New Issue
Block a user
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.