diff --git a/api/funkwhale_api/manage/serializers.py b/api/funkwhale_api/manage/serializers.py
index 67e0178e0e4688679463b308bf532f458fa51037..be67f5068a8722b4d440bf28dfef425937b4594f 100644
--- a/api/funkwhale_api/manage/serializers.py
+++ b/api/funkwhale_api/manage/serializers.py
@@ -500,6 +500,15 @@ class ManageLibrarySerializer(serializers.ModelSerializer):
             "followers_url",
             "actor",
         ]
+        read_only_fields = [
+            "fid",
+            "uuid",
+            "id",
+            "url",
+            "domain",
+            "actor",
+            "creation_date",
+        ]
 
     def get_uploads_count(self, obj):
         return getattr(obj, "_uploads_count", obj.uploads_count)
diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py
index c788dd96bcaf4582c2efb6446c83327b39d9214b..313cb681bff938f041624e8d88d0c534b3b342f2 100644
--- a/api/funkwhale_api/manage/views.py
+++ b/api/funkwhale_api/manage/views.py
@@ -200,6 +200,7 @@ follows_subquery = (
 class ManageLibraryViewSet(
     mixins.ListModelMixin,
     mixins.RetrieveModelMixin,
+    mixins.UpdateModelMixin,
     mixins.DestroyModelMixin,
     viewsets.GenericViewSet,
 ):
diff --git a/api/tests/manage/test_views.py b/api/tests/manage/test_views.py
index 72394052c9cc23132fa94f389e9c751c8d79ff70..c6571b04db463666b4bd0b20a6d2a1a3955e7e33 100644
--- a/api/tests/manage/test_views.py
+++ b/api/tests/manage/test_views.py
@@ -322,6 +322,18 @@ def test_library_detail(factories, superuser_api_client):
     assert response.data["id"] == library.id
 
 
+def test_library_update(factories, superuser_api_client):
+    library = factories["music.Library"](privacy_level="public")
+    url = reverse(
+        "api:v1:manage:library:libraries-detail", kwargs={"uuid": library.uuid}
+    )
+    response = superuser_api_client.patch(url, {"privacy_level": "me"})
+
+    assert response.status_code == 200
+    library.refresh_from_db()
+    assert library.privacy_level == "me"
+
+
 def test_library_detail_stats(factories, superuser_api_client):
     library = factories["music.Library"]()
     url = reverse(
diff --git a/changes/changelog.d/548.enhancement b/changes/changelog.d/548.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..99cd04941f768888dc13a7f7315fdf2223c220d7
--- /dev/null
+++ b/changes/changelog.d/548.enhancement
@@ -0,0 +1 @@
+Mods can now change a library visibility through the admin UI (#548)
diff --git a/front/src/views/admin/library/LibraryDetail.vue b/front/src/views/admin/library/LibraryDetail.vue
index beec7e2b408cf6b032c5f0114c979d77f958a64c..db53bd2b522d5d3cbf65500cdc8c107995e1f659 100644
--- a/front/src/views/admin/library/LibraryDetail.vue
+++ b/front/src/views/admin/library/LibraryDetail.vue
@@ -96,7 +96,16 @@
                       </router-link>
                     </td>
                     <td>
-                      {{ sharedLabels.fields.privacy_level.shortChoices[object.privacy_level] }}
+                      <select
+                        v-dropdown
+                        v-if="object.is_local"
+                        @change="updateObj('privacy_level')"
+                        v-model="object.privacy_level"
+
+                        class="ui search selection dropdown">
+                        <option v-for="p in ['me', 'instance', 'everyone']" :value="p">{{ sharedLabels.fields.privacy_level.shortChoices[p] }}</option>
+                      </select>
+                      <template v-else>{{ sharedLabels.fields.privacy_level.shortChoices[object.privacy_level] }}</template>
                     </td>
                   </tr>
                   <tr>
@@ -308,7 +317,28 @@ export default {
     },
     getQuery (field, value) {
       return `${field}:"${value}"`
-    }
+    },
+    updateObj(attr, toNull) {
+      let newValue = this.object[attr]
+      if (toNull && !newValue) {
+        newValue = null
+      }
+      let params = {}
+      params[attr] = newValue
+      axios.patch(`manage/library/libraries/${this.id}/`, params).then(
+        response => {
+          logger.default.info(
+            `${attr} was updated succcessfully to ${newValue}`
+          )
+        },
+        error => {
+          logger.default.error(
+            `Error while setting ${attr} to ${newValue}`,
+            error
+          )
+        }
+      )
+    },
   },
   computed: {
     labels() {