diff --git a/api/funkwhale_api/music/factories.py b/api/funkwhale_api/music/factories.py
index fd905f675acf8d24307135e7a9ab4f04429eb0e2..9571f978516535b357665a6672235be8abbd3b47 100644
--- a/api/funkwhale_api/music/factories.py
+++ b/api/funkwhale_api/music/factories.py
@@ -14,11 +14,28 @@ SAMPLES_PATH = os.path.join(
 )
 
 
+def playable_factory(field):
+    @factory.post_generation
+    def inner(self, create, extracted, **kwargs):
+        if not create:
+            return
+
+        if extracted:
+            UploadFactory(
+                library__privacy_level="everyone",
+                import_status="finished",
+                **{field: self}
+            )
+
+    return inner
+
+
 @registry.register
 class ArtistFactory(factory.django.DjangoModelFactory):
     name = factory.Faker("name")
     mbid = factory.Faker("uuid4")
     fid = factory.Faker("federation_url")
+    playable = playable_factory("track__album__artist")
 
     class Meta:
         model = "music.Artist"
@@ -33,6 +50,7 @@ class AlbumFactory(factory.django.DjangoModelFactory):
     artist = factory.SubFactory(ArtistFactory)
     release_group_id = factory.Faker("uuid4")
     fid = factory.Faker("federation_url")
+    playable = playable_factory("track__album")
 
     class Meta:
         model = "music.Album"
@@ -47,6 +65,7 @@ class TrackFactory(factory.django.DjangoModelFactory):
     artist = factory.SelfAttribute("album.artist")
     position = 1
     tags = ManyToManyFromList("tags")
+    playable = playable_factory("track")
 
     class Meta:
         model = "music.Track"
@@ -71,6 +90,9 @@ class UploadFactory(factory.django.DjangoModelFactory):
 
     class Params:
         in_place = factory.Trait(audio_file=None)
+        playable = factory.Trait(
+            import_status="finished", library__privacy_level="everyone"
+        )
 
 
 @registry.register
diff --git a/api/funkwhale_api/subsonic/views.py b/api/funkwhale_api/subsonic/views.py
index 48810d02b1a8b75d3be8c14a43f5285c1e8118fc..7ca9b13a897d7e41bf0c5b42b6f865fac8c8b72d 100644
--- a/api/funkwhale_api/subsonic/views.py
+++ b/api/funkwhale_api/subsonic/views.py
@@ -19,7 +19,9 @@ from funkwhale_api.playlists import models as playlists_models
 from . import authentication, filters, negotiation, serializers
 
 
-def find_object(queryset, model_field="pk", field="id", cast=int):
+def find_object(
+    queryset, model_field="pk", field="id", cast=int, filter_playable=False
+):
     def decorator(func):
         def inner(self, request, *args, **kwargs):
             data = request.GET or request.POST
@@ -50,6 +52,11 @@ def find_object(queryset, model_field="pk", field="id", cast=int):
             qs = queryset
             if hasattr(qs, "__call__"):
                 qs = qs(request)
+
+            if filter_playable:
+                actor = utils.get_actor_from_request(request)
+                qs = qs.playable_by(actor).distinct()
+
             try:
                 obj = qs.get(**{model_field: value})
             except qs.model.DoesNotExist:
@@ -124,7 +131,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
 
     @list_route(methods=["get", "post"], url_name="get_artists", url_path="getArtists")
     def get_artists(self, request, *args, **kwargs):
-        artists = music_models.Artist.objects.all()
+        artists = music_models.Artist.objects.all().playable_by(
+            utils.get_actor_from_request(request)
+        )
         data = serializers.GetArtistsSerializer(artists).data
         payload = {"artists": data}
 
@@ -132,14 +141,16 @@ class SubsonicViewSet(viewsets.GenericViewSet):
 
     @list_route(methods=["get", "post"], url_name="get_indexes", url_path="getIndexes")
     def get_indexes(self, request, *args, **kwargs):
-        artists = music_models.Artist.objects.all()
+        artists = music_models.Artist.objects.all().playable_by(
+            utils.get_actor_from_request(request)
+        )
         data = serializers.GetArtistsSerializer(artists).data
         payload = {"indexes": data}
 
         return response.Response(payload, status=200)
 
     @list_route(methods=["get", "post"], url_name="get_artist", url_path="getArtist")
-    @find_object(music_models.Artist.objects.all())
+    @find_object(music_models.Artist.objects.all(), filter_playable=True)
     def get_artist(self, request, *args, **kwargs):
         artist = kwargs.pop("obj")
         data = serializers.GetArtistSerializer(artist).data
@@ -148,7 +159,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
         return response.Response(payload, status=200)
 
     @list_route(methods=["get", "post"], url_name="get_song", url_path="getSong")
-    @find_object(music_models.Track.objects.all())
+    @find_object(music_models.Track.objects.all(), filter_playable=True)
     def get_song(self, request, *args, **kwargs):
         track = kwargs.pop("obj")
         data = serializers.GetSongSerializer(track).data
@@ -159,14 +170,16 @@ class SubsonicViewSet(viewsets.GenericViewSet):
     @list_route(
         methods=["get", "post"], url_name="get_artist_info2", url_path="getArtistInfo2"
     )
-    @find_object(music_models.Artist.objects.all())
+    @find_object(music_models.Artist.objects.all(), filter_playable=True)
     def get_artist_info2(self, request, *args, **kwargs):
         payload = {"artist-info2": {}}
 
         return response.Response(payload, status=200)
 
     @list_route(methods=["get", "post"], url_name="get_album", url_path="getAlbum")
-    @find_object(music_models.Album.objects.select_related("artist"))
+    @find_object(
+        music_models.Album.objects.select_related("artist"), filter_playable=True
+    )
     def get_album(self, request, *args, **kwargs):
         album = kwargs.pop("obj")
         data = serializers.GetAlbumSerializer(album).data
@@ -174,7 +187,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
         return response.Response(payload, status=200)
 
     @list_route(methods=["get", "post"], url_name="stream", url_path="stream")
-    @find_object(music_models.Track.objects.all())
+    @find_object(music_models.Track.objects.all(), filter_playable=True)
     def stream(self, request, *args, **kwargs):
         track = kwargs.pop("obj")
         queryset = track.uploads.select_related("track__album__artist", "track__artist")
@@ -221,6 +234,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
         data = request.GET or request.POST
         filterset = filters.AlbumList2FilterSet(data, queryset=queryset)
         queryset = filterset.qs
+        actor = utils.get_actor_from_request(request)
+        queryset = queryset.playable_by(actor)
+
         try:
             offset = int(data["offset"])
         except (TypeError, KeyError, ValueError):
@@ -240,6 +256,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
     def search3(self, request, *args, **kwargs):
         data = request.GET or request.POST
         query = str(data.get("query", "")).replace("*", "")
+        actor = utils.get_actor_from_request(request)
         conf = [
             {
                 "subsonic": "artist",
@@ -292,6 +309,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
                 queryset = c["queryset"].filter(
                     utils.get_query(query, c["search_fields"])
                 )
+            queryset = queryset.playable_by(actor)
             queryset = queryset[offset : offset + size]
             payload["searchResult3"][c["subsonic"]] = c["serializer"](queryset)
         return response.Response(payload)
diff --git a/api/tests/subsonic/test_views.py b/api/tests/subsonic/test_views.py
index 1331c281ed386274c555f0789c3f9df9cb4c2dd9..94cbd8c16a73fc90cf717b876aa19da3349f236e 100644
--- a/api/tests/subsonic/test_views.py
+++ b/api/tests/subsonic/test_views.py
@@ -74,10 +74,13 @@ def test_ping(f, db, api_client):
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_artists(f, db, logged_in_api_client, factories):
+def test_get_artists(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-artists")
     assert url.endswith("getArtists") is True
-    factories["music.Artist"].create_batch(size=10)
+    factories["music.Artist"].create_batch(size=3, playable=True)
+    playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by")
     expected = {
         "artists": serializers.GetArtistsSerializer(
             music_models.Artist.objects.all()
@@ -87,19 +90,25 @@ def test_get_artists(f, db, logged_in_api_client, factories):
 
     assert response.status_code == 200
     assert response.data == expected
+    playable_by.assert_called_once_with(music_models.Artist.objects.all(), None)
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_artist(f, db, logged_in_api_client, factories):
+def test_get_artist(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-artist")
     assert url.endswith("getArtist") is True
-    artist = factories["music.Artist"]()
-    factories["music.Album"].create_batch(size=3, artist=artist)
+    artist = factories["music.Artist"](playable=True)
+    factories["music.Album"].create_batch(size=3, artist=artist, playable=True)
+    playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by")
+
     expected = {"artist": serializers.GetArtistSerializer(artist).data}
     response = logged_in_api_client.get(url, {"id": artist.pk})
 
     assert response.status_code == 200
     assert response.data == expected
+    playable_by.assert_called_once_with(music_models.Artist.objects.all(), None)
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
@@ -114,10 +123,13 @@ def test_get_invalid_artist(f, db, logged_in_api_client, factories):
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_artist_info2(f, db, logged_in_api_client, factories):
+def test_get_artist_info2(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-artist-info2")
     assert url.endswith("getArtistInfo2") is True
-    artist = factories["music.Artist"]()
+    artist = factories["music.Artist"](playable=True)
+    playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by")
 
     expected = {"artist-info2": {}}
     response = logged_in_api_client.get(url, {"id": artist.pk})
@@ -125,50 +137,62 @@ def test_get_artist_info2(f, db, logged_in_api_client, factories):
     assert response.status_code == 200
     assert response.data == expected
 
+    playable_by.assert_called_once_with(music_models.Artist.objects.all(), None)
+
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_album(f, db, logged_in_api_client, factories):
+def test_get_album(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-album")
     assert url.endswith("getAlbum") is True
     artist = factories["music.Artist"]()
     album = factories["music.Album"](artist=artist)
-    factories["music.Track"].create_batch(size=3, album=album)
+    factories["music.Track"].create_batch(size=3, album=album, playable=True)
+    playable_by = mocker.spy(music_models.AlbumQuerySet, "playable_by")
     expected = {"album": serializers.GetAlbumSerializer(album).data}
     response = logged_in_api_client.get(url, {"f": f, "id": album.pk})
 
     assert response.status_code == 200
     assert response.data == expected
 
+    playable_by.assert_called_once_with(
+        music_models.Album.objects.select_related("artist"), None
+    )
+
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_song(f, db, logged_in_api_client, factories):
+def test_get_song(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-song")
     assert url.endswith("getSong") is True
     artist = factories["music.Artist"]()
     album = factories["music.Album"](artist=artist)
-    track = factories["music.Track"](album=album)
+    track = factories["music.Track"](album=album, playable=True)
     upload = factories["music.Upload"](track=track)
+    playable_by = mocker.spy(music_models.TrackQuerySet, "playable_by")
     response = logged_in_api_client.get(url, {"f": f, "id": track.pk})
 
     assert response.status_code == 200
     assert response.data == {
         "song": serializers.get_track_data(track.album, track, upload)
     }
+    playable_by.assert_called_once_with(music_models.Track.objects.all(), None)
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_stream(f, db, logged_in_api_client, factories, mocker):
+def test_stream(f, db, logged_in_api_client, factories, mocker, queryset_equal_queries):
     url = reverse("api:subsonic-stream")
     mocked_serve = mocker.spy(music_views, "handle_serve")
     assert url.endswith("stream") is True
-    artist = factories["music.Artist"]()
-    album = factories["music.Album"](artist=artist)
-    track = factories["music.Track"](album=album)
-    upload = factories["music.Upload"](track=track)
-    response = logged_in_api_client.get(url, {"f": f, "id": track.pk})
+    upload = factories["music.Upload"](playable=True)
+    playable_by = mocker.spy(music_models.TrackQuerySet, "playable_by")
+    response = logged_in_api_client.get(url, {"f": f, "id": upload.track.pk})
 
     mocked_serve.assert_called_once_with(upload=upload, user=logged_in_api_client.user)
     assert response.status_code == 200
+    playable_by.assert_called_once_with(music_models.Track.objects.all(), None)
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
@@ -231,25 +255,30 @@ def test_get_starred(f, db, logged_in_api_client, factories):
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_album_list2(f, db, logged_in_api_client, factories):
+def test_get_album_list2(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-album-list2")
     assert url.endswith("getAlbumList2") is True
-    album1 = factories["music.Album"]()
-    album2 = factories["music.Album"]()
+    album1 = factories["music.Album"](playable=True)
+    album2 = factories["music.Album"](playable=True)
+    factories["music.Album"]()
+    playable_by = mocker.spy(music_models.AlbumQuerySet, "playable_by")
     response = logged_in_api_client.get(url, {"f": f, "type": "newest"})
 
     assert response.status_code == 200
     assert response.data == {
         "albumList2": {"album": serializers.get_album_list2_data([album2, album1])}
     }
+    playable_by.assert_called_once()
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
 def test_get_album_list2_pagination(f, db, logged_in_api_client, factories):
     url = reverse("api:subsonic-get-album-list2")
     assert url.endswith("getAlbumList2") is True
-    album1 = factories["music.Album"]()
-    factories["music.Album"]()
+    album1 = factories["music.Album"](playable=True)
+    factories["music.Album"](playable=True)
     response = logged_in_api_client.get(
         url, {"f": f, "type": "newest", "size": 1, "offset": 1}
     )
@@ -264,12 +293,15 @@ def test_get_album_list2_pagination(f, db, logged_in_api_client, factories):
 def test_search3(f, db, logged_in_api_client, factories):
     url = reverse("api:subsonic-search3")
     assert url.endswith("search3") is True
-    artist = factories["music.Artist"](name="testvalue")
+    artist = factories["music.Artist"](name="testvalue", playable=True)
     factories["music.Artist"](name="nope")
-    album = factories["music.Album"](title="testvalue")
+    factories["music.Artist"](name="nope2", playable=True)
+    album = factories["music.Album"](title="testvalue", playable=True)
     factories["music.Album"](title="nope")
-    track = factories["music.Track"](title="testvalue")
+    factories["music.Album"](title="nope2", playable=True)
+    track = factories["music.Track"](title="testvalue", playable=True)
     factories["music.Track"](title="nope")
+    factories["music.Track"](title="nope2", playable=True)
 
     response = logged_in_api_client.get(url, {"f": f, "query": "testval"})
 
@@ -385,20 +417,25 @@ def test_get_music_folders(f, db, logged_in_api_client, factories):
 
 
 @pytest.mark.parametrize("f", ["xml", "json"])
-def test_get_indexes(f, db, logged_in_api_client, factories):
+def test_get_indexes(
+    f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
+):
     url = reverse("api:subsonic-get-indexes")
     assert url.endswith("getIndexes") is True
-    factories["music.Artist"].create_batch(size=10)
+    factories["music.Artist"].create_batch(size=3, playable=True)
     expected = {
         "indexes": serializers.GetArtistsSerializer(
             music_models.Artist.objects.all()
         ).data
     }
+    playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by")
     response = logged_in_api_client.get(url)
 
     assert response.status_code == 200
     assert response.data == expected
 
+    playable_by.assert_called_once_with(music_models.Artist.objects.all(), None)
+
 
 def test_get_cover_art_album(factories, logged_in_api_client):
     url = reverse("api:subsonic-get-cover-art")