Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
continue-on-error: true

- name: Pull docker images
run: docker compose -f docker-compose.test.yml pull db
run: docker compose -f docker-compose.test.yml pull db redis

- name: Build test_runner image
uses: docker/build-push-action@v5
Expand Down
12 changes: 11 additions & 1 deletion bookmarks/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
from bookmarks.forms import BookmarkCategoryForm, BookmarkForm
from bookmarks.models import Bookmark, BookmarkCategory
from sounds.models import Sound
from utils.download_limit import (
DownloadType,
count_download_and_set_sentinel,
download_limit_reached_response,
new_download_blocked,
user_download_limit_reached,
)
from utils.downloads import download_sounds
from utils.pagination import paginate
from utils.username import get_parameter_user_or_404, raise_404_if_user_is_deleted, redirect_if_old_username
Expand Down Expand Up @@ -59,6 +66,7 @@ def bookmarks(request, category_id=None):
page_sounds = Sound.objects.ordered_ids([bookmark.sound_id for bookmark in paginator["page"].object_list])
tvars.update(paginator)
tvars["page_bookmarks_and_sound_objects"] = zip(paginator["page"].object_list, page_sounds)
tvars["download_limit_reached"] = user_download_limit_reached(request)
return render(request, "bookmarks/bookmarks.html", tvars)


Expand Down Expand Up @@ -108,9 +116,11 @@ def download_bookmark_category(request, category_id):
sounds_list = Sound.objects.filter(
id__in=bookmarked_sounds, processing_state="OK", moderation_state="OK"
).select_related("user", "license")
if new_download_blocked(request, DownloadType.BOOKMARK_CATEGORY, category.id):
return download_limit_reached_response(request)
count_download_and_set_sentinel(request, DownloadType.BOOKMARK_CATEGORY, category.id)
licenses_url = reverse("category-licenses", args=[category_id])
licenses_content = category.get_attribution(sound_qs=sounds_list)
# NOTE: unlike pack downloads, here we are not doing any cache check to avoid consecutive downloads
return download_sounds(licenses_url, licenses_content, sounds_list, category.download_filename)


Expand Down
4 changes: 4 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ volumes:
solr9data:

services:
redis:
image: redis:8.4.2

db:
image: postgres:18.3
env_file:
Expand Down Expand Up @@ -38,6 +41,7 @@ services:
- FS_USER_ID
depends_on:
- db
- redis

test_runner_search:
image: freesound-test:ci
Expand Down
9 changes: 9 additions & 0 deletions freesound/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
CLUSTERING_CACHE_REDIS_STORE_ID = 1
AUDIO_FEATURES_REDIS_STORE_ID = 2
CELERY_BROKER_REDIS_STORE_ID = 3
ABUSE_REDIS_STORE_ID = 4
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
Expand All @@ -138,6 +139,10 @@
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{CLUSTERING_CACHE_REDIS_STORE_ID}",
},
"abuse": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{ABUSE_REDIS_STORE_ID}",
},
}

CACHE_MIDDLEWARE_SECONDS = 300
Expand Down Expand Up @@ -334,6 +339,10 @@
USERFLAG_THRESHOLD_FOR_NOTIFICATION = 3
USERFLAG_THRESHOLD_FOR_AUTOMATIC_BLOCKING = 6

# Daily download limit
MAX_DOWNLOADS_PER_DAY = 200
DOWNLOAD_LIMIT_MESSAGE = "You have reached the download limit. Come back tomorrow to download more sounds."

# Supported audio formats
# When adding support for a new audio format you have to change the variables below and check:
# - mime types in accounts.forms.validate_file_extension
Expand Down
4 changes: 4 additions & 0 deletions freesound/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"clustering": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
},
"abuse": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/15",
},
}

# django_ratelimit off in tests.
Expand Down
1 change: 1 addition & 0 deletions freesound/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
path("embed/geotags_box/iframe/", geotags.views.embed_iframe, name="embed-geotags"),
path("oembed/", sounds.views.oembed, name="oembed-sound"),
path("after-download-modal/", sounds.views.after_download_modal, name="after-download-modal"),
path("download-limit-modal/", sounds.views.download_limit_modal, name="download-limit-modal"),
path("browse/", sounds.views.sounds, name="sounds"),
path("browse/tags/", tags.views.tags, name="tags"),
path("browse/tags/<multitags:multiple_tags>/", tags.views.multiple_tags_lookup, name="tags"),
Expand Down
25 changes: 17 additions & 8 deletions fscollections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.paginator import Paginator
from django.db import transaction
from django.db.models import Case, IntegerField, Q, Value, When
from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, render
Expand All @@ -42,6 +43,13 @@
from fscollections.models import Collection, CollectionDownload, CollectionDownloadSound, CollectionSound
from sounds.models import Sound
from sounds.views import add_sounds_modal_helper
from utils.download_limit import (
DownloadType,
count_download_and_set_sentinel,
download_limit_reached_response,
new_download_blocked,
user_download_limit_reached,
)
from utils.downloads import download_sounds
from utils.pagination import build_paginator_template_context, paginate, read_page

Expand Down Expand Up @@ -121,6 +129,7 @@ def collection(request, collection):
"current_search": search,
}
tvars.update(pagination)
tvars["download_limit_reached"] = user_download_limit_reached(request)
return render(request, "collections/collection.html", tvars)


Expand All @@ -129,7 +138,10 @@ def collections_for_user(request):
user = request.user
user_collections = Collection.objects.filter(user=user).order_by("-modified")
maintainer_collections = Collection.objects.filter(maintainers__id=user.id).order_by("-modified")
tvars = {"user_collections": user_collections, "maintainer_collections": maintainer_collections}
tvars = {
"user_collections": user_collections,
"maintainer_collections": maintainer_collections,
}
# one URL needed to display all collections and one URL to display ONE collection at a time
# the collections_for_user can be reused to display ONE collection so give it a thought on full collections display
return render(request, "collections/your_collections.html", tvars)
Expand Down Expand Up @@ -368,17 +380,14 @@ def edit_collection(request, collection):


@login_required
@transaction.atomic()
@resolve_collection_from_url
def download_collection(request, collection):
sounds_list = Sound.objects.bulk_sounds_for_collection(collection.id)

if "range" not in request.headers:
"""
Download managers and some browsers use the range header to download files in multiple parts. We have observed
that all clients first make a GET with no range header (to get the file length) and then make multiple other
requests. We ignore all requests that have range header because we assume that a first query has already been
made. Unlike in pack downloads, here we do not guard against multiple consecutive downloads.
"""
if new_download_blocked(request, DownloadType.COLLECTION, collection.id):
return download_limit_reached_response(request)
if count_download_and_set_sentinel(request, DownloadType.COLLECTION, collection.id):
cd = CollectionDownload.objects.create(user=request.user, collection=collection)
cds = []
for sound in sounds_list:
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ markers = [
"search_engine: solr tests",
"forum: search engine forum tests",
"sounds: search engine search tests",
"redis: tests that require a real Redis server",
]

[tool.ty.src]
Expand Down
Loading
Loading