diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f3f787fecd58ec78d5937cca542cd0a3d6ef5679..a089db796581273303c53b1b9fec8ea973e2b700 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -203,7 +203,7 @@ pages:
     - cd docs
     - apt-get update
     - apt-get install -y graphviz
-    - pip install sphinx sphinx_rtd_theme
+    - pip install sphinx sphinx_rtd_theme django-environ django
   script:
     - ./build_docs.sh
   cache:
diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index b55cfe84af1523220c548b024eadd5f4aa0a5d62..675d3e8ce17fe4095890cd3e43278b07fd8fdb33 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -1302,3 +1302,10 @@ PODCASTS_RSS_FEED_MAX_ITEMS = env.int("PODCASTS_RSS_FEED_MAX_ITEMS", default=250
 """
 Maximum number of RSS items to load in each podcast feed.
 """
+
+IGNORE_FORWARDED_HOST_AND_PROTO = env.bool(
+    "IGNORE_FORWARDED_HOST_AND_PROTO", default=True
+)
+"""
+Use :attr:`FUNKWHALE_HOSTNAME` and :attr:`FUNKWHALE_PROTOCOL ` instead of request header.
+"""
diff --git a/api/funkwhale_api/common/apps.py b/api/funkwhale_api/common/apps.py
index cd671be291395b438ebd15a9caa42f53a81a51c6..7d94695a1f349c07d5f8e681af6c98ce42bc63e9 100644
--- a/api/funkwhale_api/common/apps.py
+++ b/api/funkwhale_api/common/apps.py
@@ -1,6 +1,7 @@
 from django.apps import AppConfig, apps
 
 from . import mutations
+from . import utils
 
 
 class CommonConfig(AppConfig):
@@ -11,3 +12,4 @@ class CommonConfig(AppConfig):
 
         app_names = [app.name for app in apps.app_configs.values()]
         mutations.registry.autodiscover(app_names)
+        utils.monkey_patch_request_build_absolute_uri()
diff --git a/api/funkwhale_api/common/utils.py b/api/funkwhale_api/common/utils.py
index b721bdf7b24c4219c330719a7e4e8679f9c4f34e..0a38ef05f0ea79dd11dc7d91fa418cb94adfb1a0 100644
--- a/api/funkwhale_api/common/utils.py
+++ b/api/funkwhale_api/common/utils.py
@@ -1,6 +1,7 @@
 import datetime
 
 from django.core.files.base import ContentFile
+from django.http import request
 from django.utils.deconstruct import deconstructible
 
 import bleach.sanitizer
@@ -433,3 +434,27 @@ def update_modification_date(obj, field="modification_date", date=None):
         obj.__class__.objects.filter(pk=obj.pk).update(**{field: date})
 
     return date
+
+
+def monkey_patch_request_build_absolute_uri():
+    """
+    Since we have FUNKWHALE_HOSTNAME and PROTOCOL hardcoded in settings, we can
+    override django's multisite logic which can break when reverse proxy aren't configured
+    properly.
+    """
+    builtin_scheme = request.HttpRequest.scheme
+
+    def scheme(self):
+        if settings.IGNORE_FORWARDED_HOST_AND_PROTO:
+            return settings.FUNKWHALE_PROTOCOL
+        return builtin_scheme.fget(self)
+
+    builtin_get_host = request.HttpRequest.get_host
+
+    def get_host(self):
+        if settings.IGNORE_FORWARDED_HOST_AND_PROTO:
+            return settings.FUNKWHALE_HOSTNAME
+        return builtin_get_host(self)
+
+    request.HttpRequest.scheme = property(scheme)
+    request.HttpRequest.get_host = get_host
diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py
index 0f0f16ce04815f03b071623d78be8aca358c25c5..adb7128e267334d66a8cc7231168a88c99e7ff96 100644
--- a/api/funkwhale_api/manage/views.py
+++ b/api/funkwhale_api/manage/views.py
@@ -84,8 +84,8 @@ class ManageArtistViewSet(
         music_models.Artist.objects.all()
         .order_by("-id")
         .select_related("attributed_to", "attachment_cover", "channel")
-        .annotate(_tracks_count=Count("tracks"))
-        .annotate(_albums_count=Count("albums"))
+        .annotate(_tracks_count=Count("tracks", distinct=True))
+        .annotate(_albums_count=Count("albums", distinct=True))
         .prefetch_related(music_views.TAG_PREFETCH)
     )
     serializer_class = serializers.ManageArtistSerializer
diff --git a/api/funkwhale_api/music/management/commands/fix_uploads.py b/api/funkwhale_api/music/management/commands/fix_uploads.py
index 94f8dd21c44e213f2726f65dc708ca41e393c2ce..582a837c47096ed8c81c684072abdf5593e962ac 100644
--- a/api/funkwhale_api/music/management/commands/fix_uploads.py
+++ b/api/funkwhale_api/music/management/commands/fix_uploads.py
@@ -16,20 +16,44 @@ class Command(BaseCommand):
             default=False,
             help="Do not execute anything",
         )
+        parser.add_argument(
+            "--mimetypes",
+            action="store_true",
+            dest="mimetypes",
+            default=True,
+            help="Check and fix mimetypes",
+        )
+        parser.add_argument(
+            "--audio-data",
+            action="store_true",
+            dest="data",
+            default=False,
+            help="Check and fix bitrate and duration, can be really slow because it needs to access files",
+        )
+        parser.add_argument(
+            "--size",
+            action="store_true",
+            dest="size",
+            default=False,
+            help="Check and fix file size, can be really slow because it needs to access files",
+        )
 
     def handle(self, *args, **options):
         if options["dry_run"]:
             self.stdout.write("Dry-run on, will not commit anything")
-        self.fix_mimetypes(**options)
-        self.fix_file_data(**options)
-        self.fix_file_size(**options)
+        if options["mimetypes"]:
+            self.fix_mimetypes(**options)
+        if options["data"]:
+            self.fix_file_data(**options)
+        if options["size"]:
+            self.fix_file_size(**options)
 
     @transaction.atomic
     def fix_mimetypes(self, dry_run, **kwargs):
         self.stdout.write("Fixing missing mimetypes...")
-        matching = models.Upload.objects.filter(source__startswith="file://").exclude(
-            mimetype__startswith="audio/"
-        )
+        matching = models.Upload.objects.filter(
+            Q(source__startswith="file://") | Q(source__startswith="upload://")
+        ).exclude(mimetype__startswith="audio/")
         self.stdout.write(
             "[mimetypes] {} entries found with bad or no mimetype".format(
                 matching.count()
diff --git a/api/funkwhale_api/music/utils.py b/api/funkwhale_api/music/utils.py
index 14f245aaa4d8fca84ef2c6e04452990b9a2d64d3..64a7c24f85b4d1910e77a8279f4b9df4bc97aee6 100644
--- a/api/funkwhale_api/music/utils.py
+++ b/api/funkwhale_api/music/utils.py
@@ -22,6 +22,8 @@ def guess_mimetype(f):
         mt, _ = mimetypes.guess_type(f.name)
         if mt:
             t = mt
+        else:
+            t = EXTENSION_TO_MIMETYPE.get(f.name.split(".")[-1])
     return t
 
 
diff --git a/api/funkwhale_api/subsonic/serializers.py b/api/funkwhale_api/subsonic/serializers.py
index fa6bc4475cb0d1887ac6688e201c49d6f4375aa3..d7042718af7194341c58e3b3bca78d0f80a5b8fc 100644
--- a/api/funkwhale_api/subsonic/serializers.py
+++ b/api/funkwhale_api/subsonic/serializers.py
@@ -130,7 +130,7 @@ def get_track_data(album, track, upload):
         data["bitrate"] = int(upload.bitrate / 1000)
     if upload.size:
         data["size"] = upload.size
-    if album.release_date:
+    if album and album.release_date:
         data["year"] = album.release_date.year
     else:
         data["year"] = track.creation_date.year
diff --git a/api/tests/common/test_utils.py b/api/tests/common/test_utils.py
index af2b25207413b2336527295652f8c55b3b102788..ffe9eae3b002771cb5516ae037005571f5d7b78c 100644
--- a/api/tests/common/test_utils.py
+++ b/api/tests/common/test_utils.py
@@ -197,3 +197,64 @@ def test_attach_file_content(factories, r_mock):
     assert new_attachment.file.read() == b"content"
     assert new_attachment.url is None
     assert new_attachment.mimetype == data["mimetype"]
+
+
+@pytest.mark.parametrize(
+    "ignore, hostname, protocol, meta, path, expected",
+    [
+        (
+            False,
+            "test.hostname",
+            "http",
+            {
+                "HTTP_X_FORWARDED_HOST": "real.hostname",
+                "HTTP_X_FORWARDED_PROTO": "https",
+            },
+            "/hello",
+            "https://real.hostname/hello",
+        ),
+        (
+            False,
+            "test.hostname",
+            "http",
+            {
+                "HTTP_X_FORWARDED_HOST": "real.hostname",
+                "HTTP_X_FORWARDED_PROTO": "http",
+            },
+            "/hello",
+            "http://real.hostname/hello",
+        ),
+        (
+            True,
+            "test.hostname",
+            "http",
+            {
+                "HTTP_X_FORWARDED_HOST": "real.hostname",
+                "HTTP_X_FORWARDED_PROTO": "https",
+            },
+            "/hello",
+            "http://test.hostname/hello",
+        ),
+        (
+            True,
+            "test.hostname",
+            "https",
+            {
+                "HTTP_X_FORWARDED_HOST": "real.hostname",
+                "HTTP_X_FORWARDED_PROTO": "http",
+            },
+            "/hello",
+            "https://test.hostname/hello",
+        ),
+    ],
+)
+def test_monkey_patch_request_build_absolute_uri(
+    ignore, hostname, protocol, meta, path, expected, fake_request, settings
+):
+    settings.IGNORE_FORWARDED_HOST_AND_PROTO = ignore
+    settings.ALLOWED_HOSTS = "*"
+    settings.FUNKWHALE_HOSTNAME = hostname
+    settings.FUNKWHALE_PROTOCOL = protocol
+    request = fake_request.get("/", **meta)
+
+    assert request.build_absolute_uri(path) == expected
diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py
index db39f571f35c23bd5aaf3174f57468e3dc491143..3d79c0eba1d2a8e25a15d3052af297551f080926 100644
--- a/api/tests/music/test_views.py
+++ b/api/tests/music/test_views.py
@@ -824,6 +824,7 @@ def test_user_can_create_draft_upload(
     assert upload.source == "upload://test"
     assert upload.import_reference == "test"
     assert upload.import_status == "draft"
+    assert upload.mimetype == "audio/ogg"
     assert upload.track is None
     m.assert_not_called()
 
diff --git a/changes/changelog.d/1082.bugfix b/changes/changelog.d/1082.bugfix
new file mode 100644
index 0000000000000000000000000000000000000000..b99f0c1f0aa9c3984ee0cb4c4d18e5a3988663d3
--- /dev/null
+++ b/changes/changelog.d/1082.bugfix
@@ -0,0 +1 @@
+Fixed issue when displaying starred tracks on subsonic (#1082)
diff --git a/changes/changelog.d/1085.enhancement b/changes/changelog.d/1085.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..49cbee6f85eef04eba6d776fbe78186fdd72b423
--- /dev/null
+++ b/changes/changelog.d/1085.enhancement
@@ -0,0 +1 @@
+Make URL-building logic more resilient against reverse proxy misconfiguration (#1085)
diff --git a/changes/changelog.d/1093.bugfix b/changes/changelog.d/1093.bugfix
new file mode 100644
index 0000000000000000000000000000000000000000..1d9a15e59171bbd412da829645095cb6228a9c64
--- /dev/null
+++ b/changes/changelog.d/1093.bugfix
@@ -0,0 +1 @@
+Fixed mimetype detection issue that broke transcoding on some tracks (#1093). Run ``python manage.py fix_uploads --mimetypes`` to set proper mimetypes on existing uploads.
diff --git a/changes/changelog.d/1096.bugfix b/changes/changelog.d/1096.bugfix
new file mode 100644
index 0000000000000000000000000000000000000000..81ffb284d9a59783606b09c69baa4d00b2e11983
--- /dev/null
+++ b/changes/changelog.d/1096.bugfix
@@ -0,0 +1 @@
+Fixed wrong album and track count in admin artist API (#1096)