Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
jovuit
funkwhale
Commits
95497e76
Commit
95497e76
authored
Feb 05, 2020
by
Eliot Berriot
Browse files
See
#170
: channels ui (listeners)
parent
b74517ff
Changes
79
Hide whitespace changes
Inline
Side-by-side
api/config/api_urls.py
View file @
95497e76
...
...
@@ -88,6 +88,9 @@ v1_patterns += [
url
(
r
"^token/?$"
,
jwt_views
.
obtain_jwt_token
,
name
=
"token"
),
url
(
r
"^token/refresh/?$"
,
jwt_views
.
refresh_jwt_token
,
name
=
"token_refresh"
),
url
(
r
"^rate-limit/?$"
,
common_views
.
RateLimitView
.
as_view
(),
name
=
"rate-limit"
),
url
(
r
"^text-preview/?$"
,
common_views
.
TextPreviewView
.
as_view
(),
name
=
"text-preview"
),
]
urlpatterns
=
[
...
...
api/funkwhale_api/audio/serializers.py
View file @
95497e76
...
...
@@ -70,6 +70,8 @@ class ChannelCreateSerializer(serializers.Serializer):
@
transaction
.
atomic
def
create
(
self
,
validated_data
):
from
.
import
views
description
=
validated_data
.
get
(
"description"
)
artist
=
music_models
.
Artist
.
objects
.
create
(
attributed_to
=
validated_data
[
"attributed_to"
],
...
...
@@ -99,10 +101,11 @@ class ChannelCreateSerializer(serializers.Serializer):
actor
=
validated_data
[
"attributed_to"
],
)
channel
.
save
()
channel
=
views
.
ChannelViewSet
.
queryset
.
get
(
pk
=
channel
.
pk
)
return
channel
def
to_representation
(
self
,
obj
):
return
ChannelSerializer
(
obj
).
data
return
ChannelSerializer
(
obj
,
context
=
self
.
context
).
data
NOOP
=
object
()
...
...
@@ -181,7 +184,7 @@ class ChannelUpdateSerializer(serializers.Serializer):
return
obj
def
to_representation
(
self
,
obj
):
return
ChannelSerializer
(
obj
).
data
return
ChannelSerializer
(
obj
,
context
=
self
.
context
).
data
class
ChannelSerializer
(
serializers
.
ModelSerializer
):
...
...
@@ -261,7 +264,8 @@ def rss_serialize_item(upload):
"link"
:
[{
"value"
:
federation_utils
.
full_url
(
upload
.
track
.
get_absolute_url
())}],
"enclosure"
:
[
{
"url"
:
upload
.
listen_url
,
# we enforce MP3, since it's the only format supported everywhere
"url"
:
federation_utils
.
full_url
(
upload
.
get_listen_url
(
to
=
"mp3"
)),
"length"
:
upload
.
size
or
0
,
"type"
:
upload
.
mimetype
or
"audio/mpeg"
,
}
...
...
@@ -271,7 +275,6 @@ def rss_serialize_item(upload):
data
[
"itunes:subtitle"
]
=
[{
"value"
:
upload
.
track
.
description
.
truncate
(
255
)}]
data
[
"itunes:summary"
]
=
[{
"cdata_value"
:
upload
.
track
.
description
.
rendered
}]
data
[
"description"
]
=
[{
"value"
:
upload
.
track
.
description
.
as_plain_text
}]
data
[
"content:encoded"
]
=
data
[
"itunes:summary"
]
if
upload
.
track
.
attachment_cover
:
data
[
"itunes:image"
]
=
[
...
...
api/funkwhale_api/audio/views.py
View file @
95497e76
...
...
@@ -6,7 +6,7 @@ from rest_framework import response
from
rest_framework
import
viewsets
from
django
import
http
from
django.db.models
import
Prefetch
from
django.db.models
import
Count
,
Prefetch
from
django.db.utils
import
IntegrityError
from
funkwhale_api.common
import
permissions
...
...
@@ -18,6 +18,12 @@ from funkwhale_api.users.oauth import permissions as oauth_permissions
from
.
import
filters
,
models
,
renderers
,
serializers
ARTIST_PREFETCH_QS
=
(
music_models
.
Artist
.
objects
.
select_related
(
"description"
,
"attachment_cover"
,)
.
prefetch_related
(
music_views
.
TAG_PREFETCH
)
.
annotate
(
_tracks_count
=
Count
(
"tracks"
))
)
class
ChannelsMixin
(
object
):
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
...
...
@@ -44,12 +50,7 @@ class ChannelViewSet(
"library"
,
"attributed_to"
,
"actor"
,
Prefetch
(
"artist"
,
queryset
=
music_models
.
Artist
.
objects
.
select_related
(
"attachment_cover"
,
"description"
).
prefetch_related
(
music_views
.
TAG_PREFETCH
,),
),
Prefetch
(
"artist"
,
queryset
=
ARTIST_PREFETCH_QS
),
)
.
order_by
(
"-creation_date"
)
)
...
...
@@ -131,7 +132,12 @@ class ChannelViewSet(
def
get_serializer_context
(
self
):
context
=
super
().
get_serializer_context
()
context
[
"subscriptions_count"
]
=
self
.
action
in
[
"retrieve"
,
"create"
,
"update"
]
context
[
"subscriptions_count"
]
=
self
.
action
in
[
"retrieve"
,
"create"
,
"update"
,
"partial_update"
,
]
return
context
...
...
@@ -148,8 +154,8 @@ class SubscriptionsViewSet(
.
prefetch_related
(
"target__channel__library"
,
"target__channel__attributed_to"
,
"target__channel__artist__description"
,
"actor"
,
Prefetch
(
"target__channel__artist"
,
queryset
=
ARTIST_PREFETCH_QS
),
)
.
order_by
(
"-creation_date"
)
)
...
...
@@ -171,10 +177,12 @@ class SubscriptionsViewSet(
to have a performant endpoint and avoid lots of queries just to display
subscription status in the UI
"""
subscriptions
=
list
(
self
.
get_queryset
().
values_list
(
"uuid"
,
flat
=
True
))
subscriptions
=
list
(
self
.
get_queryset
().
values_list
(
"uuid"
,
"target__channel__uuid"
)
)
payload
=
{
"results"
:
[
str
(
u
)
for
u
in
subscriptions
],
"results"
:
[
{
"uuid"
:
str
(
u
[
0
]),
"channel"
:
u
[
1
]}
for
u
in
subscriptions
],
"count"
:
len
(
subscriptions
),
}
return
response
.
Response
(
payload
,
status
=
200
)
api/funkwhale_api/common/filters.py
View file @
95497e76
...
...
@@ -176,6 +176,8 @@ class ActorScopeFilter(filters.CharFilter):
super
().
__init__
(
*
args
,
**
kwargs
)
def
filter
(
self
,
queryset
,
value
):
from
funkwhale_api.federation
import
models
as
federation_models
if
not
value
:
return
queryset
...
...
@@ -189,6 +191,17 @@ class ActorScopeFilter(filters.CharFilter):
qs
=
self
.
filter_me
(
user
=
user
,
queryset
=
queryset
)
elif
value
.
lower
()
==
"all"
:
return
queryset
elif
value
.
lower
().
startswith
(
"actor:"
):
full_username
=
value
.
split
(
"actor:"
,
1
)[
1
]
username
,
domain
=
full_username
.
split
(
"@"
)
try
:
actor
=
federation_models
.
Actor
.
objects
.
get
(
preferred_username
=
username
,
domain_id
=
domain
,
)
except
federation_models
.
Actor
.
DoesNotExist
:
return
queryset
.
none
()
return
queryset
.
filter
(
**
{
self
.
actor_field
:
actor
})
else
:
return
queryset
.
none
()
...
...
api/funkwhale_api/common/models.py
View file @
95497e76
...
...
@@ -201,7 +201,7 @@ class AttachmentQuerySet(models.QuerySet):
class
Attachment
(
models
.
Model
):
# Remote URL where the attachment can be fetched
url
=
models
.
URLField
(
max_length
=
500
,
null
=
True
)
url
=
models
.
URLField
(
max_length
=
500
,
null
=
True
,
blank
=
True
)
uuid
=
models
.
UUIDField
(
unique
=
True
,
db_index
=
True
,
default
=
uuid
.
uuid4
)
# Actor associated with the attachment
actor
=
models
.
ForeignKey
(
...
...
api/funkwhale_api/common/utils.py
View file @
95497e76
...
...
@@ -303,6 +303,7 @@ def attach_content(obj, field, content_data):
if
existing
:
getattr
(
obj
,
field
).
delete
()
setattr
(
obj
,
field
,
None
)
if
not
content_data
:
return
...
...
api/funkwhale_api/common/views.py
View file @
95497e76
...
...
@@ -181,3 +181,15 @@ class AttachmentViewSet(
if
instance
.
actor
is
None
or
instance
.
actor
!=
self
.
request
.
user
.
actor
:
raise
exceptions
.
PermissionDenied
()
instance
.
delete
()
class
TextPreviewView
(
views
.
APIView
):
permission_classes
=
[]
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
payload
=
request
.
data
if
"text"
not
in
payload
:
return
response
.
Response
({
"detail"
:
"Invalid input"
},
status
=
400
)
data
=
{
"rendered"
:
utils
.
render_html
(
payload
[
"text"
],
"text/markdown"
)}
return
response
.
Response
(
data
,
status
=
200
)
api/funkwhale_api/federation/api_serializers.py
View file @
95497e76
from
django.core.exceptions
import
ObjectDoesNotExist
from
rest_framework
import
serializers
from
funkwhale_api.common
import
serializers
as
common_serializers
from
funkwhale_api.music
import
models
as
music_models
from
funkwhale_api.users
import
serializers
as
users_serializers
from
.
import
filters
from
.
import
models
...
...
@@ -169,3 +172,27 @@ class FetchSerializer(serializers.ModelSerializer):
"creation_date"
,
"fetch_date"
,
]
class
FullActorSerializer
(
serializers
.
Serializer
):
fid
=
serializers
.
URLField
()
url
=
serializers
.
URLField
()
domain
=
serializers
.
CharField
(
source
=
"domain_id"
)
creation_date
=
serializers
.
DateTimeField
()
last_fetch_date
=
serializers
.
DateTimeField
()
name
=
serializers
.
CharField
()
preferred_username
=
serializers
.
CharField
()
full_username
=
serializers
.
CharField
()
type
=
serializers
.
CharField
()
is_local
=
serializers
.
BooleanField
()
is_channel
=
serializers
.
SerializerMethodField
()
manually_approves_followers
=
serializers
.
BooleanField
()
user
=
users_serializers
.
UserBasicSerializer
()
summary
=
common_serializers
.
ContentSerializer
(
source
=
"summary_obj"
)
icon
=
common_serializers
.
AttachmentSerializer
(
source
=
"attachment_icon"
)
def
get_is_channel
(
self
,
o
):
try
:
return
bool
(
o
.
channel
)
except
ObjectDoesNotExist
:
return
False
api/funkwhale_api/federation/api_urls.py
View file @
95497e76
...
...
@@ -8,5 +8,6 @@ router.register(r"follows/library", api_views.LibraryFollowViewSet, "library-fol
router
.
register
(
r
"inbox"
,
api_views
.
InboxItemViewSet
,
"inbox"
)
router
.
register
(
r
"libraries"
,
api_views
.
LibraryViewSet
,
"libraries"
)
router
.
register
(
r
"domains"
,
api_views
.
DomainViewSet
,
"domains"
)
router
.
register
(
r
"actors"
,
api_views
.
ActorViewSet
,
"actors"
)
urlpatterns
=
router
.
urls
api/funkwhale_api/federation/api_views.py
View file @
95497e76
...
...
@@ -12,6 +12,7 @@ from rest_framework import viewsets
from
funkwhale_api.common
import
preferences
from
funkwhale_api.common.permissions
import
ConditionalAuthentication
from
funkwhale_api.music
import
models
as
music_models
from
funkwhale_api.music
import
views
as
music_views
from
funkwhale_api.users.oauth
import
permissions
as
oauth_permissions
from
.
import
activity
...
...
@@ -218,3 +219,34 @@ class DomainViewSet(
if
preferences
.
get
(
"moderation__allow_list_enabled"
):
qs
=
qs
.
filter
(
allowed
=
True
)
return
qs
class
ActorViewSet
(
mixins
.
RetrieveModelMixin
,
viewsets
.
GenericViewSet
):
queryset
=
models
.
Actor
.
objects
.
select_related
(
"user"
,
"channel"
,
"summary_obj"
,
"attachment_icon"
)
permission_classes
=
[
ConditionalAuthentication
]
serializer_class
=
api_serializers
.
FullActorSerializer
lookup_field
=
"full_username"
lookup_value_regex
=
r
"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)"
def
get_object
(
self
):
queryset
=
self
.
get_queryset
()
username
,
domain
=
self
.
kwargs
[
"full_username"
].
split
(
"@"
,
1
)
return
queryset
.
get
(
preferred_username
=
username
,
domain_id
=
domain
)
def
get_queryset
(
self
):
qs
=
super
().
get_queryset
()
qs
=
qs
.
exclude
(
domain__instance_policy__is_active
=
True
,
domain__instance_policy__block_all
=
True
,
)
if
preferences
.
get
(
"moderation__allow_list_enabled"
):
qs
=
qs
.
filter
(
domain__allowed
=
True
)
return
qs
libraries
=
decorators
.
action
(
methods
=
[
"get"
],
detail
=
True
)(
music_views
.
get_libraries
(
filter_uploads
=
lambda
o
,
uploads
:
uploads
.
filter
(
library__actor
=
o
)
)
)
api/funkwhale_api/federation/serializers.py
View file @
95497e76
...
...
@@ -253,7 +253,6 @@ class APIActorSerializer(serializers.ModelSerializer):
class
Meta
:
model
=
models
.
Actor
fields
=
[
"id"
,
"fid"
,
"url"
,
"creation_date"
,
...
...
api/funkwhale_api/music/models.py
View file @
95497e76
...
...
@@ -876,6 +876,12 @@ class Upload(models.Model):
def
listen_url
(
self
):
return
self
.
track
.
listen_url
+
"?upload={}"
.
format
(
self
.
uuid
)
def
get_listen_url
(
self
,
to
=
None
):
url
=
self
.
listen_url
if
to
:
url
+=
"&to={}"
.
format
(
to
)
return
url
@
property
def
listen_url_no_download
(
self
):
# Not using reverse because this is slow
...
...
api/funkwhale_api/music/serializers.py
View file @
95497e76
...
...
@@ -156,6 +156,19 @@ def serialize_artist_simple(artist):
else
None
)
if
"attachment_cover"
in
artist
.
_state
.
fields_cache
:
data
[
"cover"
]
=
(
cover_field
.
to_representation
(
artist
.
attachment_cover
)
if
artist
.
attachment_cover
else
None
)
if
getattr
(
artist
,
"_tracks_count"
,
None
)
is
not
None
:
data
[
"tracks_count"
]
=
artist
.
_tracks_count
if
getattr
(
artist
,
"_prefetched_tagged_items"
,
None
)
is
not
None
:
data
[
"tags"
]
=
[
ti
.
tag
.
name
for
ti
in
artist
.
_prefetched_tagged_items
]
return
data
...
...
api/funkwhale_api/subsonic/renderers.py
View file @
95497e76
...
...
@@ -5,6 +5,29 @@ from rest_framework import renderers
import
funkwhale_api
# from https://stackoverflow.com/a/8915039
# because I want to avoid a lxml dependency just for outputting cdata properly
# in a RSS feed
def
CDATA
(
text
=
None
):
element
=
ET
.
Element
(
"![CDATA["
)
element
.
text
=
text
return
element
ET
.
_original_serialize_xml
=
ET
.
_serialize_xml
def
_serialize_xml
(
write
,
elem
,
qnames
,
namespaces
,
**
kwargs
):
if
elem
.
tag
==
"![CDATA["
:
write
(
"<%s%s]]>"
%
(
elem
.
tag
,
elem
.
text
))
return
return
ET
.
_original_serialize_xml
(
write
,
elem
,
qnames
,
namespaces
,
**
kwargs
)
ET
.
_serialize_xml
=
ET
.
_serialize
[
"xml"
]
=
_serialize_xml
# end of tweaks
def
structure_payload
(
data
):
payload
=
{
"funkwhaleVersion"
:
funkwhale_api
.
__version__
,
...
...
@@ -56,7 +79,7 @@ def dict_to_xml_tree(root_tag, d, parent=None):
if
key
==
"value"
:
root
.
text
=
str
(
value
)
elif
key
==
"cdata_value"
:
root
.
text
=
"<![CDATA[{}]]>"
.
format
(
str
(
value
))
root
.
append
(
CDATA
(
value
))
else
:
root
.
set
(
key
,
str
(
value
))
return
root
api/funkwhale_api/users/models.py
View file @
95497e76
...
...
@@ -229,8 +229,8 @@ class User(AbstractUser):
self
.
last_activity
=
now
self
.
save
(
update_fields
=
[
"last_activity"
])
def
create_actor
(
self
):
self
.
actor
=
create_actor
(
self
)
def
create_actor
(
self
,
**
kwargs
):
self
.
actor
=
create_actor
(
self
,
**
kwargs
)
self
.
save
(
update_fields
=
[
"actor"
])
return
self
.
actor
...
...
@@ -264,15 +264,10 @@ class User(AbstractUser):
def
full_username
(
self
):
return
"{}@{}"
.
format
(
self
.
username
,
settings
.
FEDERATION_HOSTNAME
)
@
property
def
avatar_path
(
self
):
if
not
self
.
avatar
:
return
None
try
:
return
self
.
avatar
.
path
except
NotImplementedError
:
# external storage
return
self
.
avatar
.
name
def
get_avatar
(
self
):
if
not
self
.
actor
:
return
return
self
.
actor
.
attachment_icon
def
generate_code
(
length
=
10
):
...
...
@@ -399,8 +394,9 @@ def get_actor_data(username, **kwargs):
}
def
create_actor
(
user
):
def
create_actor
(
user
,
**
kwargs
):
args
=
get_actor_data
(
user
.
username
)
args
.
update
(
kwargs
)
private
,
public
=
keys
.
get_key_pair
()
args
[
"private_key"
]
=
private
.
decode
(
"utf-8"
)
args
[
"public_key"
]
=
public
.
decode
(
"utf-8"
)
...
...
api/funkwhale_api/users/serializers.py
View file @
95497e76
...
...
@@ -90,17 +90,12 @@ class UserActivitySerializer(activity_serializers.ModelSerializer):
class
UserBasicSerializer
(
serializers
.
ModelSerializer
):
avatar
=
serializers
.
SerializerMethodField
(
)
avatar
=
common_
serializers
.
AttachmentSerializer
(
source
=
"get_avatar"
)
class
Meta
:
model
=
models
.
User
fields
=
[
"id"
,
"username"
,
"name"
,
"date_joined"
,
"avatar"
]
def
get_avatar
(
self
,
o
):
return
common_serializers
.
AttachmentSerializer
(
o
.
actor
.
attachment_icon
if
o
.
actor
else
None
).
data
class
UserWriteSerializer
(
serializers
.
ModelSerializer
):
summary
=
common_serializers
.
ContentSerializer
(
required
=
False
,
allow_null
=
True
)
...
...
@@ -140,19 +135,12 @@ class UserWriteSerializer(serializers.ModelSerializer):
obj
.
actor
.
save
(
update_fields
=
[
"attachment_icon"
])
return
obj
def
to_representation
(
self
,
obj
):
repr
=
super
().
to_representation
(
obj
)
repr
[
"avatar"
]
=
common_serializers
.
AttachmentSerializer
(
obj
.
actor
.
attachment_icon
).
data
return
repr
class
UserReadSerializer
(
serializers
.
ModelSerializer
):
permissions
=
serializers
.
SerializerMethodField
()
full_username
=
serializers
.
SerializerMethodField
()
avatar
=
serializers
.
SerializerMethodField
(
)
avatar
=
common_
serializers
.
AttachmentSerializer
(
source
=
"get_avatar"
)
class
Meta
:
model
=
models
.
User
...
...
@@ -170,9 +158,6 @@ class UserReadSerializer(serializers.ModelSerializer):
"avatar"
,
]
def
get_avatar
(
self
,
o
):
return
common_serializers
.
AttachmentSerializer
(
o
.
actor
.
attachment_icon
).
data
def
get_permissions
(
self
,
o
):
return
o
.
get_permissions
()
...
...
api/tests/audio/test_serializers.py
View file @
95497e76
...
...
@@ -185,7 +185,6 @@ def test_rss_item_serializer(factories):
"itunes:subtitle"
:
[{
"value"
:
description
.
truncate
(
255
)}],
"itunes:summary"
:
[{
"cdata_value"
:
description
.
rendered
}],
"description"
:
[{
"value"
:
description
.
as_plain_text
}],
"content:encoded"
:
[{
"cdata_value"
:
description
.
rendered
}],
"guid"
:
[{
"cdata_value"
:
str
(
upload
.
uuid
),
"isPermalink"
:
"false"
}],
"pubDate"
:
[{
"value"
:
serializers
.
rss_date
(
upload
.
creation_date
)}],
"itunes:duration"
:
[{
"value"
:
serializers
.
rss_duration
(
upload
.
duration
)}],
...
...
@@ -197,7 +196,11 @@ def test_rss_item_serializer(factories):
"itunes:image"
:
[{
"href"
:
upload
.
track
.
attachment_cover
.
download_url_original
}],
"link"
:
[{
"value"
:
federation_utils
.
full_url
(
upload
.
track
.
get_absolute_url
())}],
"enclosure"
:
[
{
"url"
:
upload
.
listen_url
,
"length"
:
upload
.
size
,
"type"
:
upload
.
mimetype
}
{
"url"
:
federation_utils
.
full_url
(
upload
.
get_listen_url
(
"mp3"
)),
"length"
:
upload
.
size
,
"type"
:
upload
.
mimetype
,
}
],
}
...
...
api/tests/audio/test_views.py
View file @
95497e76
import
uuid
import
pytest
from
django.urls
import
reverse
from
funkwhale_api.audio
import
serializers
from
funkwhale_api.audio
import
views
def
test_channel_create
(
logged_in_api_client
):
...
...
@@ -23,8 +25,10 @@ def test_channel_create(logged_in_api_client):
assert
response
.
status_code
==
201
channel
=
actor
.
owned_channels
.
select_related
(
"artist__description"
).
latest
(
"id"
)
expected
=
serializers
.
ChannelSerializer
(
channel
).
data
channel
=
views
.
ChannelViewSet
.
queryset
.
get
(
attributed_to
=
actor
)
expected
=
serializers
.
ChannelSerializer
(
channel
,
context
=
{
"subscriptions_count"
:
True
}
).
data
assert
response
.
data
==
expected
assert
channel
.
artist
.
name
==
data
[
"name"
]
...
...
@@ -43,6 +47,9 @@ def test_channel_create(logged_in_api_client):
def
test_channel_detail
(
factories
,
logged_in_api_client
):
channel
=
factories
[
"audio.Channel"
](
artist__description
=
None
)
url
=
reverse
(
"api:v1:channels-detail"
,
kwargs
=
{
"uuid"
:
channel
.
uuid
})
setattr
(
channel
.
artist
,
"_tracks_count"
,
0
)
setattr
(
channel
.
artist
,
"_prefetched_tagged_items"
,
[])
expected
=
serializers
.
ChannelSerializer
(
channel
,
context
=
{
"subscriptions_count"
:
True
}
).
data
...
...
@@ -54,6 +61,8 @@ def test_channel_detail(factories, logged_in_api_client):
def
test_channel_list
(
factories
,
logged_in_api_client
):
channel
=
factories
[
"audio.Channel"
](
artist__description
=
None
)
setattr
(
channel
.
artist
,
"_tracks_count"
,
0
)
setattr
(
channel
.
artist
,
"_prefetched_tagged_items"
,
[])
url
=
reverse
(
"api:v1:channels-list"
)
expected
=
serializers
.
ChannelSerializer
(
channel
).
data
response
=
logged_in_api_client
.
get
(
url
)
...
...
@@ -142,8 +151,11 @@ def test_channel_subscribe(factories, logged_in_api_client):
assert
response
.
status_code
==
201
subscription
=
actor
.
emitted_follows
.
select_related
(
"target__channel__artist__description"
"target__channel__artist__description"
,
"target__channel__artist__attachment_cover"
,
).
latest
(
"id"
)
setattr
(
subscription
.
target
.
channel
.
artist
,
"_tracks_count"
,
0
)
setattr
(
subscription
.
target
.
channel
.
artist
,
"_prefetched_tagged_items"
,
[])
assert
subscription
.
fid
==
subscription
.
get_federation_id
()
expected
=
serializers
.
SubscriptionSerializer
(
subscription
).
data
assert
response
.
data
==
expected
...
...
@@ -168,6 +180,8 @@ def test_subscriptions_list(factories, logged_in_api_client):
actor
=
logged_in_api_client
.
user
.
create_actor
()
channel
=
factories
[
"audio.Channel"
](
artist__description
=
None
)
subscription
=
factories
[
"audio.Subscription"
](
target
=
channel
.
actor
,
actor
=
actor
)
setattr
(
subscription
.
target
.
channel
.
artist
,
"_tracks_count"
,
0
)
setattr
(
subscription
.
target
.
channel
.
artist
,
"_prefetched_tagged_items"
,
[])
factories
[
"audio.Subscription"
](
target
=
channel
.
actor
)
url
=
reverse
(
"api:v1:subscriptions-list"
)
expected
=
serializers
.
SubscriptionSerializer
(
subscription
).
data
...
...
@@ -192,7 +206,10 @@ def test_subscriptions_all(factories, logged_in_api_client):
response
=
logged_in_api_client
.
get
(
url
)
assert
response
.
status_code
==
200
assert
response
.
data
==
{
"results"
:
[
subscription
.
uuid
],
"count"
:
1
}
assert
response
.
data
==
{
"results"
:
[{
"uuid"
:
subscription
.
uuid
,
"channel"
:
uuid
.
UUID
(
channel
.
uuid
)}],
"count"
:
1
,
}
def
test_channel_rss_feed
(
factories
,
api_client
):
...
...
api/tests/common/test_filters.py
View file @
95497e76
...
...
@@ -50,6 +50,8 @@ def test_mutation_filter_is_approved(value, expected, factories):
(
"noop"
,
0
,
[]),
(
"noop"
,
1
,
[]),
(
"noop"
,
2
,
[]),
(
"actor:actor1@domain.test"
,
0
,
[
0
]),
(
"actor:actor2@domain.test"
,
0
,
[
1
]),
],
)
def
test_actor_scope_filter
(
...
...
@@ -61,8 +63,13 @@ def test_actor_scope_filter(
mocker
,
anonymous_user
,
):
actor1
=
factories
[
"users.User"
]().
create_actor
()
actor2
=
factories
[
"users.User"
]().
create_actor
()
domain
=
factories
[
"federation.Domain"
](
name
=
"domain.test"
)
actor1
=
factories
[
"users.User"
]().
create_actor
(
preferred_username
=
"actor1"
,
domain
=
domain
)
actor2
=
factories
[
"users.User"
]().
create_actor
(
preferred_username
=
"actor2"
,
domain
=
domain
)
users
=
[
actor1
.
user
,
actor2
.
user
,
anonymous_user
]
tracks
=
[
factories
[
"music.Upload"
](
library__actor
=
actor1
,
playable
=
True
).
track
,
...
...
api/tests/common/test_views.py
View file @
95497e76
...
...
@@ -7,6 +7,7 @@ from funkwhale_api.common import serializers
from
funkwhale_api.common
import
signals
from
funkwhale_api.common
import
tasks
from
funkwhale_api.common
import
throttling
from
funkwhale_api.common
import
utils
def
test_can_detail_mutation
(
logged_in_api_client
,
factories
):
...
...
@@ -270,3 +271,13 @@ def test_attachment_destroy_not_owner(factories, logged_in_api_client):
assert
response
.
status_code
==
403
attachment
.
refresh_from_db
()
def
test_can_render_text_preview
(
api_client
,
db
):
payload
=
{
"text"
:
"Hello world"
}
url
=
reverse
(
"api:v1:text-preview"
)