diff --git a/api/funkwhale_api/music/filters.py b/api/funkwhale_api/music/filters.py
index ff937a0f5c3aac8d5609b188d18b796e3b809e3a..fbea3735a267188485bfe236ecbd39ae56ae2e39 100644
--- a/api/funkwhale_api/music/filters.py
+++ b/api/funkwhale_api/music/filters.py
@@ -1,12 +1,36 @@
-import django_filters
+from django.db.models import Count
+
+from django_filters import rest_framework as filters
 
 from . import models
 
 
-class ArtistFilter(django_filters.FilterSet):
+class ListenableMixin(filters.FilterSet):
+    listenable = filters.BooleanFilter(name='_', method='filter_listenable')
+
+    def filter_listenable(self, queryset, name, value):
+        queryset = queryset.annotate(
+            files_count=Count('tracks__files')
+        )
+        if value:
+            return queryset.filter(files_count__gt=0)
+        else:
+            return queryset.filter(files_count=0)
+
+
+class ArtistFilter(ListenableMixin):
 
     class Meta:
         model = models.Artist
         fields = {
-            'name': ['exact', 'iexact', 'startswith', 'icontains']
+            'name': ['exact', 'iexact', 'startswith', 'icontains'],
+            'listenable': 'exact',
         }
+
+
+class AlbumFilter(ListenableMixin):
+    listenable = filters.BooleanFilter(name='_', method='filter_listenable')
+
+    class Meta:
+        model = models.Album
+        fields = ['listenable']
diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index d026c9847ca3db7f9920469a225399b0d6c0d3dc..78a3588762716759c21ebbf59d265d0bb90bc558 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -54,6 +54,7 @@ class TagViewSetMixin(object):
             queryset = queryset.filter(tags__pk=tag)
         return queryset
 
+
 class ArtistViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
     queryset = (
         models.Artist.objects.all()
@@ -67,6 +68,7 @@ class ArtistViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
     filter_class = filters.ArtistFilter
     ordering_fields = ('id', 'name', 'creation_date')
 
+
 class AlbumViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
     queryset = (
         models.Album.objects.all()
@@ -78,6 +80,7 @@ class AlbumViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
     permission_classes = [ConditionalAuthentication]
     search_fields = ['title__unaccent']
     ordering_fields = ('creation_date',)
+    filter_class = filters.AlbumFilter
 
 
 class ImportBatchViewSet(
diff --git a/api/tests/conftest.py b/api/tests/conftest.py
index 2d655f23f28dd2ea1ad56d4073df1147451c603c..4ff1a8ee789fa0559c129d05ba0e87da723b8795 100644
--- a/api/tests/conftest.py
+++ b/api/tests/conftest.py
@@ -4,6 +4,7 @@ import pytest
 from django.core.cache import cache as django_cache
 from dynamic_preferences.registries import global_preferences_registry
 from rest_framework.test import APIClient
+from rest_framework.test import APIRequestFactory
 
 from funkwhale_api.activity import record
 from funkwhale_api.taskapp import celery
@@ -84,6 +85,11 @@ def superuser_client(db, factories, client):
     delattr(client, 'user')
 
 
+@pytest.fixture
+def api_request():
+    return APIRequestFactory()
+
+
 @pytest.fixture
 def activity_registry():
     r = record.registry
diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..2956046168ca30487976512518bedce919752f2e
--- /dev/null
+++ b/api/tests/music/test_views.py
@@ -0,0 +1,45 @@
+import pytest
+
+from funkwhale_api.music import views
+
+
+@pytest.mark.parametrize('param,expected', [
+    ('true', 'full'),
+    ('false', 'empty'),
+])
+def test_artist_view_filter_listenable(
+        param, expected, factories, api_request):
+    artists = {
+        'empty': factories['music.Artist'](),
+        'full': factories['music.TrackFile']().track.artist,
+    }
+
+    request = api_request.get('/', {'listenable': param})
+    view = views.ArtistViewSet()
+    view.action_map = {'get': 'list'}
+    expected = [artists[expected]]
+    view.request = view.initialize_request(request)
+    queryset = view.filter_queryset(view.get_queryset())
+
+    assert list(queryset) == expected
+
+
+@pytest.mark.parametrize('param,expected', [
+    ('true', 'full'),
+    ('false', 'empty'),
+])
+def test_album_view_filter_listenable(
+        param, expected, factories, api_request):
+    artists = {
+        'empty': factories['music.Album'](),
+        'full': factories['music.TrackFile']().track.album,
+    }
+
+    request = api_request.get('/', {'listenable': param})
+    view = views.AlbumViewSet()
+    view.action_map = {'get': 'list'}
+    expected = [artists[expected]]
+    view.request = view.initialize_request(request)
+    queryset = view.filter_queryset(view.get_queryset())
+
+    assert list(queryset) == expected
diff --git a/changes/changelog.d/114.feature b/changes/changelog.d/114.feature
new file mode 100644
index 0000000000000000000000000000000000000000..88e3bfc1194f390c2eac430b72cc706872a72338
--- /dev/null
+++ b/changes/changelog.d/114.feature
@@ -0,0 +1 @@
+Can now filter artists and albums with no listenable tracks (#114)
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index 3cf123447128d8198853036ab1be5c0b52ebbc0a..52ccbdd7465c654b28eb53c141f50a6e62ca0209 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -129,7 +129,8 @@ export default {
         page: this.page,
         page_size: this.paginateBy,
         name__icontains: this.query,
-        ordering: this.getOrderingAsString()
+        ordering: this.getOrderingAsString(),
+        listenable: 'true'
       }
       logger.default.debug('Fetching artists')
       axios.get(url, {params: params}).then((response) => {