diff --git a/api/funkwhale_api/federation/models.py b/api/funkwhale_api/federation/models.py
index 7f8121641c4b272c74cac7dd799cd1c609e36e76..fa285f3cf4cc14c3c00c66b0c3e65ca7bcab42f2 100644
--- a/api/funkwhale_api/federation/models.py
+++ b/api/funkwhale_api/federation/models.py
@@ -388,16 +388,19 @@ class Fetch(models.Model):
         from . import serializers
 
         return {
-            contexts.FW.Artist: serializers.ArtistSerializer,
-            contexts.FW.Album: serializers.AlbumSerializer,
-            contexts.FW.Track: serializers.TrackSerializer,
-            contexts.AS.Audio: serializers.UploadSerializer,
-            contexts.FW.Library: serializers.LibrarySerializer,
-            contexts.AS.Group: serializers.ActorSerializer,
-            contexts.AS.Person: serializers.ActorSerializer,
-            contexts.AS.Organization: serializers.ActorSerializer,
-            contexts.AS.Service: serializers.ActorSerializer,
-            contexts.AS.Application: serializers.ActorSerializer,
+            contexts.FW.Artist: [serializers.ArtistSerializer],
+            contexts.FW.Album: [serializers.AlbumSerializer],
+            contexts.FW.Track: [serializers.TrackSerializer],
+            contexts.AS.Audio: [
+                serializers.UploadSerializer,
+                serializers.ChannelUploadSerializer,
+            ],
+            contexts.FW.Library: [serializers.LibrarySerializer],
+            contexts.AS.Group: [serializers.ActorSerializer],
+            contexts.AS.Person: [serializers.ActorSerializer],
+            contexts.AS.Organization: [serializers.ActorSerializer],
+            contexts.AS.Service: [serializers.ActorSerializer],
+            contexts.AS.Application: [serializers.ActorSerializer],
         }
 
 
diff --git a/api/funkwhale_api/federation/serializers.py b/api/funkwhale_api/federation/serializers.py
index b1583f9ac99b14c6ff6730ad4eaa4ef40d869146..5893027f7ae2f5d5bda8def812f48ea4767d264a 100644
--- a/api/funkwhale_api/federation/serializers.py
+++ b/api/funkwhale_api/federation/serializers.py
@@ -1780,6 +1780,7 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer):
     disc = serializers.IntegerField(min_value=1, allow_null=True, required=False)
     album = serializers.URLField(max_length=500, required=False)
     license = serializers.URLField(allow_null=True, required=False)
+    attributedTo = serializers.URLField(max_length=500, required=False)
     copyright = TruncatedCharField(
         truncate_length=music_models.MAX_LENGTHS["COPYRIGHT"],
         allow_null=True,
@@ -1823,9 +1824,10 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer):
             "position": jsonld.first_val(contexts.FW.position),
             "image": jsonld.first_obj(contexts.AS.image),
             "tags": jsonld.raw(contexts.AS.tag),
+            "attributedTo": jsonld.first_id(contexts.AS.attributedTo),
         }
 
-    def validate_album(self, v):
+    def _validate_album(self, v):
         return utils.retrieve_ap_object(
             v,
             actor=actors.get_service_actor(),
@@ -1836,6 +1838,17 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer):
         )
 
     def validate(self, data):
+        if not self.context.get("channel"):
+            if not data.get("attributedTo"):
+                raise serializers.ValidationError(
+                    "Missing channel context and no attributedTo available"
+                )
+            actor = actors.get_actor(data["attributedTo"])
+            if not actor.get_channel():
+                raise serializers.ValidationError("Not a channel")
+            self.context["channel"] = actor.get_channel()
+        if data.get("album"):
+            data["album"] = self._validate_album(data["album"])
         validated_data = super().validate(data)
         if data.get("content"):
             validated_data["description"] = {
diff --git a/api/funkwhale_api/federation/tasks.py b/api/funkwhale_api/federation/tasks.py
index bbed4ce8ef463c4df2f08933d7b302bed2553734..f802c5111de763433048306fc9f7b30db7905f48 100644
--- a/api/funkwhale_api/federation/tasks.py
+++ b/api/funkwhale_api/federation/tasks.py
@@ -374,8 +374,8 @@ def fetch(fetch_obj):
     except IndexError:
         return error("missing_jsonld_type")
     try:
-        serializer_class = fetch_obj.serializers[type]
-        model = serializer_class.Meta.model
+        serializer_classes = fetch_obj.serializers[type]
+        model = serializer_classes[0].Meta.model
     except (KeyError, AttributeError):
         fetch_obj.status = "skipped"
         fetch_obj.fetch_date = timezone.now()
@@ -388,8 +388,14 @@ def fetch(fetch_obj):
     else:
         existing = model.objects.filter(fid=id).first()
 
-    serializer = serializer_class(existing, data=payload)
-    if not serializer.is_valid():
+    serializer = None
+    for serializer_class in serializer_classes:
+        serializer = serializer_class(existing, data=payload)
+        if not serializer.is_valid():
+            continue
+        else:
+            break
+    if serializer.errors:
         return error("validation", validation_errors=serializer.errors)
     try:
         obj = serializer.save()
diff --git a/api/funkwhale_api/music/factories.py b/api/funkwhale_api/music/factories.py
index 8537c5c50922e8e7cda6c34ec6e9f55401b64cd9..14bcecb863810d69b7a09f0c5e771757c4e90775 100644
--- a/api/funkwhale_api/music/factories.py
+++ b/api/funkwhale_api/music/factories.py
@@ -185,6 +185,16 @@ class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
             import_status="finished", library__privacy_level="everyone"
         )
 
+    @factory.post_generation
+    def channel(self, created, extracted, **kwargs):
+        if not extracted:
+            return
+        from funkwhale_api.audio import factories as audio_factories
+
+        audio_factories.ChannelFactory(
+            library=self.library, artist=self.track.artist, **kwargs
+        )
+
 
 @registry.register
 class UploadVersionFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
diff --git a/api/tests/federation/test_tasks.py b/api/tests/federation/test_tasks.py
index ce4736503b981e05479ad4bde5189f7fe006168c..79573ff45151e338950a040a4ffeb4cfd5eb0b1f 100644
--- a/api/tests/federation/test_tasks.py
+++ b/api/tests/federation/test_tasks.py
@@ -461,17 +461,25 @@ def test_fetch_rel_alternate(factories, r_mock, mocker):
 
 
 @pytest.mark.parametrize(
-    "factory_name, serializer_class",
+    "factory_name, factory_kwargs, serializer_class",
     [
-        ("federation.Actor", serializers.ActorSerializer),
-        ("music.Library", serializers.LibrarySerializer),
-        ("music.Artist", serializers.ArtistSerializer),
-        ("music.Album", serializers.AlbumSerializer),
-        ("music.Track", serializers.TrackSerializer),
+        ("federation.Actor", {}, serializers.ActorSerializer),
+        ("music.Library", {}, serializers.LibrarySerializer),
+        ("music.Artist", {}, serializers.ArtistSerializer),
+        ("music.Album", {}, serializers.AlbumSerializer),
+        ("music.Track", {}, serializers.TrackSerializer),
+        (
+            "music.Upload",
+            {"bitrate": 200, "duration": 20},
+            serializers.UploadSerializer,
+        ),
+        ("music.Upload", {"channel": True}, serializers.ChannelUploadSerializer),
     ],
 )
-def test_fetch_url(factory_name, serializer_class, factories, r_mock, mocker):
-    obj = factories[factory_name]()
+def test_fetch_url(
+    factory_name, factory_kwargs, serializer_class, factories, r_mock, mocker
+):
+    obj = factories[factory_name](**factory_kwargs)
     fetch = factories["federation.Fetch"](url=obj.fid)
     payload = serializer_class(obj).data
     init = mocker.spy(serializer_class, "__init__")
diff --git a/front/src/components/library/UploadDetail.vue b/front/src/components/library/UploadDetail.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ce3a1e3a82b828dbe2c1c7e7734abaea17e9cb10
--- /dev/null
+++ b/front/src/components/library/UploadDetail.vue
@@ -0,0 +1,28 @@
+<template>
+  <main>
+    <div v-if="isLoading" class="ui vertical segment">
+      <div class="ui centered active inline loader"></div>
+    </div>
+  </main>
+</template>
+
+<script>
+import axios from "axios"
+
+
+export default {
+  props: ["id"],
+  async created() {
+    let upload = await this.fetchData()
+    this.$router.replace({name: "library.tracks.detail", params: {id: upload.track.id}})
+  },
+  methods: {
+    async fetchData() {
+      this.isLoading = true
+      let response = await axios.get(`uploads/${this.id}/`, {params: {refresh: 'true', include_channels: 'true'}})
+      this.isLoading = false
+      return response.data
+    },
+  }
+}
+</script>
diff --git a/front/src/router/index.js b/front/src/router/index.js
index 4581dd0d7ee7f35f52613fad6940076336554750..5c6a42e241888b343dd39c4cf8c80e047ce21c3d 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -838,6 +838,15 @@ export default new Router({
             }
           ]
         },
+        {
+          path: "uploads/:id",
+          name: "library.uploads.detail",
+          props: true,
+          component: () =>
+            import(
+              /* webpackChunkName: "uploads" */ "@/components/library/UploadDetail"
+            ),
+        },
         {
           // browse a single library via it's uuid
           path: ":id([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",