diff --git a/api/config/api_urls.py b/api/config/api_urls.py
index e75781d14c3061892520290867d068f29991a016..98b863a93c3f108299ee67975e12df70ec9094c3 100644
--- a/api/config/api_urls.py
+++ b/api/config/api_urls.py
@@ -38,6 +38,10 @@ v1_patterns += [
         include(
             ('funkwhale_api.instance.urls', 'instance'),
             namespace='instance')),
+    url(r'^manage/',
+        include(
+            ('funkwhale_api.manage.urls', 'manage'),
+            namespace='manage')),
     url(r'^federation/',
         include(
             ('funkwhale_api.federation.api_urls', 'federation'),
diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index f376781b0498580db756062ab94204afbde77191..50c62e9d56ad60e281f0989c3d3e3c9062c4673d 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -97,6 +97,7 @@ THIRD_PARTY_APPS = (
     'dynamic_preferences',
     'django_filters',
     'cacheops',
+    'django_cleanup',
 )
 
 
diff --git a/api/funkwhale_api/common/serializers.py b/api/funkwhale_api/common/serializers.py
index 62d9c567e9693cb9a5542c5456b71e79315e6af5..a995cc360eca4792d02dacd8acffdb255690d5bc 100644
--- a/api/funkwhale_api/common/serializers.py
+++ b/api/funkwhale_api/common/serializers.py
@@ -12,6 +12,9 @@ class ActionSerializer(serializers.Serializer):
     filters = serializers.DictField(required=False)
     actions = None
     filterset_class = None
+    # those are actions identifier where we don't want to allow the "all"
+    # selector because it's to dangerous. Like object deletion.
+    dangerous_actions = []
 
     def __init__(self, *args, **kwargs):
         self.queryset = kwargs.pop('queryset')
@@ -49,6 +52,10 @@ class ActionSerializer(serializers.Serializer):
             'list of identifiers or the string "all".'.format(value))
 
     def validate(self, data):
+        dangerous = data['action'] in self.dangerous_actions
+        if dangerous and self.initial_data['objects'] == 'all':
+            raise serializers.ValidationError(
+                'This action is to dangerous to be applied to all objects')
         if self.filterset_class and 'filters' in data:
             qs_filterset = self.filterset_class(
                 data['filters'], queryset=data['objects'])
diff --git a/api/funkwhale_api/manage/__init__.py b/api/funkwhale_api/manage/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..03e091e5c5ed1abf61b267f35a379206a3ab391c
--- /dev/null
+++ b/api/funkwhale_api/manage/__init__.py
@@ -0,0 +1,3 @@
+"""
+App that includes all views/serializers and stuff for management API
+"""
diff --git a/api/funkwhale_api/manage/filters.py b/api/funkwhale_api/manage/filters.py
new file mode 100644
index 0000000000000000000000000000000000000000..9853b7a61fb3f0018f41ad2c497e635613523edd
--- /dev/null
+++ b/api/funkwhale_api/manage/filters.py
@@ -0,0 +1,25 @@
+from django.db.models import Count
+
+from django_filters import rest_framework as filters
+
+from funkwhale_api.common import fields
+from funkwhale_api.music import models as music_models
+
+
+class ManageTrackFileFilterSet(filters.FilterSet):
+    q = fields.SearchFilter(search_fields=[
+        'track__title',
+        'track__album__title',
+        'track__artist__name',
+        'source',
+    ])
+
+    class Meta:
+        model = music_models.TrackFile
+        fields = [
+            'q',
+            'track__album',
+            'track__artist',
+            'track',
+            'library_track'
+        ]
diff --git a/api/funkwhale_api/manage/serializers.py b/api/funkwhale_api/manage/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..02300ec0689c16f7250e5557a3c05d1eb2f07006
--- /dev/null
+++ b/api/funkwhale_api/manage/serializers.py
@@ -0,0 +1,82 @@
+from django.db import transaction
+from rest_framework import serializers
+
+from funkwhale_api.common import serializers as common_serializers
+from funkwhale_api.music import models as music_models
+
+from . import filters
+
+
+class ManageTrackFileArtistSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = music_models.Artist
+        fields = [
+            'id',
+            'mbid',
+            'creation_date',
+            'name',
+        ]
+
+
+class ManageTrackFileAlbumSerializer(serializers.ModelSerializer):
+    artist = ManageTrackFileArtistSerializer()
+
+    class Meta:
+        model = music_models.Album
+        fields = (
+            'id',
+            'mbid',
+            'title',
+            'artist',
+            'release_date',
+            'cover',
+            'creation_date',
+        )
+
+
+class ManageTrackFileTrackSerializer(serializers.ModelSerializer):
+    artist = ManageTrackFileArtistSerializer()
+    album = ManageTrackFileAlbumSerializer()
+
+    class Meta:
+        model = music_models.Track
+        fields = (
+            'id',
+            'mbid',
+            'title',
+            'album',
+            'artist',
+            'creation_date',
+            'position',
+        )
+
+
+class ManageTrackFileSerializer(serializers.ModelSerializer):
+    track = ManageTrackFileTrackSerializer()
+
+    class Meta:
+        model = music_models.TrackFile
+        fields = (
+            'id',
+            'path',
+            'source',
+            'filename',
+            'mimetype',
+            'track',
+            'duration',
+            'mimetype',
+            'bitrate',
+            'size',
+            'path',
+            'library_track',
+        )
+
+
+class ManageTrackFileActionSerializer(common_serializers.ActionSerializer):
+    actions = ['delete']
+    dangerous_actions = ['delete']
+    filterset_class = filters.ManageTrackFileFilterSet
+
+    @transaction.atomic
+    def handle_delete(self, objects):
+        return objects.delete()
diff --git a/api/funkwhale_api/manage/urls.py b/api/funkwhale_api/manage/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..c434581ecde46de48d4b87f36984923dfcff84fa
--- /dev/null
+++ b/api/funkwhale_api/manage/urls.py
@@ -0,0 +1,11 @@
+from django.conf.urls import include, url
+from . import views
+
+from rest_framework import routers
+library_router = routers.SimpleRouter()
+library_router.register(r'track-files', views.ManageTrackFileViewSet, 'track-files')
+
+urlpatterns = [
+    url(r'^library/',
+        include((library_router.urls, 'instance'), namespace='library')),
+]
diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..74059caa1d97a1cef31fcf7408a49e7486447f93
--- /dev/null
+++ b/api/funkwhale_api/manage/views.py
@@ -0,0 +1,49 @@
+from rest_framework import mixins
+from rest_framework import response
+from rest_framework import viewsets
+from rest_framework.decorators import list_route
+
+from funkwhale_api.music import models as music_models
+from funkwhale_api.users.permissions import HasUserPermission
+
+from . import filters
+from . import serializers
+
+
+class ManageTrackFileViewSet(
+        mixins.ListModelMixin,
+        mixins.RetrieveModelMixin,
+        mixins.DestroyModelMixin,
+        viewsets.GenericViewSet):
+    queryset = (
+        music_models.TrackFile.objects.all()
+            .select_related(
+                'track__artist',
+                'track__album__artist',
+                'library_track')
+            .order_by('-id')
+    )
+    serializer_class = serializers.ManageTrackFileSerializer
+    filter_class = filters.ManageTrackFileFilterSet
+    permission_classes = (HasUserPermission,)
+    required_permissions = ['library']
+    ordering_fields = [
+        'accessed_date',
+        'modification_date',
+        'creation_date',
+        'track__artist__name',
+        'bitrate',
+        'size',
+        'duration',
+    ]
+
+    @list_route(methods=['post'])
+    def action(self, request, *args, **kwargs):
+        queryset = self.get_queryset()
+        serializer = serializers.ManageTrackFileActionSerializer(
+            request.data,
+            queryset=queryset,
+        )
+        serializer.is_valid(raise_exception=True)
+        result = serializer.save()
+        return response.Response(result, status=200)
diff --git a/api/funkwhale_api/music/factories.py b/api/funkwhale_api/music/factories.py
index 412e2f798835579217f6fa84b35e926d59baaba9..11423f5b0134936b9efeed1b352e3e4dc6bdcd1c 100644
--- a/api/funkwhale_api/music/factories.py
+++ b/api/funkwhale_api/music/factories.py
@@ -117,6 +117,11 @@ class ImportJobFactory(factory.django.DjangoModelFactory):
             status='finished',
             audio_file=None,
         )
+        with_audio_file = factory.Trait(
+            status='finished',
+            audio_file=factory.django.FileField(
+                from_path=os.path.join(SAMPLES_PATH, 'test.ogg')),
+        )
 
 
 @registry.register(name='music.FileImportJob')
diff --git a/api/funkwhale_api/users/models.py b/api/funkwhale_api/users/models.py
index a3c5bd0bfe3ea18d5133190b7105bc2e477a9b25..fcf78d0473efa38a00e7d8e1f12d1e4a0533768e 100644
--- a/api/funkwhale_api/users/models.py
+++ b/api/funkwhale_api/users/models.py
@@ -27,7 +27,7 @@ PERMISSIONS_CONFIGURATION = {
     },
     'library': {
         'label': 'Manage library',
-        'help_text': 'Manage library',
+        'help_text': 'Manage library, delete files, tracks, artists, albums...',
     },
     'settings': {
         'label': 'Manage instance-level settings',
diff --git a/api/requirements/base.txt b/api/requirements/base.txt
index d88483de4f2d131eb419db22b74dd71246d6dcd9..13c0efdbc7df9547a6991fc589d37312de98909d 100644
--- a/api/requirements/base.txt
+++ b/api/requirements/base.txt
@@ -65,3 +65,4 @@ cryptography>=2,<3
 # requests-http-signature==0.0.3
 # clone until the branch is merged and released upstream
 git+https://github.com/EliotBerriot/requests-http-signature.git@signature-header-support
+django-cleanup==2.1.0
diff --git a/api/tests/common/test_serializers.py b/api/tests/common/test_serializers.py
index 5636765562d61c6262f7f17b77c85c3b5b98b128..f0f5fb7e61c8bd1d30fd14dec1b0edb28c5d31ab 100644
--- a/api/tests/common/test_serializers.py
+++ b/api/tests/common/test_serializers.py
@@ -18,6 +18,17 @@ class TestSerializer(serializers.ActionSerializer):
         return {'hello': 'world'}
 
 
+class TestDangerousSerializer(serializers.ActionSerializer):
+    actions = ['test', 'test_dangerous']
+    dangerous_actions = ['test_dangerous']
+
+    def handle_test(self, objects):
+        pass
+
+    def handle_test_dangerous(self, objects):
+        pass
+
+
 def test_action_serializer_validates_action():
     data = {'objects': 'all', 'action': 'nope'}
     serializer = TestSerializer(data, queryset=models.User.objects.none())
@@ -98,3 +109,28 @@ def test_action_serializers_validates_at_least_one_object():
 
     assert serializer.is_valid() is False
     assert 'non_field_errors' in serializer.errors
+
+
+def test_dangerous_actions_refuses_all(factories):
+    factories['users.User']()
+    data = {
+        'objects': 'all',
+        'action': 'test_dangerous',
+    }
+    serializer = TestDangerousSerializer(
+        data, queryset=models.User.objects.all())
+
+    assert serializer.is_valid() is False
+    assert 'non_field_errors' in serializer.errors
+
+
+def test_dangerous_actions_refuses_not_listed(factories):
+    factories['users.User']()
+    data = {
+        'objects': 'all',
+        'action': 'test',
+    }
+    serializer = TestDangerousSerializer(
+        data, queryset=models.User.objects.all())
+
+    assert serializer.is_valid() is True
diff --git a/api/tests/manage/__init__.py b/api/tests/manage/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/api/tests/manage/test_serializers.py b/api/tests/manage/test_serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..45167722ca2f95ef23eb3c98b8b96491d41207e0
--- /dev/null
+++ b/api/tests/manage/test_serializers.py
@@ -0,0 +1,10 @@
+from funkwhale_api.manage import serializers
+
+
+def test_manage_track_file_action_delete(factories):
+    tfs = factories['music.TrackFile'](size=5)
+    s = serializers.ManageTrackFileActionSerializer(queryset=None)
+
+    s.handle_delete(tfs.__class__.objects.all())
+
+    assert tfs.__class__.objects.count() == 0
diff --git a/api/tests/manage/test_views.py b/api/tests/manage/test_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..db2e0980a8b512e5267dbfe5882fcb87a76a24f3
--- /dev/null
+++ b/api/tests/manage/test_views.py
@@ -0,0 +1,26 @@
+import pytest
+
+from django.urls import reverse
+
+from funkwhale_api.manage import serializers
+from funkwhale_api.manage import views
+
+
+@pytest.mark.parametrize('view,permissions,operator', [
+    (views.ManageTrackFileViewSet, ['library'], 'and'),
+])
+def test_permissions(assert_user_permission, view, permissions, operator):
+    assert_user_permission(view, permissions, operator)
+
+
+def test_track_file_view(factories, superuser_api_client):
+    tfs = factories['music.TrackFile'].create_batch(size=5)
+    qs = tfs[0].__class__.objects.order_by('-creation_date')
+    url = reverse('api:v1:manage:library:track-files-list')
+
+    response = superuser_api_client.get(url, {'sort': '-creation_date'})
+    expected = serializers.ManageTrackFileSerializer(
+        qs, many=True, context={'request': response.wsgi_request}).data
+
+    assert response.data['count'] == len(tfs)
+    assert response.data['results'] == expected
diff --git a/changes/changelog.d/223.feature b/changes/changelog.d/223.feature
new file mode 100644
index 0000000000000000000000000000000000000000..c2f104ba59381635755c8ea249f04f50c8873686
--- /dev/null
+++ b/changes/changelog.d/223.feature
@@ -0,0 +1,10 @@
+Files management interface for users with "library" permission (#223)
+
+Files management interface
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This is the first bit of an ongoing work that will span several releases, to
+bring more powerful library management features to Funkwhale. This iteration
+includes a basic file management interface where users with the "library"
+permission can list and search available files, order them using
+various criterias (size, bitrate, duration...) and delete them.
diff --git a/changes/changelog.d/241.enhancement b/changes/changelog.d/241.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..00c84c4977a48eba574cc8cd0d5a6e070443130a
--- /dev/null
+++ b/changes/changelog.d/241.enhancement
@@ -0,0 +1 @@
+Autoremove media files on model instance deletion (#241)
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index e8f330c38aa28b19f56d98efc507ec1b2d1c56b1..72c55847fa3ed09c1cd428cba0347495f3608903 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -68,6 +68,12 @@
                 :title="$t('Pending import requests')">
                 {{ notifications.importRequests }}</div>
             </router-link>
+            <router-link
+              class="item"
+              v-if="$store.state.auth.availablePermissions['library']"
+              :to="{name: 'manage.library.files'}">
+              <i class="book icon"></i>{{ $t('Library') }}
+            </router-link>
             <router-link
               class="item"
               v-else-if="$store.state.auth.availablePermissions['upload']"
diff --git a/front/src/components/common/ActionTable.vue b/front/src/components/common/ActionTable.vue
index 718e57b19b61672913d0de6145ec7447c69ac0cf..5221c328292c317c199850d6c9a3de58fec188f9 100644
--- a/front/src/components/common/ActionTable.vue
+++ b/front/src/components/common/ActionTable.vue
@@ -21,7 +21,7 @@
                   :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']">
                   {{ $t('Go') }}</div>
                 <dangerous-button
-                  v-else :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"
+                  v-else-if="!currentAction.isDangerous" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"
                   confirm-color="green"
                   color=""
                   @confirm="launchAction">
@@ -36,7 +36,7 @@
               <div class="count field">
                 <span v-if="selectAll">{{ $t('{% count %} on {% total %} selected', {count: objectsData.count, total: objectsData.count}) }}</span>
                 <span v-else>{{ $t('{% count %} on {% total %} selected', {count: checked.length, total: objectsData.count}) }}</span>
-                <template v-if="checkable.length === checked.length">
+                <template v-if="!currentAction.isDangerous && checkable.length === checked.length">
                   <a @click="selectAll = true" v-if="!selectAll">
                     {{ $t('Select all {% total %} elements', {total: objectsData.count}) }}
                   </a>
diff --git a/front/src/components/manage/library/FilesTable.vue b/front/src/components/manage/library/FilesTable.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2788006f4a4b1c63ca3e5ff8445cad38f2ef7880
--- /dev/null
+++ b/front/src/components/manage/library/FilesTable.vue
@@ -0,0 +1,206 @@
+<template>
+  <div>
+    <div class="ui inline form">
+      <div class="fields">
+        <div class="ui field">
+          <label>{{ $t('Search') }}</label>
+          <input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
+        </div>
+        <div class="field">
+          <i18next tag="label" path="Ordering"/>
+          <select class="ui dropdown" v-model="ordering">
+            <option v-for="option in orderingOptions" :value="option[0]">
+              {{ option[1] }}
+            </option>
+          </select>
+        </div>
+        <div class="field">
+          <i18next tag="label" path="Ordering direction"/>
+          <select class="ui dropdown" v-model="orderingDirection">
+            <option value="+">Ascending</option>
+            <option value="-">Descending</option>
+          </select>
+        </div>
+      </div>
+      </div>
+    <div class="dimmable">
+      <div v-if="isLoading" class="ui active inverted dimmer">
+          <div class="ui loader"></div>
+      </div>
+      <action-table
+        v-if="result"
+        @action-launched="fetchData"
+        :objects-data="result"
+        :actions="actions"
+        :action-url="'manage/library/track-files/action/'"
+        :filters="actionFilters">
+        <template slot="header-cells">
+          <th>{{ $t('Title') }}</th>
+          <th>{{ $t('Artist') }}</th>
+          <th>{{ $t('Album') }}</th>
+          <th>{{ $t('Import date') }}</th>
+          <th>{{ $t('Type') }}</th>
+          <th>{{ $t('Bitrate') }}</th>
+          <th>{{ $t('Duration') }}</th>
+          <th>{{ $t('Size') }}</th>
+        </template>
+        <template slot="row-cells" slot-scope="scope">
+          <td>
+            <span :title="scope.obj.track.title">{{ scope.obj.track.title|truncate(30) }}</span>
+          </td>
+          <td>
+            <span :title="scope.obj.track.artist.name">{{ scope.obj.track.artist.name|truncate(30) }}</span>
+          </td>
+          <td>
+            <span :title="scope.obj.track.album.title">{{ scope.obj.track.album.title|truncate(20) }}</span>
+          </td>
+          <td>
+            <human-date :date="scope.obj.creation_date"></human-date>
+          </td>
+          <td v-if="scope.obj.audio_mimetype">
+            {{ scope.obj.audio_mimetype }}
+          </td>
+          <td v-else>
+            {{ $t('N/A') }}
+          </td>
+          <td v-if="scope.obj.bitrate">
+            {{ scope.obj.bitrate | humanSize }}/s
+          </td>
+          <td v-else>
+            {{ $t('N/A') }}
+          </td>
+          <td v-if="scope.obj.duration">
+            {{ time.parse(scope.obj.duration) }}
+          </td>
+          <td v-else>
+            {{ $t('N/A') }}
+          </td>
+          <td v-if="scope.obj.size">
+            {{ scope.obj.size | humanSize }}
+          </td>
+          <td v-else>
+            {{ $t('N/A') }}
+          </td>
+        </template>
+      </action-table>
+    </div>
+    <div>
+      <pagination
+        v-if="result && result.results.length > 0"
+        @page-changed="selectPage"
+        :compact="true"
+        :current="page"
+        :paginate-by="paginateBy"
+        :total="result.count"
+        ></pagination>
+
+      <span v-if="result && result.results.length > 0">
+        {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
+      </span>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+import _ from 'lodash'
+import time from '@/utils/time'
+import Pagination from '@/components/Pagination'
+import ActionTable from '@/components/common/ActionTable'
+import OrderingMixin from '@/components/mixins/Ordering'
+
+export default {
+  mixins: [OrderingMixin],
+  props: {
+    filters: {type: Object, required: false}
+  },
+  components: {
+    Pagination,
+    ActionTable
+  },
+  data () {
+    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
+    return {
+      time,
+      isLoading: false,
+      result: null,
+      page: 1,
+      paginateBy: 25,
+      search: '',
+      orderingDirection: defaultOrdering.direction || '+',
+      ordering: defaultOrdering.field,
+      orderingOptions: [
+        ['creation_date', 'Creation date'],
+        ['accessed_date', 'Accessed date'],
+        ['modification_date', 'Modification date'],
+        ['size', 'Size'],
+        ['bitrate', 'Bitrate'],
+        ['duration', 'Duration']
+      ]
+
+    }
+  },
+  created () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      let params = _.merge({
+        'page': this.page,
+        'page_size': this.paginateBy,
+        'q': this.search,
+        'ordering': this.getOrderingAsString()
+      }, this.filters)
+      let self = this
+      self.isLoading = true
+      self.checked = []
+      axios.get('/manage/library/track-files/', {params: params}).then((response) => {
+        self.result = response.data
+        self.isLoading = false
+      }, error => {
+        self.isLoading = false
+        self.errors = error.backendErrors
+      })
+    },
+    selectPage: function (page) {
+      this.page = page
+    }
+  },
+  computed: {
+    actionFilters () {
+      var currentFilters = {
+        q: this.search
+      }
+      if (this.filters) {
+        return _.merge(currentFilters, this.filters)
+      } else {
+        return currentFilters
+      }
+    },
+    actions () {
+      return [
+        {
+          name: 'delete',
+          label: this.$t('Delete'),
+          isDangerous: true
+        }
+      ]
+    }
+  },
+  watch: {
+    search (newValue) {
+      this.page = 1
+      this.fetchData()
+    },
+    page () {
+      this.fetchData()
+    },
+    ordering () {
+      this.fetchData()
+    },
+    orderingDirection () {
+      this.fetchData()
+    }
+  }
+}
+</script>
diff --git a/front/src/router/index.js b/front/src/router/index.js
index f71dab7f92decf05f5df0ca2129207bf7830fb48..a52070e35912b42813db85f3c8ac195f6e39e4d2 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -29,6 +29,8 @@ import PlaylistDetail from '@/views/playlists/Detail'
 import PlaylistList from '@/views/playlists/List'
 import Favorites from '@/components/favorites/List'
 import AdminSettings from '@/views/admin/Settings'
+import AdminLibraryBase from '@/views/admin/library/Base'
+import AdminLibraryFilesList from '@/views/admin/library/FilesList'
 import FederationBase from '@/views/federation/Base'
 import FederationScan from '@/views/federation/Scan'
 import FederationLibraryDetail from '@/views/federation/LibraryDetail'
@@ -167,6 +169,17 @@ export default new Router({
         { path: 'libraries/:id', name: 'federation.libraries.detail', component: FederationLibraryDetail, props: true }
       ]
     },
+    {
+      path: '/manage/library',
+      component: AdminLibraryBase,
+      children: [
+        {
+          path: 'files',
+          name: 'manage.library.files',
+          component: AdminLibraryFilesList
+        }
+      ]
+    },
     {
       path: '/library',
       component: Library,
diff --git a/front/src/views/admin/library/Base.vue b/front/src/views/admin/library/Base.vue
new file mode 100644
index 0000000000000000000000000000000000000000..834fca920f62f195cb83bbd37c4d65dd47ce946a
--- /dev/null
+++ b/front/src/views/admin/library/Base.vue
@@ -0,0 +1,28 @@
+<template>
+  <div class="main pusher"  v-title="'Manage library'">
+    <div class="ui secondary pointing menu">
+      <router-link
+        class="ui item"
+        :to="{name: 'manage.library.files'}">{{ $t('Files') }}</router-link>
+    </div>
+    <router-view :key="$route.fullPath"></router-view>
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss">
+@import '../../../style/vendor/media';
+
+.main.pusher > .ui.secondary.menu {
+  @include media(">tablet") {
+    margin: 0 2.5rem;
+  }
+  .item {
+    padding-top: 1.5em;
+    padding-bottom: 1.5em;
+  }
+}
+</style>
diff --git a/front/src/views/admin/library/FilesList.vue b/front/src/views/admin/library/FilesList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9c52de5767042d9a2ddfe805965ef316d3dc5933
--- /dev/null
+++ b/front/src/views/admin/library/FilesList.vue
@@ -0,0 +1,23 @@
+<template>
+  <div v-title="'Files'">
+    <div class="ui vertical stripe segment">
+      <h2 class="ui header">{{ $t('Library files') }}</h2>
+      <div class="ui hidden divider"></div>
+      <library-files-table :show-library="true"></library-files-table>
+    </div>
+  </div>
+</template>
+
+<script>
+import LibraryFilesTable from '@/components/manage/library/FilesTable'
+
+export default {
+  components: {
+    LibraryFilesTable
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+</style>