Use the setting above to express a '
- "preference regarding the usage of your sounds for training generative Artificial Intelligence models. "
- 'This preference applies to all your uploaded sounds. Please, read the '
- "Usage of my sounds for "
- "training generative AI models help section to learn more about the details and implications of the available options.
"
- ),
- )
def __init__(self, request, *args, **kwargs):
self.request = request
initial_kwargs = {
"username": request.user.username,
}
- ai_preference = request.user.profile.get_ai_preference()
- if ai_preference:
- initial_kwargs["ai_sound_usage_preference"] = ai_preference
kwargs.update(initial=initial_kwargs)
kwargs.update(dict(label_suffix=""))
super().__init__(*args, **kwargs)
@@ -507,6 +490,19 @@ def get_img_check_fields(self):
return [self["about"], self["signature"], self["sound_signature"]]
+class AIPreferenceForm(forms.Form):
+ ai_sound_usage_preference = forms.ChoiceField(
+ label="",
+ choices=AIPreference.AI_PREFERENCE_CHOICES,
+ widget=forms.RadioSelect(),
+ )
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields["ai_sound_usage_preference"].widget.attrs["class"] = "bw-radio"
+ self.ai_options_and_texts = AIPreference.AI_PREFERENCE_CHOICES_AND_EXPLANATION
+
+
class EmailResetForm(forms.Form):
email = forms.EmailField(label="New email address", max_length=254)
password = forms.CharField(label="Your password", widget=forms.PasswordInput)
diff --git a/accounts/models.py b/accounts/models.py
index 5971487e4e..e61a36403a 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -722,17 +722,34 @@ class GdprAcceptance(models.Model):
class AIPreference(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="ai_preference")
date_updated = models.DateTimeField(auto_now=True)
- AI_PREFERENCE_CHOICES = (
- (
- "freesound-cc-recommendation",
- "My sounds are used following Freesound's recommendations for interpreting Creative Commons licenses in a generative AI training context",
- ),
- ("open-models", "My sounds are used to train open source models that are freely available to the public"),
- (
- "open-noncommercial-models",
- "My sounds are used to train open source models that are freely available to the public and that do not allow a commercial use",
- ),
- )
+
+ AI_PREFERENCE_CHOICES_AND_EXPLANATION = [
+ {
+ "code": "no-additional-restrictions",
+ "label": "No additional restrictions",
+ "explanation": """this is the default option, and it means that sounds can be used for training Gen AI models as long as the recommendations described in the 2024 blog post are followed. Here is a summary of the recommendations:
+ CC0 sounds can be used without restriction.
+ CC-BY sounds can be used as long as the training set of the AI model is disclosed.
+ CC-BY-NC sounds can be used as long as the training set of the AI model is disclosed and the model is not trained in a commercial setting or used for commercial purposes.""",
+ },
+ {
+ "code": "open-models",
+ "label": "Open Models",
+ "explanation": "if this option is selected, the uploader allows their sounds to be used for training Gen AI models as long as these models are made open source* and freely available to the public. Note that selecting this option does not supersede the non-commercial clause of CC-BY-NC sounds, therefore model developers still need to exclude CC-BY-NC sounds if training a model in a commercial setting or if the model is to be used for commercial purposes.",
+ },
+ {
+ "code": "open-noncommercial-models",
+ "label": "Open Non-Commercial Models",
+ "explanation": "this option is similar to the previous one, but with an additional restriction of not allowing the use of sounds for models which are trained in a commercial setting or that are used for commercial purposes. Note that by using this option, sound uploaders can restrict the use of their sounds for training commercial Gen AI models even if the individual sound license does not have the non-commercial clause (i.e. for CC0 and CC-BY sounds).",
+ },
+ {
+ "code": "no-gen-ai",
+ "label": "No generative AI",
+ "explanation": "My sounds are not used to train any AI models",
+ },
+ ]
+
+ AI_PREFERENCE_CHOICES = [(item["code"], item["label"]) for item in AI_PREFERENCE_CHOICES_AND_EXPLANATION]
DEFAULT_AI_PREFERENCE = "freesound-cc-recommendation"
preference = models.CharField(choices=AI_PREFERENCE_CHOICES, default=DEFAULT_AI_PREFERENCE)
diff --git a/accounts/urls.py b/accounts/urls.py
index b7eb8a63ea..7ee98076d6 100644
--- a/accounts/urls.py
+++ b/accounts/urls.py
@@ -86,6 +86,7 @@
path("", accounts.home, name="accounts-home"),
path("edit/", accounts.edit, name="accounts-edit"),
path("email-settings/", accounts.edit_email_settings, name="accounts-email-settings"),
+ path("ai-preferences/", accounts.edit_ai_preferences, name="accounts-ai-preferences"),
path("delete/", accounts.delete, name="accounts-delete"),
path("attribution/", accounts.attribution, name="accounts-attribution"),
path("download-attribution/", accounts.download_attribution, name="accounts-download-attribution"),
diff --git a/accounts/views.py b/accounts/views.py
index 0da4eb205f..d3c64b88f5 100644
--- a/accounts/views.py
+++ b/accounts/views.py
@@ -67,6 +67,7 @@
import utils.sound_upload
from accounts.forms import (
+ AIPreferenceForm,
AvatarForm,
BulkDescribeForm,
DeleteUserForm,
@@ -461,6 +462,37 @@ def edit_email_settings(request):
return render(request, "accounts/edit_email_settings.html", tvars)
+@login_required
+@transaction.atomic()
+def edit_ai_preferences(request):
+ profile = request.user.profile
+
+ form = AIPreferenceForm(request.POST or None)
+ # Save a couple of variables that we will need later to check if they have changed and we need
+ # to mark user sounds as index dirty or show some messages to the user
+ old_ai_preference = str(profile.get_ai_preference(default_if_not_set=False))
+
+ if form.is_valid():
+ # Update AI sound usage preference, only if field present in the form
+ if "ai_sound_usage_preference" in form.cleaned_data:
+ profile.set_ai_preference(form.cleaned_data["ai_sound_usage_preference"])
+
+ if old_ai_preference != profile.get_ai_preference(default_if_not_set=False):
+ Sound.objects.filter(user=request.user).update(is_index_dirty=True)
+ invalidate_user_template_caches(request.user.id)
+
+ msg_txt = "Your AI preference options have been updated correctly."
+ messages.add_message(request, messages.INFO, msg_txt)
+ return HttpResponseRedirect(reverse("accounts-ai-preferences"))
+ else:
+ form = AIPreferenceForm(
+ initial={"ai_sound_usage_preference": profile.get_ai_preference(default_if_not_set=False)}
+ )
+
+ tvars = {"form": form, "activePage": "ai_preferences"}
+ return render(request, "accounts/edit_ai_preferences.html", tvars)
+
+
@login_required
@transaction.atomic()
def edit(request):
@@ -481,14 +513,9 @@ def is_selected(prefix):
profile_form = ProfileForm(request, request.POST, instance=profile, prefix="profile")
# Save a couple of variables that we will need later to check if they have changed and we need
# to mark user sounds as index dirty or show some messages to the user
- old_ai_preference = str(profile.get_ai_preference(default_if_not_set=False))
old_username = request.user.username
old_sound_signature = profile.sound_signature
if profile_form.is_valid():
- # Update AI sound usage preference, only if field present in the form
- if "ai_sound_usage_preference" in profile_form.cleaned_data:
- profile.set_ai_preference(profile_form.cleaned_data["ai_sound_usage_preference"])
-
# Update username, this will create an entry in OldUsername
request.user.username = profile_form.cleaned_data["username"]
try:
@@ -501,11 +528,8 @@ def is_selected(prefix):
request.user.refresh_from_db(fields=["username"])
else:
profile.save()
-
# profile.refresh_from_db() # Refresh profile to get updated ai preference when calling get_ai_preference
- if old_username != request.user.username or old_ai_preference != profile.get_ai_preference(
- default_if_not_set=False
- ):
+ if old_username != request.user.username:
Sound.objects.filter(user=request.user).update(is_index_dirty=True)
invalidate_user_template_caches(request.user.id)
@@ -659,7 +683,6 @@ def process_filter_and_sort_options(request, sort_options, tab):
"sounds_processing_count": sounds_processing_count,
"sounds_pending_description_count": sounds_pending_description_count,
"packs_count": packs_count,
- "user_has_ai_preference_unset": request.user.profile.get_ai_preference(default_if_not_set=False) is None,
}
# Then do dedicated processing for each tab
diff --git a/templates/accounts/account_settings_base.html b/templates/accounts/account_settings_base.html
index 26b21293d1..4398fcb8c6 100644
--- a/templates/accounts/account_settings_base.html
+++ b/templates/accounts/account_settings_base.html
@@ -21,6 +21,9 @@
- {% bw_icon 'notification' %} You haven't set your preference regarding the usage of your sounds for training generative AI models.
- While no preference is set, the default option will be used.
- To disable this warning, please set your preference in your account settings page and save profile settings.
-
- {% endif %}
{% if tab == 'published' or tab == 'pending_moderation' %}
@@ -44,13 +44,13 @@
{{ ai_option.label }}
- When referring to open source models, we refer to the definition of Open Source AI by the Open Source Initiative (OSI). In short, that means that for a model to be considered open source, it needs to be released under terms that grant the freedom to use, study, modify and share the model, including their code, model weights and training documentation.
+ Note that when using the term open source models, we refer to the definition of Open Source AI by the Open Source Initiative (OSI). In short, that means that for a model to be considered open source, it needs to be released under terms that grant the freedom to use, study, modify and share the model, including their code, model weights and training documentation.
- For more information about this topic, including how your preferences are applied, please read the
+ For more information about this topic, including how these Gen AI preferences are applied, please read the
Usage of my sounds for training generative AI models help section.
-
+
From 3b1078b55cdc88342b78c9974cc8e6efcf4bdf48 Mon Sep 17 00:00:00 2001
From: ffont
Date: Wed, 8 Jul 2026 10:42:29 +0200
Subject: [PATCH 04/10] Rename ai_preference to gen_ai_preference, update
texts, update tests
---
_docs/api/source/resources.rst | 2 +-
accounts/models.py | 20 ++++++-------
accounts/tests/test_profile.py | 32 +++++++--------------
accounts/views.py | 8 +++---
apiv2/serializers.py | 18 ++++++------
templates/accounts/edit_ai_preferences.html | 17 +++++------
utils/search/backends/solr555pysolr.py | 2 +-
utils/search/schema/freesound.json | 2 +-
8 files changed, 43 insertions(+), 58 deletions(-)
diff --git a/_docs/api/source/resources.rst b/_docs/api/source/resources.rst
index 9b73936307..29c42722b8 100644
--- a/_docs/api/source/resources.rst
+++ b/_docs/api/source/resources.rst
@@ -299,7 +299,7 @@ geotag string yes* Latitude and longitude o
is_geotagged boolean yes Whether the sound has geotag information.
created string yes The date when the sound was uploaded (e.g. "2014-04-16T20:07:11.145").
license string yes The Creative Commons license under which the sound is available to you ("Attribution", "Attribution NonCommercial", "Creative Commons 0").
-ai_preference string yes The user preference regarding the use of the sound for training generative AI models ("freesound-cc-recommendation", "open-models", "open-noncommercial-models"). Please check `our help section about generative AI training preferences `_ for more information about the meaning of these values.
+gen_ai_preference string yes The user preference regarding the use of the sound for training generative AI models ("no-additional-restrictions", "open-models", "open-noncommercial-models", "no-gen-ai"). Please check `our help section about generative AI training preferences `_ for more information about the meaning of these values.
type string yes The original type of the sound (wav, aif, aiff, ogg, mp3, m4a, or flac).
channels integer yes The number of sound channels (mostly 1 or 2).
filesize integer yes The size of the file in bytes.
diff --git a/accounts/models.py b/accounts/models.py
index 446062fd8d..1b7709ef18 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -695,7 +695,7 @@ def get_stats_for_profile_page(self):
stats_from_db.update(stats_from_cache)
return stats_from_db
- def get_ai_preference(self, default_if_not_set=True):
+ def get_gen_ai_preference(self, default_if_not_set=True):
try:
return self.user.ai_preference.preference
except AIPreference.DoesNotExist:
@@ -704,7 +704,7 @@ def get_ai_preference(self, default_if_not_set=True):
return AIPreference.DEFAULT_AI_PREFERENCE
return None
- def set_ai_preference(self, preference_value):
+ def set_gen_ai_preference(self, preference_value):
AIPreference.objects.update_or_create(user=self.user, defaults={"preference": preference_value})
class Meta:
@@ -727,27 +727,25 @@ class AIPreference(models.Model):
{
"code": "no-additional-restrictions",
"label": "No additional restrictions",
- "explanation": """Sounds can be used for training Gen AI models without any additional restrictions to those already set by the corresponding Creative Commons license. Here is a summary of the Creative Commons terms applied to the use case of training AI models:
-
CC0 sounds can be used without restriction.
-
CC-BY sounds can be used as long as the training set of the AI model is disclosed.
-
CC-BY-NC sounds can be used as long as the training set of the AI model is disclosed and the model is not trained in a commercial setting or used for commercial purposes.
""",
+ "explanation": """No additional statement, your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms.
+ Read here for more details.""",
},
{
"code": "open-models",
"label": "Open Models only",
- "explanation": "Sounds can be used for training Gen AI models as long as the Creative Commons license terms summarised above are met, and as long as the trained models are made open source and freely available to the public. "
- "Note that selecting this option does not supersede the non-commercial clause of CC-BY-NC sounds, therefore model developers still need to exclude CC-BY-NC sounds when training a model in a commercial setting or if the model is to be used for commercial purposes.",
+ "explanation": """Your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms and as long as the trained models are made open source and freely available to the public.
+ Read here for more details.""",
},
{
"code": "open-noncommercial-models",
"label": "Open Non-Commercial Models only",
- "explanation": "Sounds can be used for training Gen AI models as long as the Creative Commons license terms summarised above are met, and as long as the trained models are made open source, freely available to the public, and not trained in a commercial setting nor used for commercial purposes. "
- "Note that by using this option, sound uploaders can restrict the use of their sounds for training commercial Gen AI models even if the individual sound licenses do not have the non-commercial clause (i.e. for CC0 and CC-BY sounds).",
+ "explanation": """Your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms and as long as the trained models are made open source, freely available to the public, and not trained in a commercial setting nor used for commercial purposes.
+ Read here for more details.""",
},
{
"code": "no-gen-ai",
"label": "No generative AI",
- "explanation": "By selecting this option, sound uploaders expresses their preference that their uploaded sounds should not be used for training any type of generative AI models.",
+ "explanation": """Your sounds can not be used for the purpose of training Gen AI models. Read here for more details.""",
},
]
diff --git a/accounts/tests/test_profile.py b/accounts/tests/test_profile.py
index 98433e0fc7..2d4dd8a212 100644
--- a/accounts/tests/test_profile.py
+++ b/accounts/tests/test_profile.py
@@ -216,46 +216,36 @@ def test_edit_user_profile_ai_preference(self):
user.profile.save()
# Check that user does not start with any preference set
- self.assertEqual(user.profile.get_ai_preference(default_if_not_set=False), None)
+ self.assertEqual(user.profile.get_gen_ai_preference(default_if_not_set=False), None)
- # Check that "get_ai_preference" returns the default one when no preference is set and default_if_not_set is True (default)
- self.assertEqual(user.profile.get_ai_preference(), AIPreference.DEFAULT_AI_PREFERENCE)
+ # Check that "get_gen_ai_preference" returns the default one when no preference is set and default_if_not_set is True (default)
+ self.assertEqual(user.profile.get_gen_ai_preference(), AIPreference.DEFAULT_AI_PREFERENCE)
# Now user edits preference in profile page
self.client.force_login(user)
new_preference = "open-models"
- self.client.post(
- "/home/edit/",
+ resp = self.client.post(
+ "/home/ai-preferences/",
{
- "profile-home_page": "http://www.example.com/",
- "profile-username": "testuser",
- "profile-about": "About test text",
- "profile-signature": "Signature test text",
- "profile-ui_theme_preference": "d",
- "profile-ai_sound_usage_preference": new_preference,
+ "ai_sound_usage_preference": new_preference,
},
)
# Check that new preference is set and that sounds were marked as dirty
user = User.objects.select_related("profile").get(username="testuser")
- self.assertEqual(user.profile.get_ai_preference(), new_preference)
+ self.assertEqual(user.profile.get_gen_ai_preference(), new_preference)
self.assertEqual(Sound.objects.filter(user=user, is_index_dirty=True).count(), len(sounds))
# Now that there's an AI preference object already existing, try to change preference again and check that it works as expected
- even_newer_preference = "freesound-cc-recommendation"
+ even_newer_preference = "no-additional-restrictions"
self.client.post(
- "/home/edit/",
+ "/home/ai-preferences/",
{
- "profile-home_page": "http://www.example.com/",
- "profile-username": "testuser",
- "profile-about": "About test text",
- "profile-signature": "Signature test text",
- "profile-ui_theme_preference": "d",
- "profile-ai_sound_usage_preference": even_newer_preference,
+ "ai_sound_usage_preference": even_newer_preference,
},
)
user = User.objects.select_related("profile").get(username="testuser")
- self.assertEqual(user.profile.get_ai_preference(), even_newer_preference)
+ self.assertEqual(user.profile.get_gen_ai_preference(), even_newer_preference)
def test_edit_user_email_settings(self):
EmailPreferenceType.objects.create(name="email", display_name="email")
diff --git a/accounts/views.py b/accounts/views.py
index cb7aceb1c9..c5a8d4ae70 100644
--- a/accounts/views.py
+++ b/accounts/views.py
@@ -476,8 +476,8 @@ def edit_ai_preferences(request):
# save a "default preference" object so that we know if users have explicitly set it through the
# preferences panel. For this reason, here we don't want to get the default if the preference is not
# set, so that we can compare with the form value and properly create the object if needed.
- old_ai_preference = str(profile.get_ai_preference(default_if_not_set=False))
- profile.set_ai_preference(form.cleaned_data["ai_sound_usage_preference"])
+ old_ai_preference = str(profile.get_gen_ai_preference(default_if_not_set=False))
+ profile.set_gen_ai_preference(form.cleaned_data["ai_sound_usage_preference"])
# If preference has changed, mark all sounds as index dirty
if old_ai_preference != form.cleaned_data["ai_sound_usage_preference"]:
@@ -487,7 +487,7 @@ def edit_ai_preferences(request):
messages.add_message(request, messages.INFO, msg_txt)
return HttpResponseRedirect(reverse("accounts-ai-preferences"))
else:
- form = AIPreferenceForm(initial={"ai_sound_usage_preference": profile.get_ai_preference()})
+ form = AIPreferenceForm(initial={"ai_sound_usage_preference": profile.get_gen_ai_preference()})
tvars = {"form": form, "activePage": "ai_preferences"}
return render(request, "accounts/edit_ai_preferences.html", tvars)
@@ -528,7 +528,7 @@ def is_selected(prefix):
request.user.refresh_from_db(fields=["username"])
else:
profile.save()
- # profile.refresh_from_db() # Refresh profile to get updated ai preference when calling get_ai_preference
+ # profile.refresh_from_db() # Refresh profile to get updated ai preference when calling get_gen_ai_preference
if old_username != request.user.username:
Sound.objects.filter(user=request.user).update(is_index_dirty=True)
invalidate_user_template_caches(request.user.id)
diff --git a/apiv2/serializers.py b/apiv2/serializers.py
index c06786ccf3..f0227876df 100644
--- a/apiv2/serializers.py
+++ b/apiv2/serializers.py
@@ -42,7 +42,7 @@
+ "geotag,is_geotagged,created,license,type,channels,filesize,bitrate,"
+ "bitdepth,duration,samplerate,username,pack,pack_name,download,bookmark,previews,images,"
+ "num_downloads,avg_rating,num_ratings,rate,comments,num_comments,comment,similar_sounds,"
- + "is_explicit,is_remix,was_remixed,md5,ai_preference"
+ + "is_explicit,is_remix,was_remixed,md5,gen_ai_preference"
)
DEFAULT_FIELDS_IN_PACK_DETAIL = None # Separated by commas (None = all)
@@ -145,7 +145,7 @@ class Meta:
"is_geotagged",
"created",
"license",
- "ai_preference",
+ "gen_ai_preference",
"type",
"channels",
"filesize",
@@ -222,10 +222,10 @@ def get_tags(self, obj):
def get_license(self, obj):
return obj.license.deed_url
- ai_preference = serializers.SerializerMethodField()
+ gen_ai_preference = serializers.SerializerMethodField()
- def get_ai_preference(self, obj):
- return obj.user.profile.get_ai_preference()
+ def get_gen_ai_preference(self, obj):
+ return obj.user.profile.get_gen_ai_preference()
category = serializers.SerializerMethodField()
@@ -445,7 +445,7 @@ class Meta:
"packs",
"num_posts",
"num_comments",
- "ai_preference",
+ "gen_ai_preference",
)
url = serializers.SerializerMethodField()
@@ -522,10 +522,10 @@ def get_num_posts(self, obj):
def get_num_comments(self, obj):
return obj.comment_set.all().count()
- ai_preference = serializers.SerializerMethodField()
+ gen_ai_preference = serializers.SerializerMethodField()
- def get_ai_preference(self, obj):
- return obj.profile.get_ai_preference()
+ def get_gen_ai_preference(self, obj):
+ return obj.profile.get_gen_ai_preference()
##################
diff --git a/templates/accounts/edit_ai_preferences.html b/templates/accounts/edit_ai_preferences.html
index 63524e593c..f0eeadc75a 100644
--- a/templates/accounts/edit_ai_preferences.html
+++ b/templates/accounts/edit_ai_preferences.html
@@ -17,9 +17,9 @@
Generative AI preferences
This section allows you to set your preferences regarding the use of your uploaded sounds for the purpose of training generative Artificial Intelligence (Gen AI) models.
- Beyond the usage terms already defined by the specific Creative Commons license associated with each of your sounds, you can define
- additional usage preferences for the specific case of training Gen AI models.
- These additional preferences are applied to all your uploaded sounds, and are independent of the individual sound licenses. Please, select one of the options below:
+ Sounds may be used for Gen AI training in accordance with your chosen Creative Commons license terms.
+ You may choose to add a statement to express additional usage terms for the specific case of training Gen AI models, however there is no guarantee that these will have any legal effect.
+ Please, select one of the options below:
{% if form.ai_sound_usage_preference.errors %}
@@ -43,13 +43,10 @@
{{ ai_option.label }}
{% endfor %}
-
- Note that when using the term open source models, we refer to the definition of Open Source AI by the Open Source Initiative (OSI). In short, that means that for a model to be considered open source, it needs to be released under terms that grant the freedom to use, study, modify and share the model, including their code, model weights and training documentation.
-
diff --git a/utils/search/backends/solr555pysolr.py b/utils/search/backends/solr555pysolr.py
index f2c263c918..26e805e207 100644
--- a/utils/search/backends/solr555pysolr.py
+++ b/utils/search/backends/solr555pysolr.py
@@ -331,7 +331,7 @@ def convert_sound_to_search_engine_document(self, sound):
document["preview_path"] = locations["preview"]["LQ"]["mp3"]["path"]
# Index uploader AI preference
- document["ai_preference"] = sound.user.profile.get_ai_preference()
+ document["gen_ai_preference"] = sound.user.profile.get_gen_ai_preference()
# Index consolidated audio descriptors
descriptors_to_index = {}
diff --git a/utils/search/schema/freesound.json b/utils/search/schema/freesound.json
index c57f91978b..6d298d9e1c 100644
--- a/utils/search/schema/freesound.json
+++ b/utils/search/schema/freesound.json
@@ -246,7 +246,7 @@
"stored": false
},
{
- "name": "ai_preference",
+ "name": "gen_ai_preference",
"type": "string",
"indexed": true,
"stored": false
From e329e89a54eda3f958bcba477de12ec0d314ffa6 Mon Sep 17 00:00:00 2001
From: ffont
Date: Wed, 8 Jul 2026 10:58:43 +0200
Subject: [PATCH 05/10] Remove gen_ai_preference from user serializer as it is
not relevant there
Note that sound might have preference overrides in some situations (now or in the future), so therefore it is better not to add the preference at the user level in the API
---
apiv2/serializers.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/apiv2/serializers.py b/apiv2/serializers.py
index f0227876df..41cab24e8e 100644
--- a/apiv2/serializers.py
+++ b/apiv2/serializers.py
@@ -445,7 +445,6 @@ class Meta:
"packs",
"num_posts",
"num_comments",
- "gen_ai_preference",
)
url = serializers.SerializerMethodField()
@@ -522,11 +521,6 @@ def get_num_posts(self, obj):
def get_num_comments(self, obj):
return obj.comment_set.all().count()
- gen_ai_preference = serializers.SerializerMethodField()
-
- def get_gen_ai_preference(self, obj):
- return obj.profile.get_gen_ai_preference()
-
##################
# PACK SERIALIZERS
From fa6cbbe499794c9723a4900f13685d02613f2257 Mon Sep 17 00:00:00 2001
From: ffont
Date: Wed, 8 Jul 2026 12:34:36 +0200
Subject: [PATCH 06/10] Add AI preference for voice recordings, other fixes and
improvements
---
accounts/forms.py | 7 +++
...43_aipreference_opt_out_speech_and_more.py | 23 ++++++++++
accounts/models.py | 44 ++++++++++++++-----
accounts/tests/test_profile.py | 9 +++-
accounts/views.py | 18 ++++++--
apiv2/serializers.py | 2 +-
freesound/settings.py | 10 +++++
sounds/models.py | 3 ++
templates/accounts/edit_ai_preferences.html | 13 +++++-
utils/search/backends/solr555pysolr.py | 6 +--
10 files changed, 114 insertions(+), 21 deletions(-)
create mode 100644 accounts/migrations/0043_aipreference_opt_out_speech_and_more.py
diff --git a/accounts/forms.py b/accounts/forms.py
index c70b5d8511..5ce7503863 100644
--- a/accounts/forms.py
+++ b/accounts/forms.py
@@ -496,11 +496,18 @@ class AIPreferenceForm(forms.Form):
choices=AIPreference.AI_PREFERENCE_CHOICES,
widget=forms.RadioSelect(),
)
+ opt_out_speech = forms.BooleanField(
+ label=mark_safe(
+ "Do not allow sounds classified under the \"Speech > Solo speech\" BST category to be used for the purpose of training Gen AI models."
+ ),
+ required=False,
+ )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["ai_sound_usage_preference"].widget.attrs["class"] = "bw-radio"
self.ai_options_and_texts = AIPreference.AI_PREFERENCE_CHOICES_AND_EXPLANATION
+ self.fields["opt_out_speech"].widget.attrs["class"] = "bw-checkbox"
class EmailResetForm(forms.Form):
diff --git a/accounts/migrations/0043_aipreference_opt_out_speech_and_more.py b/accounts/migrations/0043_aipreference_opt_out_speech_and_more.py
new file mode 100644
index 0000000000..43f3699629
--- /dev/null
+++ b/accounts/migrations/0043_aipreference_opt_out_speech_and_more.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.27 on 2026-07-08 09:52
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('accounts', '0042_aipreference'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='aipreference',
+ name='opt_out_speech',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterField(
+ model_name='aipreference',
+ name='preference',
+ field=models.CharField(choices=[('no-additional-restrictions', 'No additional restrictions'), ('open-models', 'Open Models only'), ('open-noncommercial-models', 'Open Non-Commercial Models only'), ('no-gen-ai', 'No generative AI')], default='no-additional-restrictions'),
+ ),
+ ]
diff --git a/accounts/models.py b/accounts/models.py
index 1b7709ef18..36643c0a59 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -695,17 +695,39 @@ def get_stats_for_profile_page(self):
stats_from_db.update(stats_from_cache)
return stats_from_db
- def get_gen_ai_preference(self, default_if_not_set=True):
+ def get_gen_ai_preference(self, default_if_not_set=True, category_code=None):
+ """
+ Returns the user's generative AI preference.
+ If no preference is set, returns the default preference if default_if_not_set is True, otherwise None.
+ If "category_code" argument is specified, this method will consider if there should be any preference overrides
+ because of that.
+ """
try:
- return self.user.ai_preference.preference
+ preference_object = self.user.ai_preference
+ if (
+ category_code is not None
+ and preference_object.opt_out_speech is True
+ and category_code in settings.AI_OPT_OUT_SPEECH_TAXONOMY_CODES
+ ):
+ return settings.AI_PREF_NO_GEN_AI
+ return preference_object.preference
except AIPreference.DoesNotExist:
# If no preference is set, return the default one
if default_if_not_set:
- return AIPreference.DEFAULT_AI_PREFERENCE
+ return settings.DEFAULT_AI_PREFERENCE
return None
- def set_gen_ai_preference(self, preference_value):
- AIPreference.objects.update_or_create(user=self.user, defaults={"preference": preference_value})
+ def set_gen_ai_preference(self, preference_value, opt_out_speech):
+ preference, _ = AIPreference.objects.update_or_create(user=self.user, defaults={"preference": preference_value})
+ if opt_out_speech != preference.opt_out_speech:
+ preference.opt_out_speech = opt_out_speech
+ preference.save()
+
+ def get_gen_ai_preference_opt_out_speech(self):
+ try:
+ return self.user.ai_preference.opt_out_speech
+ except AIPreference.DoesNotExist:
+ return settings.DEFAULT_OPT_OUT_SPEECH
class Meta:
ordering = ("-user__date_joined",)
@@ -725,33 +747,33 @@ class AIPreference(models.Model):
AI_PREFERENCE_CHOICES_AND_EXPLANATION = [
{
- "code": "no-additional-restrictions",
+ "code": settings.AI_PREF_NO_ADDITIONAL_RESTRICTIONS,
"label": "No additional restrictions",
"explanation": """No additional statement, your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms.
Read here for more details.""",
},
{
- "code": "open-models",
+ "code": settings.AI_PREF_OPEN_MODELS,
"label": "Open Models only",
"explanation": """Your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms and as long as the trained models are made open source and freely available to the public.
Read here for more details.""",
},
{
- "code": "open-noncommercial-models",
+ "code": settings.AI_PREF_OPEN_NONCOMMERCIAL_MODELS,
"label": "Open Non-Commercial Models only",
"explanation": """Your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms and as long as the trained models are made open source, freely available to the public, and not trained in a commercial setting nor used for commercial purposes.
Read here for more details.""",
},
{
- "code": "no-gen-ai",
+ "code": settings.AI_PREF_NO_GEN_AI,
"label": "No generative AI",
"explanation": """Your sounds can not be used for the purpose of training Gen AI models. Read here for more details.""",
},
]
AI_PREFERENCE_CHOICES = [(item["code"], item["label"]) for item in AI_PREFERENCE_CHOICES_AND_EXPLANATION]
- DEFAULT_AI_PREFERENCE = "no-additional-restrictions"
- preference = models.CharField(choices=AI_PREFERENCE_CHOICES, default=DEFAULT_AI_PREFERENCE)
+ preference = models.CharField(choices=AI_PREFERENCE_CHOICES, default=settings.DEFAULT_AI_PREFERENCE)
+ opt_out_speech = models.BooleanField(default=settings.DEFAULT_OPT_OUT_SPEECH)
class UserFlag(models.Model):
diff --git a/accounts/tests/test_profile.py b/accounts/tests/test_profile.py
index 2d4dd8a212..ffa4793418 100644
--- a/accounts/tests/test_profile.py
+++ b/accounts/tests/test_profile.py
@@ -36,7 +36,7 @@
import accounts.models
from accounts.management.commands.process_email_bounces import decode_idna_email, process_message
-from accounts.models import AIPreference, EmailBounce, EmailPreferenceType, UserEmailSetting
+from accounts.models import EmailBounce, EmailPreferenceType, UserEmailSetting
from accounts.views import handle_uploaded_image
from forum.models import Forum, Post, Thread
from geotags.models import GeoTag
@@ -217,9 +217,10 @@ def test_edit_user_profile_ai_preference(self):
# Check that user does not start with any preference set
self.assertEqual(user.profile.get_gen_ai_preference(default_if_not_set=False), None)
+ self.assertEqual(user.profile.get_gen_ai_preference_opt_out_speech(), settings.DEFAULT_OPT_OUT_SPEECH)
# Check that "get_gen_ai_preference" returns the default one when no preference is set and default_if_not_set is True (default)
- self.assertEqual(user.profile.get_gen_ai_preference(), AIPreference.DEFAULT_AI_PREFERENCE)
+ self.assertEqual(user.profile.get_gen_ai_preference(), settings.DEFAULT_AI_PREFERENCE)
# Now user edits preference in profile page
self.client.force_login(user)
@@ -228,12 +229,14 @@ def test_edit_user_profile_ai_preference(self):
"/home/ai-preferences/",
{
"ai_sound_usage_preference": new_preference,
+ "opt_out_speech": True,
},
)
# Check that new preference is set and that sounds were marked as dirty
user = User.objects.select_related("profile").get(username="testuser")
self.assertEqual(user.profile.get_gen_ai_preference(), new_preference)
+ self.assertEqual(user.profile.get_gen_ai_preference_opt_out_speech(), True)
self.assertEqual(Sound.objects.filter(user=user, is_index_dirty=True).count(), len(sounds))
# Now that there's an AI preference object already existing, try to change preference again and check that it works as expected
@@ -242,10 +245,12 @@ def test_edit_user_profile_ai_preference(self):
"/home/ai-preferences/",
{
"ai_sound_usage_preference": even_newer_preference,
+ "opt_out_speech": False,
},
)
user = User.objects.select_related("profile").get(username="testuser")
self.assertEqual(user.profile.get_gen_ai_preference(), even_newer_preference)
+ self.assertEqual(user.profile.get_gen_ai_preference_opt_out_speech(), False)
def test_edit_user_email_settings(self):
EmailPreferenceType.objects.create(name="email", display_name="email")
diff --git a/accounts/views.py b/accounts/views.py
index c5a8d4ae70..be659f009e 100644
--- a/accounts/views.py
+++ b/accounts/views.py
@@ -477,17 +477,29 @@ def edit_ai_preferences(request):
# preferences panel. For this reason, here we don't want to get the default if the preference is not
# set, so that we can compare with the form value and properly create the object if needed.
old_ai_preference = str(profile.get_gen_ai_preference(default_if_not_set=False))
- profile.set_gen_ai_preference(form.cleaned_data["ai_sound_usage_preference"])
+ old_ai_opt_out_speech = profile.get_gen_ai_preference_opt_out_speech()
+
+ profile.set_gen_ai_preference(
+ form.cleaned_data["ai_sound_usage_preference"], form.cleaned_data["opt_out_speech"]
+ )
# If preference has changed, mark all sounds as index dirty
- if old_ai_preference != form.cleaned_data["ai_sound_usage_preference"]:
+ if (
+ old_ai_preference != form.cleaned_data["ai_sound_usage_preference"]
+ or old_ai_opt_out_speech != form.cleaned_data["opt_out_speech"]
+ ):
Sound.objects.filter(user=request.user).update(is_index_dirty=True)
msg_txt = "Your Gen AI preference options have been updated correctly."
messages.add_message(request, messages.INFO, msg_txt)
return HttpResponseRedirect(reverse("accounts-ai-preferences"))
else:
- form = AIPreferenceForm(initial={"ai_sound_usage_preference": profile.get_gen_ai_preference()})
+ form = AIPreferenceForm(
+ initial={
+ "ai_sound_usage_preference": profile.get_gen_ai_preference(),
+ "opt_out_speech": profile.get_gen_ai_preference_opt_out_speech(),
+ }
+ )
tvars = {"form": form, "activePage": "ai_preferences"}
return render(request, "accounts/edit_ai_preferences.html", tvars)
diff --git a/apiv2/serializers.py b/apiv2/serializers.py
index 41cab24e8e..50f655c003 100644
--- a/apiv2/serializers.py
+++ b/apiv2/serializers.py
@@ -225,7 +225,7 @@ def get_license(self, obj):
gen_ai_preference = serializers.SerializerMethodField()
def get_gen_ai_preference(self, obj):
- return obj.user.profile.get_gen_ai_preference()
+ return obj.user.profile.get_gen_ai_preference(category_code=obj.category_code)
category = serializers.SerializerMethodField()
diff --git a/freesound/settings.py b/freesound/settings.py
index fb688ac9bb..396d7fcd55 100644
--- a/freesound/settings.py
+++ b/freesound/settings.py
@@ -663,6 +663,16 @@ def load_broad_sound_taxonomy_from_csv(path):
SEARCH_LOG_SLOW_QUERIES_MS_THRESHOLD = 1000 # Log search queries that take longer than this threshold in milliseconds. Set it to -1 to disable logging of slow queries.
SEARCH_LOG_SLOW_QUERIES_QUERY_BASE_URL = "http://localhost:8983/solr/freesound/select/"
+# -------------------------------------------------------------------------------
+# AI preferences panel
+AI_PREF_NO_ADDITIONAL_RESTRICTIONS = "no-additional-restrictions"
+AI_PREF_OPEN_MODELS = "open-models"
+AI_PREF_OPEN_NONCOMMERCIAL_MODELS = "open-noncommercial-models"
+AI_PREF_NO_GEN_AI = "no-gen-ai"
+DEFAULT_AI_PREFERENCE = AI_PREF_NO_ADDITIONAL_RESTRICTIONS
+DEFAULT_OPT_OUT_SPEECH = False
+AI_OPT_OUT_SPEECH_TAXONOMY_CODES = ["sp-s"] # Solo-speech
+
# -------------------------------------------------------------------------------
# Tag recommendation client settings
TAGRECOMMENDATION_ADDRESS = "tagrecommendation"
diff --git a/sounds/models.py b/sounds/models.py
index b981ad84b4..2d336c5d8c 100644
--- a/sounds/models.py
+++ b/sounds/models.py
@@ -1770,6 +1770,9 @@ def get_second_level_category_search_url(self):
else:
return None
+ def get_gen_ai_preference(self):
+ return self.user.profile.get_gen_ai_preference(category_code=self.category_code)
+
def estimate_bpm_from_metadata(self, min_bpm=25, max_bpm=300):
"""
Estimate the bpm of a sound by looking at its description, tags and name.
diff --git a/templates/accounts/edit_ai_preferences.html b/templates/accounts/edit_ai_preferences.html
index f0eeadc75a..b56e7b44c3 100644
--- a/templates/accounts/edit_ai_preferences.html
+++ b/templates/accounts/edit_ai_preferences.html
@@ -42,8 +42,19 @@
{{ ai_option.label }}
{% endfor %}
+
+
+ Complementarily to the options above, you can make an extra statement to opt-out from Gen AI model training for the particular case of voice recordings.
+ This option is designed to avoid the use of such sounds for training models that may generate speech that could be misused to impersonate real people.
+ Mark the checkbox below to enable this option:
+
diff --git a/utils/search/backends/solr555pysolr.py b/utils/search/backends/solr555pysolr.py
index 26e805e207..6a04b9b551 100644
--- a/utils/search/backends/solr555pysolr.py
+++ b/utils/search/backends/solr555pysolr.py
@@ -330,9 +330,6 @@ def convert_sound_to_search_engine_document(self, sound):
document["spectral_path_l"] = locations["display"]["spectral"]["L"]["path"]
document["preview_path"] = locations["preview"]["LQ"]["mp3"]["path"]
- # Index uploader AI preference
- document["gen_ai_preference"] = sound.user.profile.get_gen_ai_preference()
-
# Index consolidated audio descriptors
descriptors_to_index = {}
descriptors_data = sound.get_consolidated_analysis_data()
@@ -381,6 +378,9 @@ def convert_sound_to_search_engine_document(self, sound):
f"{settings.SEARCH_SOUNDS_FIELD_SUBCATEGORY}{SOLR_DYNAMIC_FIELDS_SUFFIX_MAP[settings.AUDIO_DESCRIPTOR_TYPE_STRING]}"
] = user_provided_subcategory
+ # Index uploader AI preference
+ document["gen_ai_preference"] = sound.get_gen_ai_preference()
+
# Finally add the sound ID and content type
document.update({"id": sound.id, "content_type": SOLR_DOC_CONTENT_TYPES["sound"]})
From dc1015c1aa72cefd9446c74694745d938e20d0e2 Mon Sep 17 00:00:00 2001
From: ffont
Date: Fri, 10 Jul 2026 08:57:01 +0200
Subject: [PATCH 07/10] Formatting
---
freesound/audio_descriptor_settings.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/freesound/audio_descriptor_settings.py b/freesound/audio_descriptor_settings.py
index 7814b2efbd..965abeda30 100644
--- a/freesound/audio_descriptor_settings.py
+++ b/freesound/audio_descriptor_settings.py
@@ -61,9 +61,9 @@
{
"name": "bpm_confidence",
"analyzer": "ac-extractor_v3", # "fs-essentia-extractor_v1",
- "get_func": lambda d, s: (
- 1.0 if s.estimate_bpm_from_metadata() else d["tempo_confidence"]
- ), # d["fs.bpm_confidence"],
+ "get_func": lambda d, s: 1.0
+ if s.estimate_bpm_from_metadata()
+ else d["tempo_confidence"], # d["fs.bpm_confidence"],
},
{
"name": "brightness",
From 2cd9283d5aaf643c1051c8a187e84d27f7a6d4af Mon Sep 17 00:00:00 2001
From: ffont
Date: Fri, 10 Jul 2026 10:04:39 +0200
Subject: [PATCH 08/10] Further improvements in wording and option names for AI
preferences
---
_docs/api/source/resources.rst | 2 +-
accounts/forms.py | 2 +-
.../0044_alter_aipreference_preference.py | 35 +++++++++++++++++++
accounts/models.py | 16 ++++-----
accounts/tests/test_profile.py | 4 +--
freesound/settings.py | 8 ++---
templates/accounts/edit_ai_preferences.html | 6 ++--
7 files changed, 54 insertions(+), 19 deletions(-)
create mode 100644 accounts/migrations/0044_alter_aipreference_preference.py
diff --git a/_docs/api/source/resources.rst b/_docs/api/source/resources.rst
index 29c42722b8..117256d468 100644
--- a/_docs/api/source/resources.rst
+++ b/_docs/api/source/resources.rst
@@ -299,7 +299,7 @@ geotag string yes* Latitude and longitude o
is_geotagged boolean yes Whether the sound has geotag information.
created string yes The date when the sound was uploaded (e.g. "2014-04-16T20:07:11.145").
license string yes The Creative Commons license under which the sound is available to you ("Attribution", "Attribution NonCommercial", "Creative Commons 0").
-gen_ai_preference string yes The user preference regarding the use of the sound for training generative AI models ("no-additional-restrictions", "open-models", "open-noncommercial-models", "no-gen-ai"). Please check `our help section about generative AI training preferences `_ for more information about the meaning of these values.
+gen_ai_preference string yes The user preference regarding the use of the sound for training generative AI models ("no-additional-preferences", "open-source-models", "noncommercial-open-source-models", "no-gen-ai"). Please check `our help section about generative AI training preferences `_ for more information about the meaning of these values.
type string yes The original type of the sound (wav, aif, aiff, ogg, mp3, m4a, or flac).
channels integer yes The number of sound channels (mostly 1 or 2).
filesize integer yes The size of the file in bytes.
diff --git a/accounts/forms.py b/accounts/forms.py
index 5ce7503863..6418d55f22 100644
--- a/accounts/forms.py
+++ b/accounts/forms.py
@@ -498,7 +498,7 @@ class AIPreferenceForm(forms.Form):
)
opt_out_speech = forms.BooleanField(
label=mark_safe(
- "Do not allow sounds classified under the \"Speech > Solo speech\" BST category to be used for the purpose of training Gen AI models."
+ "Express my preference that sounds classified under the \"Speech > Solo speech\" BST category not be used for the purpose of training Gen AI models."
),
required=False,
)
diff --git a/accounts/migrations/0044_alter_aipreference_preference.py b/accounts/migrations/0044_alter_aipreference_preference.py
new file mode 100644
index 0000000000..9f15673837
--- /dev/null
+++ b/accounts/migrations/0044_alter_aipreference_preference.py
@@ -0,0 +1,35 @@
+# Generated by Django 4.2.27 on 2026-07-10 07:41
+
+from django.db import migrations, models
+
+
+def rename_aipreference_preferences(apps, schema_editor):
+ AIPreference = apps.get_model('accounts', 'AIPreference')
+
+ preference_mapping = {
+ 'freesound-cc-recommendation': 'no-additional-preferences',
+ 'open-noncommercial-models': 'noncommercial-open-source-models',
+ 'open-models': 'open-source-models',
+ }
+
+ for old_value, new_value in preference_mapping.items():
+ AIPreference.objects.filter(preference=old_value).update(preference=new_value)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('accounts', '0043_aipreference_opt_out_speech_and_more'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='aipreference',
+ name='preference',
+ field=models.CharField(choices=[('no-additional-preferences', 'No additional preferences'), ('open-source-models', 'Open Source Models only'), ('noncommercial-open-source-models', 'Non-Commercial Open Source Models only'), ('no-gen-ai', 'No generative AI')], default='no-additional-preferences'),
+ ),
+ migrations.RunPython(
+ rename_aipreference_preferences,
+ reverse_code=migrations.RunPython.noop,
+ ),
+ ]
diff --git a/accounts/models.py b/accounts/models.py
index 36643c0a59..b7ba1ce1d5 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -747,27 +747,27 @@ class AIPreference(models.Model):
AI_PREFERENCE_CHOICES_AND_EXPLANATION = [
{
- "code": settings.AI_PREF_NO_ADDITIONAL_RESTRICTIONS,
- "label": "No additional restrictions",
- "explanation": """No additional statement, your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms.
+ "code": settings.AI_PREF_NO_ADDITIONAL_PREFERENCES,
+ "label": "No additional preferences",
+ "explanation": """No additional statement, your sounds remain subject to the Creative Commons license terms selected for them.
Read here for more details.""",
},
{
"code": settings.AI_PREF_OPEN_MODELS,
- "label": "Open Models only",
- "explanation": """Your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms and as long as the trained models are made open source and freely available to the public.
+ "label": "Open Source Models only",
+ "explanation": """You express a preference for your sounds to be used for the purpose of training Gen AI models only when the trained models are released as Open Source Models and made freely available to the public.
Read here for more details.""",
},
{
"code": settings.AI_PREF_OPEN_NONCOMMERCIAL_MODELS,
- "label": "Open Non-Commercial Models only",
- "explanation": """Your sounds can be used for the purpose of training Gen AI models in accordance with the Creative Commons license terms and as long as the trained models are made open source, freely available to the public, and not trained in a commercial setting nor used for commercial purposes.
+ "label": "Non-Commercial Open Source Models only",
+ "explanation": """You express a preference for your sounds to be used for the purpose of training Gen AI models only when the trained models are released as Open Source Models, made freely available to the public, and not trained in a commercial setting nor used for commercial purposes.
Read here for more details.""",
},
{
"code": settings.AI_PREF_NO_GEN_AI,
"label": "No generative AI",
- "explanation": """Your sounds can not be used for the purpose of training Gen AI models. Read here for more details.""",
+ "explanation": """You express a preference that your sounds not be used for the purpose of training Gen AI models. Read here for more details.""",
},
]
diff --git a/accounts/tests/test_profile.py b/accounts/tests/test_profile.py
index ffa4793418..77f0fd3a58 100644
--- a/accounts/tests/test_profile.py
+++ b/accounts/tests/test_profile.py
@@ -224,7 +224,7 @@ def test_edit_user_profile_ai_preference(self):
# Now user edits preference in profile page
self.client.force_login(user)
- new_preference = "open-models"
+ new_preference = "open-source-models"
resp = self.client.post(
"/home/ai-preferences/",
{
@@ -240,7 +240,7 @@ def test_edit_user_profile_ai_preference(self):
self.assertEqual(Sound.objects.filter(user=user, is_index_dirty=True).count(), len(sounds))
# Now that there's an AI preference object already existing, try to change preference again and check that it works as expected
- even_newer_preference = "no-additional-restrictions"
+ even_newer_preference = "no-additional-preferences"
self.client.post(
"/home/ai-preferences/",
{
diff --git a/freesound/settings.py b/freesound/settings.py
index afcf21b7aa..8d06892fc9 100644
--- a/freesound/settings.py
+++ b/freesound/settings.py
@@ -671,11 +671,11 @@ def load_broad_sound_taxonomy_from_csv(path):
# -------------------------------------------------------------------------------
# AI preferences panel
-AI_PREF_NO_ADDITIONAL_RESTRICTIONS = "no-additional-restrictions"
-AI_PREF_OPEN_MODELS = "open-models"
-AI_PREF_OPEN_NONCOMMERCIAL_MODELS = "open-noncommercial-models"
+AI_PREF_NO_ADDITIONAL_PREFERENCES = "no-additional-preferences"
+AI_PREF_OPEN_MODELS = "open-source-models"
+AI_PREF_OPEN_NONCOMMERCIAL_MODELS = "noncommercial-open-source-models"
AI_PREF_NO_GEN_AI = "no-gen-ai"
-DEFAULT_AI_PREFERENCE = AI_PREF_NO_ADDITIONAL_RESTRICTIONS
+DEFAULT_AI_PREFERENCE = AI_PREF_NO_ADDITIONAL_PREFERENCES
DEFAULT_OPT_OUT_SPEECH = False
AI_OPT_OUT_SPEECH_TAXONOMY_CODES = ["sp-s"] # Solo-speech
diff --git a/templates/accounts/edit_ai_preferences.html b/templates/accounts/edit_ai_preferences.html
index b56e7b44c3..15f526032e 100644
--- a/templates/accounts/edit_ai_preferences.html
+++ b/templates/accounts/edit_ai_preferences.html
@@ -17,8 +17,8 @@
Generative AI preferences
This section allows you to set your preferences regarding the use of your uploaded sounds for the purpose of training generative Artificial Intelligence (Gen AI) models.
- Sounds may be used for Gen AI training in accordance with your chosen Creative Commons license terms.
- You may choose to add a statement to express additional usage terms for the specific case of training Gen AI models, however there is no guarantee that these will have any legal effect.
+ The use of sounds for Gen AI training depends on your chosen Creative Commons license terms and how those terms are interpreted and applied.
+ You can add a statement to express your preferences for the specific case of training Gen AI models, however there is no guarantee that these will have any legal effect.
Please, select one of the options below:
@@ -45,7 +45,7 @@
{{ ai_option.label }}
Complementarily to the options above, you can make an extra statement to opt-out from Gen AI model training for the particular case of voice recordings.
- This option is designed to avoid the use of such sounds for training models that may generate speech that could be misused to impersonate real people.
+ This option is designed to express a preference against the use of such sounds for training models that may generate speech that could be misused to impersonate real people.
Mark the checkbox below to enable this option: