Merge branch 'refetch-remote-entities' into 'develop'

Added "refresh=true" API param to artist, track and album detail to retrieve up-to-date data if needed

See merge request funkwhale/funkwhale!837
This commit is contained in:
Eliot Berriot 2019-07-24 09:36:15 +02:00
commit c885c10be1
9 changed files with 191 additions and 3 deletions

View file

@ -202,6 +202,11 @@ class Artist(APIModelMixin):
related_name="attributed_artists",
)
tagged_items = GenericRelation(tags_models.TaggedItem)
fetches = GenericRelation(
"federation.Fetch",
content_type_field="object_content_type",
object_id_field="object_id",
)
api = musicbrainz.api.artists
objects = ArtistQuerySet.as_manager()
@ -282,6 +287,11 @@ class Album(APIModelMixin):
related_name="attributed_albums",
)
tagged_items = GenericRelation(tags_models.TaggedItem)
fetches = GenericRelation(
"federation.Fetch",
content_type_field="object_content_type",
object_id_field="object_id",
)
api_includes = ["artist-credits", "recordings", "media", "release-groups"]
api = musicbrainz.api.releases
@ -463,6 +473,11 @@ class Track(APIModelMixin):
import_hooks = [import_tags]
objects = TrackQuerySet.as_manager()
tagged_items = GenericRelation(tags_models.TaggedItem)
fetches = GenericRelation(
"federation.Fetch",
content_type_field="object_content_type",
object_id_field="object_id",
)
class Meta:
ordering = ["album", "disc_number", "position"]

View file

@ -1,3 +1,4 @@
import datetime
import logging
import urllib
@ -21,7 +22,9 @@ from funkwhale_api.federation.authentication import SignatureAuthentication
from funkwhale_api.federation import actors
from funkwhale_api.federation import api_serializers as federation_api_serializers
from funkwhale_api.federation import decorators as federation_decorators
from funkwhale_api.federation import models as federation_models
from funkwhale_api.federation import routes
from funkwhale_api.federation import tasks as federation_tasks
from funkwhale_api.tags.models import Tag, TaggedItem
from funkwhale_api.tags.serializers import TagSerializer
from funkwhale_api.users.oauth import permissions as oauth_permissions
@ -59,6 +62,37 @@ def get_libraries(filter_uploads):
return libraries
def refetch_obj(obj, queryset):
"""
Given an Artist/Album/Track instance, if the instance is from a remote pod,
will attempt to update local data with the latest ActivityPub representation.
"""
if obj.is_local:
return obj
now = timezone.now()
limit = now - datetime.timedelta(minutes=settings.FEDERATION_OBJECT_FETCH_DELAY)
last_fetch = obj.fetches.order_by("-creation_date").first()
if last_fetch is not None and last_fetch.creation_date > limit:
# we fetched recently, no need to do it again
return obj
logger.info("Refetching %s:%s at %s", obj._meta.label, obj.pk, obj.fid)
actor = actors.get_service_actor()
fetch = federation_models.Fetch.objects.create(actor=actor, url=obj.fid, object=obj)
try:
federation_tasks.fetch(fetch_id=fetch.pk)
except Exception:
logger.exception(
"Error while refetching %s:%s at %s", obj._meta.label, obj.pk, obj.fid
)
else:
fetch.refresh_from_db()
if fetch.status == "finished":
obj = queryset.get(pk=obj.pk)
return obj
class ArtistViewSet(common_views.SkipFilterForGetObject, viewsets.ReadOnlyModelViewSet):
queryset = models.Artist.objects.all().select_related("attributed_to")
serializer_class = serializers.ArtistWithAlbumsSerializer
@ -71,6 +105,16 @@ class ArtistViewSet(common_views.SkipFilterForGetObject, viewsets.ReadOnlyModelV
fetches = federation_decorators.fetches_route()
mutations = common_decorators.mutations_route(types=["update"])
def get_object(self):
obj = super().get_object()
if (
self.action == "retrieve"
and self.request.GET.get("refresh", "").lower() == "true"
):
obj = refetch_obj(obj, self.get_queryset())
return obj
def get_queryset(self):
queryset = super().get_queryset()
albums = models.Album.objects.with_tracks_count()
@ -106,6 +150,16 @@ class AlbumViewSet(common_views.SkipFilterForGetObject, viewsets.ReadOnlyModelVi
fetches = federation_decorators.fetches_route()
mutations = common_decorators.mutations_route(types=["update"])
def get_object(self):
obj = super().get_object()
if (
self.action == "retrieve"
and self.request.GET.get("refresh", "").lower() == "true"
):
obj = refetch_obj(obj, self.get_queryset())
return obj
def get_queryset(self):
queryset = super().get_queryset()
tracks = (
@ -212,6 +266,16 @@ class TrackViewSet(common_views.SkipFilterForGetObject, viewsets.ReadOnlyModelVi
fetches = federation_decorators.fetches_route()
mutations = common_decorators.mutations_route(types=["update"])
def get_object(self):
obj = super().get_object()
if (
self.action == "retrieve"
and self.request.GET.get("refresh", "").lower() == "true"
):
obj = refetch_obj(obj, self.get_queryset())
return obj
def get_queryset(self):
queryset = super().get_queryset()
filter_favorites = self.request.GET.get("favorites", None)