Merge branch '141-timeline-first-load' into 'develop'
Resolve "Populate timeline on first load" Closes #141 See merge request funkwhale/funkwhale!109
This commit is contained in:
commit
cc95e2f1f6
|
@ -1,5 +1,6 @@
|
|||
from rest_framework import routers
|
||||
from django.conf.urls import include, url
|
||||
from funkwhale_api.activity import views as activity_views
|
||||
from funkwhale_api.instance import views as instance_views
|
||||
from funkwhale_api.music import views
|
||||
from funkwhale_api.playlists import views as playlists_views
|
||||
|
@ -10,6 +11,7 @@ from dynamic_preferences.users.viewsets import UserPreferencesViewSet
|
|||
|
||||
router = routers.SimpleRouter()
|
||||
router.register(r'settings', GlobalPreferencesViewSet, base_name='settings')
|
||||
router.register(r'activity', activity_views.ActivityViewSet, 'activity')
|
||||
router.register(r'tags', views.TagViewSet, 'tags')
|
||||
router.register(r'tracks', views.TrackViewSet, 'tracks')
|
||||
router.register(r'trackfiles', views.TrackFileViewSet, 'trackfiles')
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from funkwhale_api.activity import record
|
||||
|
||||
|
||||
class ModelSerializer(serializers.ModelSerializer):
|
||||
id = serializers.CharField(source='get_activity_url')
|
||||
|
@ -8,3 +10,15 @@ class ModelSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_url(self, obj):
|
||||
return self.get_id(obj)
|
||||
|
||||
|
||||
class AutoSerializer(serializers.Serializer):
|
||||
"""
|
||||
A serializer that will automatically use registered activity serializers
|
||||
to serialize an henerogeneous list of objects (favorites, listenings, etc.)
|
||||
"""
|
||||
def to_representation(self, instance):
|
||||
serializer = record.registry[instance._meta.label]['serializer'](
|
||||
instance
|
||||
)
|
||||
return serializer.data
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
from django.db import models
|
||||
|
||||
from funkwhale_api.common import fields
|
||||
from funkwhale_api.favorites.models import TrackFavorite
|
||||
from funkwhale_api.history.models import Listening
|
||||
|
||||
|
||||
def combined_recent(limit, **kwargs):
|
||||
datetime_field = kwargs.pop('datetime_field', 'creation_date')
|
||||
source_querysets = {
|
||||
qs.model._meta.label: qs for qs in kwargs.pop('querysets')
|
||||
}
|
||||
querysets = {
|
||||
k: qs.annotate(
|
||||
__type=models.Value(
|
||||
qs.model._meta.label, output_field=models.CharField()
|
||||
)
|
||||
).values('pk', datetime_field, '__type')
|
||||
for k, qs in source_querysets.items()
|
||||
}
|
||||
_qs_list = list(querysets.values())
|
||||
union_qs = _qs_list[0].union(*_qs_list[1:])
|
||||
records = []
|
||||
for row in union_qs.order_by('-{}'.format(datetime_field))[:limit]:
|
||||
records.append({
|
||||
'type': row['__type'],
|
||||
'when': row[datetime_field],
|
||||
'pk': row['pk']
|
||||
})
|
||||
# Now we bulk-load each object type in turn
|
||||
to_load = {}
|
||||
for record in records:
|
||||
to_load.setdefault(record['type'], []).append(record['pk'])
|
||||
fetched = {}
|
||||
|
||||
for key, pks in to_load.items():
|
||||
for item in source_querysets[key].filter(pk__in=pks):
|
||||
fetched[(key, item.pk)] = item
|
||||
|
||||
# Annotate 'records' with loaded objects
|
||||
for record in records:
|
||||
record['object'] = fetched[(record['type'], record['pk'])]
|
||||
return records
|
||||
|
||||
|
||||
def get_activity(user, limit=20):
|
||||
query = fields.privacy_level_query(
|
||||
user, lookup_field='user__privacy_level')
|
||||
querysets = [
|
||||
Listening.objects.filter(query).select_related(
|
||||
'track',
|
||||
'user',
|
||||
'track__artist',
|
||||
'track__album__artist',
|
||||
),
|
||||
TrackFavorite.objects.filter(query).select_related(
|
||||
'track',
|
||||
'user',
|
||||
'track__artist',
|
||||
'track__album__artist',
|
||||
),
|
||||
]
|
||||
records = combined_recent(limit=limit, querysets=querysets)
|
||||
return [r['object'] for r in records]
|
|
@ -0,0 +1,20 @@
|
|||
from rest_framework import viewsets
|
||||
from rest_framework.response import Response
|
||||
|
||||
from funkwhale_api.common.permissions import ConditionalAuthentication
|
||||
from funkwhale_api.favorites.models import TrackFavorite
|
||||
|
||||
from . import serializers
|
||||
from . import utils
|
||||
|
||||
|
||||
class ActivityViewSet(viewsets.GenericViewSet):
|
||||
|
||||
serializer_class = serializers.AutoSerializer
|
||||
permission_classes = [ConditionalAuthentication]
|
||||
queryset = TrackFavorite.objects.none()
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
activity = utils.get_activity(user=request.user)
|
||||
serializer = self.serializer_class(activity, many=True)
|
||||
return Response({'results': serializer.data}, status=200)
|
|
@ -22,6 +22,6 @@ def privacy_level_query(user, lookup_field='privacy_level'):
|
|||
|
||||
return models.Q(**{
|
||||
'{}__in'.format(lookup_field): [
|
||||
'me', 'followers', 'instance', 'everyone'
|
||||
'followers', 'instance', 'everyone'
|
||||
]
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ from . import models
|
|||
|
||||
@admin.register(models.Listening)
|
||||
class ListeningAdmin(admin.ModelAdmin):
|
||||
list_display = ['track', 'end_date', 'user', 'session_key']
|
||||
list_display = ['track', 'creation_date', 'user', 'session_key']
|
||||
search_fields = ['track__name', 'user__username']
|
||||
list_select_related = [
|
||||
'user',
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 2.0.3 on 2018-03-25 14:33
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('history', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='listening',
|
||||
options={'ordering': ('-creation_date',)},
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='listening',
|
||||
old_name='end_date',
|
||||
new_name='creation_date',
|
||||
),
|
||||
]
|
|
@ -6,7 +6,8 @@ from funkwhale_api.music.models import Track
|
|||
|
||||
|
||||
class Listening(models.Model):
|
||||
end_date = models.DateTimeField(default=timezone.now, null=True, blank=True)
|
||||
creation_date = models.DateTimeField(
|
||||
default=timezone.now, null=True, blank=True)
|
||||
track = models.ForeignKey(
|
||||
Track, related_name="listenings", on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(
|
||||
|
@ -18,7 +19,7 @@ class Listening(models.Model):
|
|||
session_key = models.CharField(max_length=100, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ('-end_date',)
|
||||
ordering = ('-creation_date',)
|
||||
|
||||
def save(self, **kwargs):
|
||||
if not self.user and not self.session_key:
|
||||
|
|
|
@ -12,7 +12,7 @@ class ListeningActivitySerializer(activity_serializers.ModelSerializer):
|
|||
type = serializers.SerializerMethodField()
|
||||
object = TrackActivitySerializer(source='track')
|
||||
actor = UserActivitySerializer(source='user')
|
||||
published = serializers.DateTimeField(source='end_date')
|
||||
published = serializers.DateTimeField(source='creation_date')
|
||||
|
||||
class Meta:
|
||||
model = models.Listening
|
||||
|
@ -36,7 +36,7 @@ class ListeningSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = models.Listening
|
||||
fields = ('id', 'user', 'session_key', 'track', 'end_date')
|
||||
fields = ('id', 'user', 'session_key', 'track', 'creation_date')
|
||||
|
||||
|
||||
def create(self, validated_data):
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
from funkwhale_api.activity import serializers
|
||||
from funkwhale_api.favorites.serializers import TrackFavoriteActivitySerializer
|
||||
from funkwhale_api.history.serializers import \
|
||||
ListeningActivitySerializer
|
||||
|
||||
|
||||
def test_autoserializer(factories):
|
||||
favorite = factories['favorites.TrackFavorite']()
|
||||
listening = factories['history.Listening']()
|
||||
objects = [favorite, listening]
|
||||
serializer = serializers.AutoSerializer(objects, many=True)
|
||||
expected = [
|
||||
TrackFavoriteActivitySerializer(favorite).data,
|
||||
ListeningActivitySerializer(listening).data,
|
||||
]
|
||||
|
||||
assert serializer.data == expected
|
|
@ -0,0 +1,21 @@
|
|||
from funkwhale_api.activity import utils
|
||||
|
||||
|
||||
def test_get_activity(factories):
|
||||
user = factories['users.User']()
|
||||
listening = factories['history.Listening']()
|
||||
favorite = factories['favorites.TrackFavorite']()
|
||||
|
||||
objects = list(utils.get_activity(user))
|
||||
assert objects == [favorite, listening]
|
||||
|
||||
|
||||
def test_get_activity_honors_privacy_level(factories, anonymous_user):
|
||||
listening = factories['history.Listening'](user__privacy_level='me')
|
||||
favorite1 = factories['favorites.TrackFavorite'](
|
||||
user__privacy_level='everyone')
|
||||
favorite2 = factories['favorites.TrackFavorite'](
|
||||
user__privacy_level='instance')
|
||||
|
||||
objects = list(utils.get_activity(anonymous_user))
|
||||
assert objects == [favorite1]
|
|
@ -0,0 +1,18 @@
|
|||
from django.urls import reverse
|
||||
|
||||
from funkwhale_api.activity import serializers
|
||||
from funkwhale_api.activity import utils
|
||||
|
||||
|
||||
def test_activity_view(factories, api_client, settings, anonymous_user):
|
||||
settings.API_AUTHENTICATION_REQUIRED = False
|
||||
favorite = factories['favorites.TrackFavorite'](
|
||||
user__privacy_level='everyone')
|
||||
listening = factories['history.Listening']()
|
||||
url = reverse('api:v1:activity-list')
|
||||
objects = utils.get_activity(anonymous_user)
|
||||
serializer = serializers.AutoSerializer(objects, many=True)
|
||||
response = api_client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data['results'] == serializer.data
|
|
@ -10,7 +10,7 @@ from funkwhale_api.users.factories import UserFactory
|
|||
@pytest.mark.parametrize('user,expected', [
|
||||
(AnonymousUser(), Q(privacy_level='everyone')),
|
||||
(UserFactory.build(pk=1),
|
||||
Q(privacy_level__in=['me', 'followers', 'instance', 'everyone'])),
|
||||
Q(privacy_level__in=['followers', 'instance', 'everyone'])),
|
||||
])
|
||||
def test_privacy_level_query(user,expected):
|
||||
query = fields.privacy_level_query(user)
|
||||
|
|
|
@ -2,7 +2,6 @@ import pytest
|
|||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.http import Http404
|
||||
|
||||
from funkwhale_api.common import permissions
|
||||
|
@ -19,24 +18,26 @@ def test_owner_permission_owner_field_ok(nodb_factories, api_request):
|
|||
assert check is True
|
||||
|
||||
|
||||
def test_owner_permission_owner_field_not_ok(nodb_factories, api_request):
|
||||
def test_owner_permission_owner_field_not_ok(
|
||||
anonymous_user, nodb_factories, api_request):
|
||||
playlist = nodb_factories['playlists.Playlist']()
|
||||
view = APIView.as_view()
|
||||
permission = permissions.OwnerPermission()
|
||||
request = api_request.get('/')
|
||||
setattr(request, 'user', AnonymousUser())
|
||||
setattr(request, 'user', anonymous_user)
|
||||
|
||||
with pytest.raises(Http404):
|
||||
permission.has_object_permission(request, view, playlist)
|
||||
|
||||
|
||||
def test_owner_permission_read_only(nodb_factories, api_request):
|
||||
def test_owner_permission_read_only(
|
||||
anonymous_user, nodb_factories, api_request):
|
||||
playlist = nodb_factories['playlists.Playlist']()
|
||||
view = APIView.as_view()
|
||||
setattr(view, 'owner_checks', ['write'])
|
||||
permission = permissions.OwnerPermission()
|
||||
request = api_request.get('/')
|
||||
setattr(request, 'user', AnonymousUser())
|
||||
setattr(request, 'user', anonymous_user)
|
||||
check = permission.has_object_permission(request, view, playlist)
|
||||
|
||||
assert check is True
|
||||
|
|
|
@ -3,6 +3,7 @@ import tempfile
|
|||
import shutil
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.cache import cache as django_cache
|
||||
from dynamic_preferences.registries import global_preferences_registry
|
||||
|
||||
|
@ -66,6 +67,11 @@ def logged_in_client(db, factories, client):
|
|||
delattr(client, 'user')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anonymous_user():
|
||||
return AnonymousUser()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client(client):
|
||||
return APIClient()
|
||||
|
@ -126,3 +132,11 @@ def activity_registry():
|
|||
@pytest.fixture
|
||||
def activity_muted(activity_registry, mocker):
|
||||
yield mocker.patch.object(record, 'send')
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def media_root(settings):
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
settings.MEDIA_ROOT = tmp_dir
|
||||
yield settings.MEDIA_ROOT
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
|
|
@ -23,7 +23,7 @@ def test_activity_listening_serializer(factories):
|
|||
"id": listening.get_activity_url(),
|
||||
"actor": actor,
|
||||
"object": TrackActivitySerializer(listening.track).data,
|
||||
"published": field.to_representation(listening.end_date),
|
||||
"published": field.to_representation(listening.creation_date),
|
||||
}
|
||||
|
||||
data = serializers.ListeningActivitySerializer(listening).data
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,502 +0,0 @@
|
|||
artists = {'search': {}, 'get': {}}
|
||||
artists['search']['adhesive_wombat'] = {
|
||||
'artist-list': [
|
||||
{
|
||||
'type': 'Person',
|
||||
'ext:score': '100',
|
||||
'id': '62c3befb-6366-4585-b256-809472333801',
|
||||
'disambiguation': 'George Shaw',
|
||||
'gender': 'male',
|
||||
'area': {'sort-name': 'Raleigh', 'id': '3f8828b9-ba93-4604-9b92-1f616fa1abd1', 'name': 'Raleigh'},
|
||||
'sort-name': 'Wombat, Adhesive',
|
||||
'life-span': {'ended': 'false'},
|
||||
'name': 'Adhesive Wombat'
|
||||
},
|
||||
{
|
||||
'country': 'SE',
|
||||
'type': 'Group',
|
||||
'ext:score': '42',
|
||||
'id': '61b34e69-7573-4208-bc89-7061bca5a8fc',
|
||||
'area': {'sort-name': 'Sweden', 'id': '23d10872-f5ae-3f0c-bf55-332788a16ecb', 'name': 'Sweden'},
|
||||
'sort-name': 'Adhesive',
|
||||
'life-span': {'end': '2002-07-12', 'begin': '1994', 'ended': 'true'},
|
||||
'name': 'Adhesive',
|
||||
'begin-area': {
|
||||
'sort-name': 'Katrineholm',
|
||||
'id': '02390d96-b5a3-4282-a38f-e64a95d08b7f',
|
||||
'name': 'Katrineholm'
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
artists['get']['adhesive_wombat'] = {'artist': artists['search']['adhesive_wombat']['artist-list'][0]}
|
||||
|
||||
artists['get']['soad'] = {
|
||||
'artist': {
|
||||
'country': 'US',
|
||||
'isni-list': ['0000000121055332'],
|
||||
'type': 'Group',
|
||||
'area': {
|
||||
'iso-3166-1-code-list': ['US'],
|
||||
'sort-name': 'United States',
|
||||
'id': '489ce91b-6658-3307-9877-795b68554c98',
|
||||
'name': 'United States'
|
||||
},
|
||||
'begin-area': {
|
||||
'sort-name': 'Glendale',
|
||||
'id': '6db2e45d-d7f3-43da-ac0b-7ba5ca627373',
|
||||
'name': 'Glendale'
|
||||
},
|
||||
'id': 'cc0b7089-c08d-4c10-b6b0-873582c17fd6',
|
||||
'life-span': {'begin': '1994'},
|
||||
'sort-name': 'System of a Down',
|
||||
'name': 'System of a Down'
|
||||
}
|
||||
}
|
||||
|
||||
albums = {'search': {}, 'get': {}, 'get_with_includes': {}}
|
||||
albums['search']['hypnotize'] = {
|
||||
'release-list': [
|
||||
{
|
||||
"artist-credit": [
|
||||
{
|
||||
"artist": {
|
||||
"alias-list": [
|
||||
{
|
||||
"alias": "SoaD",
|
||||
"sort-name": "SoaD",
|
||||
"type": "Search hint"
|
||||
},
|
||||
{
|
||||
"alias": "S.O.A.D.",
|
||||
"sort-name": "S.O.A.D.",
|
||||
"type": "Search hint"
|
||||
},
|
||||
{
|
||||
"alias": "System Of Down",
|
||||
"sort-name": "System Of Down",
|
||||
"type": "Search hint"
|
||||
}
|
||||
],
|
||||
"id": "cc0b7089-c08d-4c10-b6b0-873582c17fd6",
|
||||
"name": "System of a Down",
|
||||
"sort-name": "System of a Down"
|
||||
}
|
||||
}
|
||||
],
|
||||
"artist-credit-phrase": "System of a Down",
|
||||
"barcode": "",
|
||||
"country": "US",
|
||||
"date": "2005",
|
||||
"ext:score": "100",
|
||||
"id": "47ae093f-1607-49a3-be11-a15d335ccc94",
|
||||
"label-info-list": [
|
||||
{
|
||||
"catalog-number": "8-2796-93871-2",
|
||||
"label": {
|
||||
"id": "f5be9cfe-e1af-405c-a074-caeaed6797c0",
|
||||
"name": "American Recordings"
|
||||
}
|
||||
},
|
||||
{
|
||||
"catalog-number": "D162990",
|
||||
"label": {
|
||||
"id": "9a7d39a4-a887-40f3-a645-a9a136d1f13f",
|
||||
"name": "BMG Direct Marketing, Inc."
|
||||
}
|
||||
}
|
||||
],
|
||||
"medium-count": 1,
|
||||
"medium-list": [
|
||||
{
|
||||
"disc-count": 1,
|
||||
"disc-list": [],
|
||||
"format": "CD",
|
||||
"track-count": 12,
|
||||
"track-list": []
|
||||
}
|
||||
],
|
||||
"medium-track-count": 12,
|
||||
"packaging": "Digipak",
|
||||
"release-event-list": [
|
||||
{
|
||||
"area": {
|
||||
"id": "489ce91b-6658-3307-9877-795b68554c98",
|
||||
"iso-3166-1-code-list": [
|
||||
"US"
|
||||
],
|
||||
"name": "United States",
|
||||
"sort-name": "United States"
|
||||
},
|
||||
"date": "2005"
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"id": "72035143-d6ec-308b-8ee5-070b8703902a",
|
||||
"primary-type": "Album",
|
||||
"type": "Album"
|
||||
},
|
||||
"status": "Official",
|
||||
"text-representation": {
|
||||
"language": "eng",
|
||||
"script": "Latn"
|
||||
},
|
||||
"title": "Hypnotize"
|
||||
},
|
||||
{
|
||||
"artist-credit": [
|
||||
{
|
||||
"artist": {
|
||||
"alias-list": [
|
||||
{
|
||||
"alias": "SoaD",
|
||||
"sort-name": "SoaD",
|
||||
"type": "Search hint"
|
||||
},
|
||||
{
|
||||
"alias": "S.O.A.D.",
|
||||
"sort-name": "S.O.A.D.",
|
||||
"type": "Search hint"
|
||||
},
|
||||
{
|
||||
"alias": "System Of Down",
|
||||
"sort-name": "System Of Down",
|
||||
"type": "Search hint"
|
||||
}
|
||||
],
|
||||
"id": "cc0b7089-c08d-4c10-b6b0-873582c17fd6",
|
||||
"name": "System of a Down",
|
||||
"sort-name": "System of a Down"
|
||||
}
|
||||
}
|
||||
],
|
||||
"artist-credit-phrase": "System of a Down",
|
||||
"asin": "B000C6NRY8",
|
||||
"barcode": "827969387115",
|
||||
"country": "US",
|
||||
"date": "2005-12-20",
|
||||
"ext:score": "100",
|
||||
"id": "8a4034a9-7834-3b7e-a6f0-d0791e3731fb",
|
||||
"medium-count": 1,
|
||||
"medium-list": [
|
||||
{
|
||||
"disc-count": 0,
|
||||
"disc-list": [],
|
||||
"format": "Vinyl",
|
||||
"track-count": 12,
|
||||
"track-list": []
|
||||
}
|
||||
],
|
||||
"medium-track-count": 12,
|
||||
"release-event-list": [
|
||||
{
|
||||
"area": {
|
||||
"id": "489ce91b-6658-3307-9877-795b68554c98",
|
||||
"iso-3166-1-code-list": [
|
||||
"US"
|
||||
],
|
||||
"name": "United States",
|
||||
"sort-name": "United States"
|
||||
},
|
||||
"date": "2005-12-20"
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"id": "72035143-d6ec-308b-8ee5-070b8703902a",
|
||||
"primary-type": "Album",
|
||||
"type": "Album"
|
||||
},
|
||||
"status": "Official",
|
||||
"text-representation": {
|
||||
"language": "eng",
|
||||
"script": "Latn"
|
||||
},
|
||||
"title": "Hypnotize"
|
||||
},
|
||||
]
|
||||
}
|
||||
albums['get']['hypnotize'] = {'release': albums['search']['hypnotize']['release-list'][0]}
|
||||
albums['get_with_includes']['hypnotize'] = {
|
||||
'release': {
|
||||
'artist-credit': [
|
||||
{'artist': {'id': 'cc0b7089-c08d-4c10-b6b0-873582c17fd6',
|
||||
'name': 'System of a Down',
|
||||
'sort-name': 'System of a Down'}}],
|
||||
'artist-credit-phrase': 'System of a Down',
|
||||
'barcode': '',
|
||||
'country': 'US',
|
||||
'cover-art-archive': {'artwork': 'true',
|
||||
'back': 'false',
|
||||
'count': '1',
|
||||
'front': 'true'},
|
||||
'date': '2005',
|
||||
'id': '47ae093f-1607-49a3-be11-a15d335ccc94',
|
||||
'medium-count': 1,
|
||||
'medium-list': [{'format': 'CD',
|
||||
'position': '1',
|
||||
'track-count': 12,
|
||||
'track-list': [{'id': '59f5cf9a-75b2-3aa3-abda-6807a87107b3',
|
||||
'length': '186000',
|
||||
'number': '1',
|
||||
'position': '1',
|
||||
'recording': {'id': '76d03fc5-758c-48d0-a354-a67de086cc68',
|
||||
'length': '186000',
|
||||
'title': 'Attack'},
|
||||
'track_or_recording_length': '186000'},
|
||||
{'id': '3aaa28c1-12b1-3c2a-b90a-82e09e355608',
|
||||
'length': '239000',
|
||||
'number': '2',
|
||||
'position': '2',
|
||||
'recording': {'id': '327543b0-9193-48c5-83c9-01c7b36c8c0a',
|
||||
'length': '239000',
|
||||
'title': 'Dreaming'},
|
||||
'track_or_recording_length': '239000'},
|
||||
{'id': 'a34fef19-e637-3436-b7eb-276ff2814d6f',
|
||||
'length': '147000',
|
||||
'number': '3',
|
||||
'position': '3',
|
||||
'recording': {'id': '6e27866c-07a1-425d-bb4f-9d9e728db344',
|
||||
'length': '147000',
|
||||
'title': 'Kill Rock ’n Roll'},
|
||||
'track_or_recording_length': '147000'},
|
||||
{'id': '72a4e5c0-c150-3ba1-9ceb-3ab82648af25',
|
||||
'length': '189000',
|
||||
'number': '4',
|
||||
'position': '4',
|
||||
'recording': {'id': '7ff8a67d-c8e2-4b3a-a045-7ad3561d0605',
|
||||
'length': '189000',
|
||||
'title': 'Hypnotize'},
|
||||
'track_or_recording_length': '189000'},
|
||||
{'id': 'a748fa6e-b3b7-3b22-89fb-a038ec92ac32',
|
||||
'length': '178000',
|
||||
'number': '5',
|
||||
'position': '5',
|
||||
'recording': {'id': '19b6eb6a-0e76-4ef7-b63f-959339dbd5d2',
|
||||
'length': '178000',
|
||||
'title': 'Stealing Society'},
|
||||
'track_or_recording_length': '178000'},
|
||||
{'id': '5c5a8d4e-e21a-317e-a719-6e2dbdefa5d2',
|
||||
'length': '216000',
|
||||
'number': '6',
|
||||
'position': '6',
|
||||
'recording': {'id': 'c3c2afe1-ee9a-47cb-b3c6-ff8100bc19d5',
|
||||
'length': '216000',
|
||||
'title': 'Tentative'},
|
||||
'track_or_recording_length': '216000'},
|
||||
{'id': '265718ba-787f-3193-947b-3b6fa69ffe96',
|
||||
'length': '175000',
|
||||
'number': '7',
|
||||
'position': '7',
|
||||
'recording': {'id': '96f804e1-f600-4faa-95a6-ce597e7db120',
|
||||
'length': '175000',
|
||||
'title': 'U‐Fig'},
|
||||
'title': 'U-Fig',
|
||||
'track_or_recording_length': '175000'},
|
||||
{'id': 'cdcf8572-3060-31ca-a72c-1ded81ca1f7a',
|
||||
'length': '328000',
|
||||
'number': '8',
|
||||
'position': '8',
|
||||
'recording': {'id': '26ba38f0-b26b-48b7-8e77-226b22a55f79',
|
||||
'length': '328000',
|
||||
'title': 'Holy Mountains'},
|
||||
'track_or_recording_length': '328000'},
|
||||
{'id': 'f9f00cb0-5635-3217-a2a0-bd61917eb0df',
|
||||
'length': '171000',
|
||||
'number': '9',
|
||||
'position': '9',
|
||||
'recording': {'id': '039f3379-3a69-4e75-a882-df1c4e1608aa',
|
||||
'length': '171000',
|
||||
'title': 'Vicinity of Obscenity'},
|
||||
'track_or_recording_length': '171000'},
|
||||
{'id': 'cdd45914-6741-353e-bbb5-d281048ff24f',
|
||||
'length': '164000',
|
||||
'number': '10',
|
||||
'position': '10',
|
||||
'recording': {'id': 'c24d541a-a9a8-4a22-84c6-5e6419459cf8',
|
||||
'length': '164000',
|
||||
'title': 'She’s Like Heroin'},
|
||||
'track_or_recording_length': '164000'},
|
||||
{'id': 'cfcf12ac-6831-3dd6-a2eb-9d0bfeee3f6d',
|
||||
'length': '167000',
|
||||
'number': '11',
|
||||
'position': '11',
|
||||
'recording': {'id': '0aff4799-849f-4f83-84f4-22cabbba2378',
|
||||
'length': '167000',
|
||||
'title': 'Lonely Day'},
|
||||
'track_or_recording_length': '167000'},
|
||||
{'id': '7e38bb38-ff62-3e41-a670-b7d77f578a1f',
|
||||
'length': '220000',
|
||||
'number': '12',
|
||||
'position': '12',
|
||||
'recording': {'id': 'e1b4d90f-2f44-4fe6-a826-362d4e3d9b88',
|
||||
'length': '220000',
|
||||
'title': 'Soldier Side'},
|
||||
'track_or_recording_length': '220000'}]}],
|
||||
'packaging': 'Digipak',
|
||||
'quality': 'normal',
|
||||
'release-event-count': 1,
|
||||
'release-event-list': [{'area': {'id': '489ce91b-6658-3307-9877-795b68554c98',
|
||||
'iso-3166-1-code-list': ['US'],
|
||||
'name': 'United States',
|
||||
'sort-name': 'United States'},
|
||||
'date': '2005'}],
|
||||
'status': 'Official',
|
||||
'text-representation': {'language': 'eng', 'script': 'Latn'},
|
||||
'title': 'Hypnotize'}}
|
||||
|
||||
albums['get']['marsupial'] = {
|
||||
'release': {
|
||||
"artist-credit": [
|
||||
{
|
||||
"artist": {
|
||||
"disambiguation": "George Shaw",
|
||||
"id": "62c3befb-6366-4585-b256-809472333801",
|
||||
"name": "Adhesive Wombat",
|
||||
"sort-name": "Wombat, Adhesive"
|
||||
}
|
||||
}
|
||||
],
|
||||
"artist-credit-phrase": "Adhesive Wombat",
|
||||
"country": "XW",
|
||||
"cover-art-archive": {
|
||||
"artwork": "true",
|
||||
"back": "false",
|
||||
"count": "1",
|
||||
"front": "true"
|
||||
},
|
||||
"date": "2013-06-05",
|
||||
"id": "a50d2a81-2a50-484d-9cb4-b9f6833f583e",
|
||||
"packaging": "None",
|
||||
"quality": "normal",
|
||||
"release-event-count": 1,
|
||||
"release-event-list": [
|
||||
{
|
||||
"area": {
|
||||
"id": "525d4e18-3d00-31b9-a58b-a146a916de8f",
|
||||
"iso-3166-1-code-list": [
|
||||
"XW"
|
||||
],
|
||||
"name": "[Worldwide]",
|
||||
"sort-name": "[Worldwide]"
|
||||
},
|
||||
"date": "2013-06-05"
|
||||
}
|
||||
],
|
||||
"status": "Official",
|
||||
"text-representation": {
|
||||
"language": "eng",
|
||||
"script": "Latn"
|
||||
},
|
||||
"title": "Marsupial Madness"
|
||||
}
|
||||
}
|
||||
|
||||
tracks = {'search': {}, 'get': {}}
|
||||
|
||||
tracks['search']['8bitadventures'] = {
|
||||
'recording-list': [
|
||||
{
|
||||
"artist-credit": [
|
||||
{
|
||||
"artist": {
|
||||
"disambiguation": "George Shaw",
|
||||
"id": "62c3befb-6366-4585-b256-809472333801",
|
||||
"name": "Adhesive Wombat",
|
||||
"sort-name": "Wombat, Adhesive"
|
||||
}
|
||||
}
|
||||
],
|
||||
"artist-credit-phrase": "Adhesive Wombat",
|
||||
"ext:score": "100",
|
||||
"id": "9968a9d6-8d92-4051-8f76-674e157b6eed",
|
||||
"length": "271000",
|
||||
"release-list": [
|
||||
{
|
||||
"country": "XW",
|
||||
"date": "2013-06-05",
|
||||
"id": "a50d2a81-2a50-484d-9cb4-b9f6833f583e",
|
||||
"medium-list": [
|
||||
{
|
||||
"format": "Digital Media",
|
||||
"position": "1",
|
||||
"track-count": 11,
|
||||
"track-list": [
|
||||
{
|
||||
"id": "64d43604-c1ee-4f45-a02c-030672d2fe27",
|
||||
"length": "271000",
|
||||
"number": "1",
|
||||
"title": "8-Bit Adventure",
|
||||
"track_or_recording_length": "271000"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"medium-track-count": 11,
|
||||
"release-event-list": [
|
||||
{
|
||||
"area": {
|
||||
"id": "525d4e18-3d00-31b9-a58b-a146a916de8f",
|
||||
"iso-3166-1-code-list": [
|
||||
"XW"
|
||||
],
|
||||
"name": "[Worldwide]",
|
||||
"sort-name": "[Worldwide]"
|
||||
},
|
||||
"date": "2013-06-05"
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"id": "447b4979-2178-405c-bfe6-46bf0b09e6c7",
|
||||
"primary-type": "Album",
|
||||
"type": "Album"
|
||||
},
|
||||
"status": "Official",
|
||||
"title": "Marsupial Madness"
|
||||
}
|
||||
],
|
||||
"title": "8-Bit Adventure",
|
||||
"tag-list": [
|
||||
{
|
||||
"count": "2",
|
||||
"name": "techno"
|
||||
},
|
||||
{
|
||||
"count": "2",
|
||||
"name": "good-music"
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
tracks['get']['8bitadventures'] = {'recording': tracks['search']['8bitadventures']['recording-list'][0]}
|
||||
tracks['get']['chop_suey'] = {
|
||||
'recording': {
|
||||
'id': '46c7368a-013a-47b6-97cc-e55e7ab25213',
|
||||
'length': '210240',
|
||||
'title': 'Chop Suey!',
|
||||
'work-relation-list': [{'target': 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5',
|
||||
'type': 'performance',
|
||||
'type-id': 'a3005666-a872-32c3-ad06-98af558e99b0',
|
||||
'work': {'id': 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5',
|
||||
'language': 'eng',
|
||||
'title': 'Chop Suey!'}}]}}
|
||||
|
||||
works = {'search': {}, 'get': {}}
|
||||
works['get']['chop_suey'] = {'work': {'id': 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5',
|
||||
'language': 'eng',
|
||||
'recording-relation-list': [{'direction': 'backward',
|
||||
'recording': {'disambiguation': 'edit',
|
||||
'id': '07ca77cf-f513-4e9c-b190-d7e24bbad448',
|
||||
'length': '170893',
|
||||
'title': 'Chop Suey!'},
|
||||
'target': '07ca77cf-f513-4e9c-b190-d7e24bbad448',
|
||||
'type': 'performance',
|
||||
'type-id': 'a3005666-a872-32c3-ad06-98af558e99b0'},
|
||||
],
|
||||
'title': 'Chop Suey!',
|
||||
'type': 'Song',
|
||||
'url-relation-list': [{'direction': 'backward',
|
||||
'target': 'http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!',
|
||||
'type': 'lyrics',
|
||||
'type-id': 'e38e65aa-75e0-42ba-ace0-072aeb91a538'}]}}
|
File diff suppressed because one or more lines are too long
|
@ -8,21 +8,21 @@ from funkwhale_api.musicbrainz import api
|
|||
from funkwhale_api.music import serializers
|
||||
from funkwhale_api.music import tasks
|
||||
|
||||
from . import data as api_data
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def test_can_submit_youtube_url_for_track_import(mocker, superuser_client):
|
||||
def test_can_submit_youtube_url_for_track_import(
|
||||
artists, albums, tracks, mocker, superuser_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['adhesive_wombat'])
|
||||
return_value=artists['get']['adhesive_wombat'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get']['marsupial'])
|
||||
return_value=albums['get']['marsupial'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.get',
|
||||
return_value=api_data.tracks['get']['8bitadventures'])
|
||||
return_value=tracks['get']['8bitadventures'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.music.models.TrackFile.download_file',
|
||||
return_value=None)
|
||||
|
@ -58,17 +58,18 @@ def test_import_creates_an_import_with_correct_data(mocker, superuser_client):
|
|||
assert job.source == 'https://www.youtube.com/watch?v={0}'.format(video_id)
|
||||
|
||||
|
||||
def test_can_import_whole_album(mocker, superuser_client):
|
||||
def test_can_import_whole_album(
|
||||
artists, albums, mocker, superuser_client):
|
||||
mocker.patch('funkwhale_api.music.tasks.import_job_run')
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['soad'])
|
||||
return_value=artists['get']['soad'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.images.get_front',
|
||||
return_value=b'')
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get_with_includes']['hypnotize'])
|
||||
return_value=albums['get_with_includes']['hypnotize'])
|
||||
payload = {
|
||||
'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94',
|
||||
'tracks': [
|
||||
|
@ -97,7 +98,7 @@ def test_can_import_whole_album(mocker, superuser_client):
|
|||
|
||||
album = models.Album.objects.latest('id')
|
||||
assert str(album.mbid) == '47ae093f-1607-49a3-be11-a15d335ccc94'
|
||||
medium_data = api_data.albums['get_with_includes']['hypnotize']['release']['medium-list'][0]
|
||||
medium_data = albums['get_with_includes']['hypnotize']['release']['medium-list'][0]
|
||||
assert int(medium_data['track-count']) == album.tracks.all().count()
|
||||
|
||||
for track in medium_data['track-list']:
|
||||
|
@ -113,17 +114,18 @@ def test_can_import_whole_album(mocker, superuser_client):
|
|||
assert job.source == row['source']
|
||||
|
||||
|
||||
def test_can_import_whole_artist(mocker, superuser_client):
|
||||
def test_can_import_whole_artist(
|
||||
artists, albums, mocker, superuser_client):
|
||||
mocker.patch('funkwhale_api.music.tasks.import_job_run')
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['soad'])
|
||||
return_value=artists['get']['soad'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.images.get_front',
|
||||
return_value=b'')
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get_with_includes']['hypnotize'])
|
||||
return_value=albums['get_with_includes']['hypnotize'])
|
||||
payload = {
|
||||
'artistId': 'mbid',
|
||||
'albums': [
|
||||
|
@ -157,7 +159,7 @@ def test_can_import_whole_artist(mocker, superuser_client):
|
|||
|
||||
album = models.Album.objects.latest('id')
|
||||
assert str(album.mbid) == '47ae093f-1607-49a3-be11-a15d335ccc94'
|
||||
medium_data = api_data.albums['get_with_includes']['hypnotize']['release']['medium-list'][0]
|
||||
medium_data = albums['get_with_includes']['hypnotize']['release']['medium-list'][0]
|
||||
assert int(medium_data['track-count']) == album.tracks.all().count()
|
||||
|
||||
for track in medium_data['track-list']:
|
||||
|
@ -173,55 +175,57 @@ def test_can_import_whole_artist(mocker, superuser_client):
|
|||
assert job.source == row['source']
|
||||
|
||||
|
||||
def test_user_can_query_api_for_his_own_batches(client, factories):
|
||||
user1 = factories['users.SuperUser']()
|
||||
user2 = factories['users.SuperUser']()
|
||||
|
||||
job = factories['music.ImportJob'](batch__submitted_by=user1)
|
||||
def test_user_can_query_api_for_his_own_batches(
|
||||
superuser_api_client, factories):
|
||||
factories['music.ImportJob']()
|
||||
job = factories['music.ImportJob'](
|
||||
batch__submitted_by=superuser_api_client.user)
|
||||
url = reverse('api:v1:import-batches-list')
|
||||
|
||||
client.login(username=user2.username, password='test')
|
||||
response2 = client.get(url)
|
||||
results = json.loads(response2.content.decode('utf-8'))
|
||||
assert results['count'] == 0
|
||||
client.logout()
|
||||
|
||||
client.login(username=user1.username, password='test')
|
||||
response1 = client.get(url)
|
||||
results = json.loads(response1.content.decode('utf-8'))
|
||||
response = superuser_api_client.get(url)
|
||||
results = response.data
|
||||
assert results['count'] == 1
|
||||
assert results['results'][0]['jobs'][0]['mbid'] == job.mbid
|
||||
|
||||
|
||||
def test_user_can_create_an_empty_batch(client, factories):
|
||||
user = factories['users.SuperUser']()
|
||||
def test_user_cannnot_access_other_batches(
|
||||
superuser_api_client, factories):
|
||||
factories['music.ImportJob']()
|
||||
job = factories['music.ImportJob']()
|
||||
url = reverse('api:v1:import-batches-list')
|
||||
client.login(username=user.username, password='test')
|
||||
response = client.post(url)
|
||||
|
||||
response = superuser_api_client.get(url)
|
||||
results = response.data
|
||||
assert results['count'] == 0
|
||||
|
||||
|
||||
def test_user_can_create_an_empty_batch(superuser_api_client, factories):
|
||||
url = reverse('api:v1:import-batches-list')
|
||||
response = superuser_api_client.post(url)
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
batch = user.imports.latest('id')
|
||||
batch = superuser_api_client.user.imports.latest('id')
|
||||
|
||||
assert batch.submitted_by == user
|
||||
assert batch.submitted_by == superuser_api_client.user
|
||||
assert batch.source == 'api'
|
||||
|
||||
|
||||
def test_user_can_create_import_job_with_file(client, factories, mocker):
|
||||
def test_user_can_create_import_job_with_file(
|
||||
superuser_api_client, factories, mocker):
|
||||
path = os.path.join(DATA_DIR, 'test.ogg')
|
||||
m = mocker.patch('funkwhale_api.common.utils.on_commit')
|
||||
user = factories['users.SuperUser']()
|
||||
batch = factories['music.ImportBatch'](submitted_by=user)
|
||||
batch = factories['music.ImportBatch'](
|
||||
submitted_by=superuser_api_client.user)
|
||||
url = reverse('api:v1:import-jobs-list')
|
||||
client.login(username=user.username, password='test')
|
||||
with open(path, 'rb') as f:
|
||||
content = f.read()
|
||||
f.seek(0)
|
||||
response = client.post(url, {
|
||||
response = superuser_api_client.post(url, {
|
||||
'batch': batch.pk,
|
||||
'audio_file': f,
|
||||
'source': 'file://'
|
||||
}, format='multipart')
|
||||
})
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
|
@ -237,16 +241,16 @@ def test_user_can_create_import_job_with_file(client, factories, mocker):
|
|||
import_job_id=job.pk)
|
||||
|
||||
|
||||
def test_can_search_artist(factories, client):
|
||||
def test_can_search_artist(factories, logged_in_client):
|
||||
artist1 = factories['music.Artist']()
|
||||
artist2 = factories['music.Artist']()
|
||||
expected = [serializers.ArtistSerializerNested(artist1).data]
|
||||
url = reverse('api:v1:artists-search')
|
||||
response = client.get(url, {'query': artist1.name})
|
||||
assert json.loads(response.content.decode('utf-8')) == expected
|
||||
response = logged_in_client.get(url, {'query': artist1.name})
|
||||
assert response.data == expected
|
||||
|
||||
|
||||
def test_can_search_artist_by_name_start(factories, client):
|
||||
def test_can_search_artist_by_name_start(factories, logged_in_client):
|
||||
artist1 = factories['music.Artist'](name='alpha')
|
||||
artist2 = factories['music.Artist'](name='beta')
|
||||
expected = {
|
||||
|
@ -256,20 +260,20 @@ def test_can_search_artist_by_name_start(factories, client):
|
|||
'results': [serializers.ArtistSerializerNested(artist1).data]
|
||||
}
|
||||
url = reverse('api:v1:artists-list')
|
||||
response = client.get(url, {'name__startswith': 'a'})
|
||||
response = logged_in_client.get(url, {'name__startswith': 'a'})
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
def test_can_search_tracks(factories, client):
|
||||
def test_can_search_tracks(factories, logged_in_client):
|
||||
track1 = factories['music.Track'](title="test track 1")
|
||||
track2 = factories['music.Track']()
|
||||
query = 'test track 1'
|
||||
expected = [serializers.TrackSerializerNested(track1).data]
|
||||
url = reverse('api:v1:tracks-search')
|
||||
response = client.get(url, {'query': query})
|
||||
response = logged_in_client.get(url, {'query': query})
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
@pytest.mark.parametrize('route,method', [
|
||||
|
@ -278,24 +282,31 @@ def test_can_search_tracks(factories, client):
|
|||
('api:v1:artists-list', 'get'),
|
||||
('api:v1:albums-list', 'get'),
|
||||
])
|
||||
def test_can_restrict_api_views_to_authenticated_users(db, route, method, settings, client):
|
||||
def test_can_restrict_api_views_to_authenticated_users(
|
||||
db, route, method, settings, client):
|
||||
url = reverse(route)
|
||||
settings.API_AUTHENTICATION_REQUIRED = True
|
||||
response = getattr(client, method)(url)
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_track_file_url_is_restricted_to_authenticated_users(client, factories, settings):
|
||||
def test_track_file_url_is_restricted_to_authenticated_users(
|
||||
api_client, factories, settings):
|
||||
settings.API_AUTHENTICATION_REQUIRED = True
|
||||
f = factories['music.TrackFile']()
|
||||
assert f.audio_file is not None
|
||||
url = f.path
|
||||
response = client.get(url)
|
||||
response = api_client.get(url)
|
||||
assert response.status_code == 401
|
||||
|
||||
user = factories['users.SuperUser']()
|
||||
client.login(username=user.username, password='test')
|
||||
response = client.get(url)
|
||||
|
||||
def test_track_file_url_is_accessible_to_authenticated_users(
|
||||
logged_in_api_client, factories, settings):
|
||||
settings.API_AUTHENTICATION_REQUIRED = True
|
||||
f = factories['music.TrackFile']()
|
||||
assert f.audio_file is not None
|
||||
url = f.path
|
||||
response = logged_in_api_client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response['X-Accel-Redirect'] == '/_protected{}'.format(f.audio_file.url)
|
||||
|
|
|
@ -2,23 +2,21 @@ import json
|
|||
|
||||
from django.urls import reverse
|
||||
|
||||
from . import data as api_data
|
||||
|
||||
|
||||
def test_create_import_can_bind_to_request(
|
||||
mocker, factories, superuser_api_client):
|
||||
artists, albums, mocker, factories, superuser_api_client):
|
||||
request = factories['requests.ImportRequest']()
|
||||
|
||||
mocker.patch('funkwhale_api.music.tasks.import_job_run')
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['soad'])
|
||||
return_value=artists['get']['soad'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.images.get_front',
|
||||
return_value=b'')
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get_with_includes']['hypnotize'])
|
||||
return_value=albums['get_with_includes']['hypnotize'])
|
||||
payload = {
|
||||
'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94',
|
||||
'importRequest': request.pk,
|
||||
|
|
|
@ -7,15 +7,12 @@ from funkwhale_api.music import serializers
|
|||
from funkwhale_api.music import tasks
|
||||
from funkwhale_api.music import lyrics as lyrics_utils
|
||||
|
||||
from .mocking import lyricswiki
|
||||
from . import data as api_data
|
||||
|
||||
|
||||
|
||||
def test_works_import_lyrics_if_any(mocker, factories):
|
||||
def test_works_import_lyrics_if_any(
|
||||
lyricswiki_content, mocker, factories):
|
||||
mocker.patch(
|
||||
'funkwhale_api.music.lyrics._get_html',
|
||||
return_value=lyricswiki.content)
|
||||
return_value=lyricswiki_content)
|
||||
lyrics = factories['music.Lyrics'](
|
||||
url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!')
|
||||
|
||||
|
@ -48,16 +45,22 @@ Is it me you're looking for?"""
|
|||
assert expected == l.content_rendered
|
||||
|
||||
|
||||
def test_works_import_lyrics_if_any(mocker, factories, logged_in_client):
|
||||
def test_works_import_lyrics_if_any(
|
||||
lyricswiki_content,
|
||||
works,
|
||||
tracks,
|
||||
mocker,
|
||||
factories,
|
||||
logged_in_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.works.get',
|
||||
return_value=api_data.works['get']['chop_suey'])
|
||||
return_value=works['get']['chop_suey'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.get',
|
||||
return_value=api_data.tracks['get']['chop_suey'])
|
||||
return_value=tracks['get']['chop_suey'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.music.lyrics._get_html',
|
||||
return_value=lyricswiki.content)
|
||||
return_value=lyricswiki_content)
|
||||
track = factories['music.Track'](
|
||||
work=None,
|
||||
mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448')
|
||||
|
|
|
@ -2,14 +2,11 @@ import pytest
|
|||
from funkwhale_api.music import models
|
||||
import datetime
|
||||
|
||||
from . import data as api_data
|
||||
from .cover import binary_data
|
||||
|
||||
|
||||
def test_can_create_artist_from_api(mocker, db):
|
||||
def test_can_create_artist_from_api(artists, mocker, db):
|
||||
mocker.patch(
|
||||
'musicbrainzngs.search_artists',
|
||||
return_value=api_data.artists['search']['adhesive_wombat'])
|
||||
return_value=artists['search']['adhesive_wombat'])
|
||||
artist = models.Artist.create_from_api(query="Adhesive wombat")
|
||||
data = models.Artist.api.search(query='Adhesive wombat')['artist-list'][0]
|
||||
|
||||
|
@ -19,13 +16,13 @@ def test_can_create_artist_from_api(mocker, db):
|
|||
assert artist.name, 'Adhesive Wombat'
|
||||
|
||||
|
||||
def test_can_create_album_from_api(mocker, db):
|
||||
def test_can_create_album_from_api(artists, albums, mocker, db):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.search',
|
||||
return_value=api_data.albums['search']['hypnotize'])
|
||||
return_value=albums['search']['hypnotize'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['soad'])
|
||||
return_value=artists['get']['soad'])
|
||||
album = models.Album.create_from_api(query="Hypnotize", artist='system of a down', type='album')
|
||||
data = models.Album.api.search(query='Hypnotize', artist='system of a down', type='album')['release-list'][0]
|
||||
|
||||
|
@ -38,16 +35,16 @@ def test_can_create_album_from_api(mocker, db):
|
|||
assert album.artist.mbid, data['artist-credit'][0]['artist']['id']
|
||||
|
||||
|
||||
def test_can_create_track_from_api(mocker, db):
|
||||
def test_can_create_track_from_api(artists, albums, tracks, mocker, db):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['adhesive_wombat'])
|
||||
return_value=artists['get']['adhesive_wombat'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get']['marsupial'])
|
||||
return_value=albums['get']['marsupial'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.search',
|
||||
return_value=api_data.tracks['search']['8bitadventures'])
|
||||
return_value=tracks['search']['8bitadventures'])
|
||||
track = models.Track.create_from_api(query="8-bit adventure")
|
||||
data = models.Track.api.search(query='8-bit adventure')['recording-list'][0]
|
||||
assert int(data['ext:score']) == 100
|
||||
|
@ -60,16 +57,17 @@ def test_can_create_track_from_api(mocker, db):
|
|||
assert track.album.title == 'Marsupial Madness'
|
||||
|
||||
|
||||
def test_can_create_track_from_api_with_corresponding_tags(mocker, db):
|
||||
def test_can_create_track_from_api_with_corresponding_tags(
|
||||
artists, albums, tracks, mocker, db):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['adhesive_wombat'])
|
||||
return_value=artists['get']['adhesive_wombat'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get']['marsupial'])
|
||||
return_value=albums['get']['marsupial'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.get',
|
||||
return_value=api_data.tracks['get']['8bitadventures'])
|
||||
return_value=tracks['get']['8bitadventures'])
|
||||
track = models.Track.create_from_api(id='9968a9d6-8d92-4051-8f76-674e157b6eed')
|
||||
expected_tags = ['techno', 'good-music']
|
||||
track_tags = [tag.slug for tag in track.tags.all()]
|
||||
|
@ -77,16 +75,17 @@ def test_can_create_track_from_api_with_corresponding_tags(mocker, db):
|
|||
assert tag in track_tags
|
||||
|
||||
|
||||
def test_can_get_or_create_track_from_api(mocker, db):
|
||||
def test_can_get_or_create_track_from_api(
|
||||
artists, albums, tracks, mocker, db):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['adhesive_wombat'])
|
||||
return_value=artists['get']['adhesive_wombat'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get']['marsupial'])
|
||||
return_value=albums['get']['marsupial'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.search',
|
||||
return_value=api_data.tracks['search']['8bitadventures'])
|
||||
return_value=tracks['search']['8bitadventures'])
|
||||
track = models.Track.create_from_api(query="8-bit adventure")
|
||||
data = models.Track.api.search(query='8-bit adventure')['recording-list'][0]
|
||||
assert int(data['ext:score']) == 100
|
||||
|
@ -126,13 +125,13 @@ def test_artist_tags_deduced_from_album_tags(factories, django_assert_num_querie
|
|||
assert tag in artist.tags
|
||||
|
||||
|
||||
def test_can_download_image_file_for_album(mocker, factories):
|
||||
def test_can_download_image_file_for_album(binary_cover, mocker, factories):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.images.get_front',
|
||||
return_value=binary_data)
|
||||
return_value=binary_cover)
|
||||
# client._api.get_image_front('55ea4f82-b42b-423e-a0e5-290ccdf443ed')
|
||||
album = factories['music.Album'](mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed')
|
||||
album.get_image()
|
||||
album.save()
|
||||
|
||||
assert album.cover.file.read() == binary_data
|
||||
assert album.cover.file.read() == binary_cover
|
||||
|
|
|
@ -4,8 +4,6 @@ import pytest
|
|||
from funkwhale_api.providers.acoustid import get_acoustid_client
|
||||
from funkwhale_api.music import tasks
|
||||
|
||||
from . import data as api_data
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
|
@ -50,7 +48,7 @@ def test_set_acoustid_on_track_file_required_high_score(factories, mocker):
|
|||
|
||||
|
||||
def test_import_job_can_run_with_file_and_acoustid(
|
||||
preferences, factories, mocker):
|
||||
artists, albums, tracks, preferences, factories, mocker):
|
||||
preferences['providers_acoustid__api_key'] = 'test'
|
||||
path = os.path.join(DATA_DIR, 'test.ogg')
|
||||
mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed'
|
||||
|
@ -66,13 +64,13 @@ def test_import_job_can_run_with_file_and_acoustid(
|
|||
}
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['adhesive_wombat'])
|
||||
return_value=artists['get']['adhesive_wombat'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get']['marsupial'])
|
||||
return_value=albums['get']['marsupial'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.search',
|
||||
return_value=api_data.tracks['search']['8bitadventures'])
|
||||
return_value=tracks['search']['8bitadventures'])
|
||||
mocker.patch('acoustid.match', return_value=acoustid_payload)
|
||||
|
||||
job = factories['music.FileImportJob'](audio_file__path=path)
|
||||
|
@ -129,7 +127,8 @@ def test__do_import_skipping_accoustid_if_no_key(
|
|||
m.assert_called_once_with(p)
|
||||
|
||||
|
||||
def test_import_job_can_be_skipped(factories, mocker, preferences):
|
||||
def test_import_job_can_be_skipped(
|
||||
artists, albums, tracks, factories, mocker, preferences):
|
||||
preferences['providers_acoustid__api_key'] = 'test'
|
||||
path = os.path.join(DATA_DIR, 'test.ogg')
|
||||
mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed'
|
||||
|
@ -146,13 +145,13 @@ def test_import_job_can_be_skipped(factories, mocker, preferences):
|
|||
}
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['adhesive_wombat'])
|
||||
return_value=artists['get']['adhesive_wombat'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.get',
|
||||
return_value=api_data.albums['get']['marsupial'])
|
||||
return_value=albums['get']['marsupial'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.search',
|
||||
return_value=api_data.tracks['search']['8bitadventures'])
|
||||
return_value=tracks['search']['8bitadventures'])
|
||||
mocker.patch('acoustid.match', return_value=acoustid_payload)
|
||||
|
||||
job = factories['music.FileImportJob'](audio_file__path=path)
|
||||
|
|
|
@ -5,13 +5,11 @@ from funkwhale_api.music import models
|
|||
from funkwhale_api.musicbrainz import api
|
||||
from funkwhale_api.music import serializers
|
||||
|
||||
from . import data as api_data
|
||||
|
||||
|
||||
def test_can_import_work(factories, mocker):
|
||||
def test_can_import_work(factories, mocker, works):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.works.get',
|
||||
return_value=api_data.works['get']['chop_suey'])
|
||||
return_value=works['get']['chop_suey'])
|
||||
recording = factories['music.Track'](
|
||||
mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448')
|
||||
mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5'
|
||||
|
@ -28,13 +26,13 @@ def test_can_import_work(factories, mocker):
|
|||
assert recording.work == work
|
||||
|
||||
|
||||
def test_can_get_work_from_recording(factories, mocker):
|
||||
def test_can_get_work_from_recording(factories, mocker, works, tracks):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.works.get',
|
||||
return_value=api_data.works['get']['chop_suey'])
|
||||
return_value=works['get']['chop_suey'])
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.get',
|
||||
return_value=api_data.tracks['get']['chop_suey'])
|
||||
return_value=tracks['get']['chop_suey'])
|
||||
recording = factories['music.Track'](
|
||||
work=None,
|
||||
mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448')
|
||||
|
@ -53,10 +51,10 @@ def test_can_get_work_from_recording(factories, mocker):
|
|||
assert recording.work == work
|
||||
|
||||
|
||||
def test_works_import_lyrics_if_any(db, mocker):
|
||||
def test_works_import_lyrics_if_any(db, mocker, works):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.works.get',
|
||||
return_value=api_data.works['get']['chop_suey'])
|
||||
return_value=works['get']['chop_suey'])
|
||||
mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5'
|
||||
work = models.Work.create_from_api(id=mbid)
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
artists = {'search': {}, 'get': {}}
|
||||
artists['search']['lost fingers'] = {
|
||||
import pytest
|
||||
|
||||
_artists = {'search': {}, 'get': {}}
|
||||
_artists['search']['lost fingers'] = {
|
||||
'artist-count': 696,
|
||||
'artist-list': [
|
||||
{
|
||||
|
@ -21,7 +23,7 @@ artists['search']['lost fingers'] = {
|
|||
},
|
||||
]
|
||||
}
|
||||
artists['get']['lost fingers'] = {
|
||||
_artists['get']['lost fingers'] = {
|
||||
"artist": {
|
||||
"life-span": {
|
||||
"begin": "2008"
|
||||
|
@ -102,8 +104,8 @@ artists['get']['lost fingers'] = {
|
|||
}
|
||||
|
||||
|
||||
release_groups = {'browse': {}}
|
||||
release_groups['browse']["lost fingers"] = {
|
||||
_release_groups = {'browse': {}}
|
||||
_release_groups['browse']["lost fingers"] = {
|
||||
"release-group-list": [
|
||||
{
|
||||
"first-release-date": "2010",
|
||||
|
@ -165,8 +167,8 @@ release_groups['browse']["lost fingers"] = {
|
|||
"release-group-count": 8
|
||||
}
|
||||
|
||||
recordings = {'search': {}, 'get': {}}
|
||||
recordings['search']['brontide matador'] = {
|
||||
_recordings = {'search': {}, 'get': {}}
|
||||
_recordings['search']['brontide matador'] = {
|
||||
"recording-count": 1044,
|
||||
"recording-list": [
|
||||
{
|
||||
|
@ -217,8 +219,8 @@ recordings['search']['brontide matador'] = {
|
|||
]
|
||||
}
|
||||
|
||||
releases = {'search': {}, 'get': {}, 'browse': {}}
|
||||
releases['search']['brontide matador'] = {
|
||||
_releases = {'search': {}, 'get': {}, 'browse': {}}
|
||||
_releases['search']['brontide matador'] = {
|
||||
"release-count": 116, "release-list": [
|
||||
{
|
||||
"ext:score": "100",
|
||||
|
@ -283,7 +285,7 @@ releases['search']['brontide matador'] = {
|
|||
]
|
||||
}
|
||||
|
||||
releases['browse']['Lost in the 80s'] = {
|
||||
_releases['browse']['Lost in the 80s'] = {
|
||||
"release-count": 3,
|
||||
"release-list": [
|
||||
{
|
||||
|
@ -476,3 +478,23 @@ releases['browse']['Lost in the 80s'] = {
|
|||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def releases():
|
||||
return _releases
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def release_groups():
|
||||
return _release_groups
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def artists():
|
||||
return _artists
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def recordings():
|
||||
return _recordings
|
|
@ -2,64 +2,65 @@ import json
|
|||
from django.urls import reverse
|
||||
|
||||
from funkwhale_api.musicbrainz import api
|
||||
from . import data as api_data
|
||||
|
||||
|
||||
|
||||
def test_can_search_recording_in_musicbrainz_api(db, mocker, client):
|
||||
def test_can_search_recording_in_musicbrainz_api(
|
||||
recordings, db, mocker, logged_in_api_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.recordings.search',
|
||||
return_value=api_data.recordings['search']['brontide matador'])
|
||||
return_value=recordings['search']['brontide matador'])
|
||||
query = 'brontide matador'
|
||||
url = reverse('api:v1:providers:musicbrainz:search-recordings')
|
||||
expected = api_data.recordings['search']['brontide matador']
|
||||
response = client.get(url, data={'query': query})
|
||||
expected = recordings['search']['brontide matador']
|
||||
response = logged_in_api_client.get(url, data={'query': query})
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
def test_can_search_release_in_musicbrainz_api(db, mocker, client):
|
||||
def test_can_search_release_in_musicbrainz_api(releases, db, mocker, logged_in_api_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.search',
|
||||
return_value=api_data.releases['search']['brontide matador'])
|
||||
return_value=releases['search']['brontide matador'])
|
||||
query = 'brontide matador'
|
||||
url = reverse('api:v1:providers:musicbrainz:search-releases')
|
||||
expected = api_data.releases['search']['brontide matador']
|
||||
response = client.get(url, data={'query': query})
|
||||
expected = releases['search']['brontide matador']
|
||||
response = logged_in_api_client.get(url, data={'query': query})
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
def test_can_search_artists_in_musicbrainz_api(db, mocker, client):
|
||||
def test_can_search_artists_in_musicbrainz_api(artists, db, mocker, logged_in_api_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.search',
|
||||
return_value=api_data.artists['search']['lost fingers'])
|
||||
return_value=artists['search']['lost fingers'])
|
||||
query = 'lost fingers'
|
||||
url = reverse('api:v1:providers:musicbrainz:search-artists')
|
||||
expected = api_data.artists['search']['lost fingers']
|
||||
response = client.get(url, data={'query': query})
|
||||
expected = artists['search']['lost fingers']
|
||||
response = logged_in_api_client.get(url, data={'query': query})
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
def test_can_get_artist_in_musicbrainz_api(db, mocker, client):
|
||||
def test_can_get_artist_in_musicbrainz_api(artists, db, mocker, logged_in_api_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.artists.get',
|
||||
return_value=api_data.artists['get']['lost fingers'])
|
||||
return_value=artists['get']['lost fingers'])
|
||||
uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9'
|
||||
url = reverse('api:v1:providers:musicbrainz:artist-detail', kwargs={
|
||||
'uuid': uuid,
|
||||
})
|
||||
response = client.get(url)
|
||||
expected = api_data.artists['get']['lost fingers']
|
||||
response = logged_in_api_client.get(url)
|
||||
expected = artists['get']['lost fingers']
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
def test_can_broswe_release_group_using_musicbrainz_api(db, mocker, client):
|
||||
def test_can_broswe_release_group_using_musicbrainz_api(
|
||||
release_groups, db, mocker, logged_in_api_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.release_groups.browse',
|
||||
return_value=api_data.release_groups['browse']['lost fingers'])
|
||||
return_value=release_groups['browse']['lost fingers'])
|
||||
uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9'
|
||||
url = reverse(
|
||||
'api:v1:providers:musicbrainz:release-group-browse',
|
||||
|
@ -67,16 +68,17 @@ def test_can_broswe_release_group_using_musicbrainz_api(db, mocker, client):
|
|||
'artist_uuid': uuid,
|
||||
}
|
||||
)
|
||||
response = client.get(url)
|
||||
expected = api_data.release_groups['browse']['lost fingers']
|
||||
response = logged_in_api_client.get(url)
|
||||
expected = release_groups['browse']['lost fingers']
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
||||
|
||||
def test_can_broswe_releases_using_musicbrainz_api(db, mocker, client):
|
||||
def test_can_broswe_releases_using_musicbrainz_api(
|
||||
releases, db, mocker, logged_in_api_client):
|
||||
mocker.patch(
|
||||
'funkwhale_api.musicbrainz.api.releases.browse',
|
||||
return_value=api_data.releases['browse']['Lost in the 80s'])
|
||||
return_value=releases['browse']['Lost in the 80s'])
|
||||
uuid = 'f04ed607-11b7-3843-957e-503ecdd485d1'
|
||||
url = reverse(
|
||||
'api:v1:providers:musicbrainz:release-browse',
|
||||
|
@ -84,7 +86,7 @@ def test_can_broswe_releases_using_musicbrainz_api(db, mocker, client):
|
|||
'release_group_uuid': uuid,
|
||||
}
|
||||
)
|
||||
response = client.get(url)
|
||||
expected = api_data.releases['browse']['Lost in the 80s']
|
||||
response = logged_in_api_client.get(url)
|
||||
expected = releases['browse']['Lost in the 80s']
|
||||
|
||||
assert expected == json.loads(response.content.decode('utf-8'))
|
||||
assert expected == response.data
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
API endpoint for fetching instance activity and updated timeline to use this new
|
||||
endpoint (#141)
|
1
dev.yml
1
dev.yml
|
@ -50,6 +50,7 @@ services:
|
|||
- ./api:/app
|
||||
- ./data/music:/music
|
||||
environment:
|
||||
- "PYTHONDONTWRITEBYTECODE=true"
|
||||
- "DJANGO_ALLOWED_HOSTS=localhost,nginx"
|
||||
- "DJANGO_SETTINGS_MODULE=config.settings.local"
|
||||
- "DJANGO_SECRET_KEY=dev"
|
||||
|
|
|
@ -36,9 +36,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { WebSocketBridge } from 'django-channels'
|
||||
|
||||
import logger from '@/logging'
|
||||
import Sidebar from '@/components/Sidebar'
|
||||
import Raven from '@/components/Raven'
|
||||
|
||||
|
@ -53,34 +50,11 @@ export default {
|
|||
},
|
||||
created () {
|
||||
this.$store.dispatch('instance/fetchSettings')
|
||||
this.openWebsocket()
|
||||
let self = this
|
||||
setInterval(() => {
|
||||
// used to redraw ago dates every minute
|
||||
self.$store.commit('ui/computeLastDate')
|
||||
}, 1000 * 60)
|
||||
},
|
||||
methods: {
|
||||
openWebsocket () {
|
||||
if (!this.$store.state.auth.authenticated) {
|
||||
return
|
||||
}
|
||||
let self = this
|
||||
let token = this.$store.state.auth.token
|
||||
// let token = 'test'
|
||||
const bridge = new WebSocketBridge()
|
||||
bridge.connect(
|
||||
`/api/v1/instance/activity?token=${token}`,
|
||||
null,
|
||||
{reconnectInterval: 5000})
|
||||
bridge.listen(function (event) {
|
||||
logger.default.info('Received timeline update', event)
|
||||
self.$store.commit('instance/event', event)
|
||||
})
|
||||
bridge.socket.addEventListener('open', function () {
|
||||
console.log('Connected to WebSocket')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -43,6 +43,9 @@ export default {
|
|||
if (state.events.length > state.maxEvents) {
|
||||
state.events = state.events.slice(0, state.maxEvents)
|
||||
}
|
||||
},
|
||||
events: (state, value) => {
|
||||
state.events = value
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div class="main pusher">
|
||||
<div class="ui vertical center aligned stripe segment">
|
||||
<div class="ui text container">
|
||||
<div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
|
||||
<div class="ui text loader">Loading timeline...</div>
|
||||
</div>
|
||||
<div v-else class="ui text container">
|
||||
<h1 class="ui header">Recent activity on this instance</h1>
|
||||
<div class="ui feed">
|
||||
<component
|
||||
|
@ -26,6 +29,9 @@
|
|||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import { WebSocketBridge } from 'django-channels'
|
||||
import axios from 'axios'
|
||||
import logger from '@/logging'
|
||||
|
||||
import Like from '@/components/activity/Like'
|
||||
import Listen from '@/components/activity/Listen'
|
||||
|
@ -33,16 +39,51 @@ import Listen from '@/components/activity/Listen'
|
|||
export default {
|
||||
data () {
|
||||
return {
|
||||
isLoading: false,
|
||||
components: {
|
||||
'Like': Like,
|
||||
'Listen': Listen
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.openWebsocket()
|
||||
this.fetchEvents()
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
events: state => state.instance.events
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
fetchEvents () {
|
||||
this.isLoading = true
|
||||
let self = this
|
||||
axios.get('/activity/').then((response) => {
|
||||
self.isLoading = false
|
||||
self.$store.commit('instance/events', response.data.results)
|
||||
})
|
||||
},
|
||||
openWebsocket () {
|
||||
if (!this.$store.state.auth.authenticated) {
|
||||
return
|
||||
}
|
||||
let self = this
|
||||
let token = this.$store.state.auth.token
|
||||
// let token = 'test'
|
||||
const bridge = new WebSocketBridge()
|
||||
bridge.connect(
|
||||
`/api/v1/instance/activity?token=${token}`,
|
||||
null,
|
||||
{reconnectInterval: 5000})
|
||||
bridge.listen(function (event) {
|
||||
logger.default.info('Received timeline update', event)
|
||||
self.$store.commit('instance/event', event)
|
||||
})
|
||||
bridge.socket.addEventListener('open', function () {
|
||||
console.log('Connected to WebSocket')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue