loads of things

Part-of: <https://dev.funkwhale.audio/funkwhale/funkwhale/-/merge_requests/2658>
This commit is contained in:
Petitminion 2023-12-20 00:08:48 +01:00 committed by Ciarán Ainsworth
parent f45fd1e465
commit 37acfa475d
6 changed files with 250 additions and 54 deletions

View File

@ -25,7 +25,8 @@ def submit_listen(listening, conf, **kwargs):
client.submit_single_listen(listen) client.submit_single_listen(listen)
def get_listen(track): def get_listen(listening):
track = listening.track
additional_info = { additional_info = {
"media_player": "Funkwhale", "media_player": "Funkwhale",
"media_player_version": funkwhale_api.__version__, "media_player_version": funkwhale_api.__version__,
@ -54,7 +55,7 @@ def get_listen(track):
return liblistenbrainz.Listen( return liblistenbrainz.Listen(
track_name=track.title, track_name=track.title,
artist_name=track.artist.name, artist_name=track.artist.name,
listened_at=int(timezone.now()), listened_at=listening.creation_date.timestamp(),
release_name=release_name, release_name=release_name,
additional_info=additional_info, additional_info=additional_info,
) )
@ -74,7 +75,7 @@ def submit_favorite_creation(track_favorite, conf, **kwargs):
"This tracks doesn't have a mbid. Feedback will not be sublited to Listenbrainz" "This tracks doesn't have a mbid. Feedback will not be sublited to Listenbrainz"
) )
return return
# client.feedback(track, 1) client.submit_user_feedback(1, track.mbid)
@plugins.register_hook(plugins.FAVORITE_DELETED, PLUGIN) @plugins.register_hook(plugins.FAVORITE_DELETED, PLUGIN)
@ -91,13 +92,12 @@ def submit_favorite_deletion(track_favorite, conf, **kwargs):
"This tracks doesn't have a mbid. Feedback will not be submited to Listenbrainz" "This tracks doesn't have a mbid. Feedback will not be submited to Listenbrainz"
) )
return return
# client.feedback(track, 0) client.submit_user_feedback(0, track.mbid)
@plugins.register_hook(plugins.LISTENING_SYNC, PLUGIN) @plugins.register_hook(plugins.LISTENING_SYNC, PLUGIN)
def sync_listenings_from_listenbrainz(user, conf): def sync_listenings_from_listenbrainz(user, conf):
user_name = conf["user_name"] user_name = conf["user_name"]
user_token = conf["user_token"]
if not user_name or not conf["sync_listenings"]: if not user_name or not conf["sync_listenings"]:
return return
@ -110,8 +110,10 @@ def sync_listenings_from_listenbrainz(user, conf):
.latest("creation_date") .latest("creation_date")
.values_list("creation_date", flat=True) .values_list("creation_date", flat=True)
) )
last_ts.timestamp()
except history_models.Listening.DoesNotExist: except history_models.Listening.DoesNotExist:
tasks.import_listenbrainz_listenings(user, user_name, ts=0) tasks.import_listenbrainz_listenings(user, user_name, ts=0)
return
tasks.import_listenbrainz_listenings(user, user_name, ts=last_ts) tasks.import_listenbrainz_listenings(user, user_name, ts=last_ts)
@ -119,7 +121,6 @@ def sync_listenings_from_listenbrainz(user, conf):
@plugins.register_hook(plugins.FAVORITE_SYNC, PLUGIN) @plugins.register_hook(plugins.FAVORITE_SYNC, PLUGIN)
def sync_favorites_from_listenbrainz(user, conf): def sync_favorites_from_listenbrainz(user, conf):
user_name = conf["user_name"] user_name = conf["user_name"]
user_token = conf["user_token"]
if not user_name or not conf["sync_favorites"]: if not user_name or not conf["sync_favorites"]:
return return
@ -130,5 +131,9 @@ def sync_favorites_from_listenbrainz(user, conf):
.latest("creation_date") .latest("creation_date")
.values_list("creation_date", flat=True) .values_list("creation_date", flat=True)
) )
last_ts.timestamp()
except history_models.Listening.DoesNotExist: except history_models.Listening.DoesNotExist:
tasks.import_listenbrainz_favorites(user, user_name, last_ts) tasks.import_listenbrainz_favorites(user, user_name, ts=0)
return
tasks.import_listenbrainz_favorites(user, user_name, ts=last_ts)

View File

@ -3,6 +3,7 @@ import pylistenbrainz
from django.utils import timezone from django.utils import timezone
from config import plugins from config import plugins
from funkwhale_api.favorites import models as favorites_models
from funkwhale_api.history import models as history_models from funkwhale_api.history import models as history_models
from funkwhale_api.music import models as music_models from funkwhale_api.music import models as music_models
from funkwhale_api.taskapp import celery from funkwhale_api.taskapp import celery
@ -53,13 +54,24 @@ def import_listenbrainz_listenings(user, user_name, ts):
new_ts = 13 new_ts = 13
last_ts = 12 last_ts = 12
while new_ts != last_ts: while new_ts != last_ts:
last_ts = listens[0].listened_at last_ts = max(
listens,
key=lambda obj: datetime.datetime.fromtimestamp(
obj.listened_at, timezone.utc
),
)
listens = client.get_listens(username=user_name, min_ts=new_ts, count=100) listens = client.get_listens(username=user_name, min_ts=new_ts, count=100)
new_ts = listens[0].listened_at new_ts = max(
listens,
key=lambda obj: datetime.datetime.fromtimestamp(
obj.listened_at, timezone.utc
),
)
add_lb_listenings_to_db(listens, user) add_lb_listenings_to_db(listens, user)
def add_lb_listenings_to_db(listens, user): def add_lb_listenings_to_db(listens, user):
logger = PLUGIN["logger"]
fw_listens = [] fw_listens = []
for listen in listens: for listen in listens:
if ( if (
@ -67,9 +79,14 @@ def add_lb_listenings_to_db(listens, user):
and listen.additional_info.get("submission_client") and listen.additional_info.get("submission_client")
== "Funkwhale ListenBrainz plugin" == "Funkwhale ListenBrainz plugin"
and history_models.Listening.objects.filter( and history_models.Listening.objects.filter(
creation_date=listen.listened_at creation_date=datetime.datetime.fromtimestamp(
listen.listened_at, timezone.utc
)
).exists() ).exists()
): ):
logger.info(
f"Listen with ts {listen.listened_at} skipped because already in db"
)
continue continue
mbid = ( mbid = (
@ -79,7 +96,6 @@ def add_lb_listenings_to_db(listens, user):
) )
if not mbid: if not mbid:
logger = PLUGIN["logger"]
logger.info("Received listening doesn't have a mbid. Skipping...") logger.info("Received listening doesn't have a mbid. Skipping...")
try: try:
@ -90,7 +106,9 @@ def add_lb_listenings_to_db(listens, user):
user = user user = user
fw_listen = history_models.Listening( fw_listen = history_models.Listening(
creation_date=timezone.make_aware(listen.listened_at), creation_date=datetime.datetime.fromtimestamp(
listen.listened_at, timezone.utc
),
track=track, track=track,
user=user, user=user,
source="Listenbrainz", source="Listenbrainz",
@ -101,5 +119,46 @@ def add_lb_listenings_to_db(listens, user):
@celery.app.task(name="listenbrainz.import_listenbrainz_favorites") @celery.app.task(name="listenbrainz.import_listenbrainz_favorites")
def import_listenbrainz_favorites(): def import_listenbrainz_favorites(user, user_name, last_sync):
return "to do" client = pylistenbrainz.ListenBrainz()
last_ts = int(datetime.datetime.now(timezone.utc).timestamp())
offset = 0
while last_ts >= last_sync:
feedbacks = client.get_user_feedback(username=user_name, offset=offset)
add_lb_feedback_to_db(feedbacks, user)
offset = feedbacks.count
last_ts = max(
feedbacks.feedback,
key=lambda obj: datetime.datetime.fromtimestamp(obj.created, timezone.utc),
)
# to do implement offset in pylb
def add_lb_feedback_to_db(feedbacks, user):
logger = PLUGIN["logger"]
fw_listens = []
for feedback in feedbacks.feedback:
try:
track = music_models.Track.objects.get(mbid=feedback.recording_mbid)
except music_models.Track.DoesNotExist:
logger.info(
"Received feedback track doesn't exist in fw database. Skipping..."
)
continue
if feedback.score == 1:
favorites_models.TrackFavorite.objects.get_or_create(
user=user,
creation_date=datetime.datetime.fromtimestamp(
feedback.created, timezone.utc
),
track=track,
source="Listenbrainz",
)
elif feedback.score == 0:
try:
favorites_models.TrackFavorite.objects.delete(user=user, track=track)
except favorites_models.TrackFavorite.DoesNotExist:
continue
elif feedback.score == -1:
logger.info("Funkwhale doesn't support hate yet <3")

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2023-12-09 14:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('favorites', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='trackfavorite',
name='source',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2023-12-09 14:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('history', '0002_auto_20180325_1433'),
]
operations = [
migrations.AddField(
model_name='listening',
name='source',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

View File

@ -1,6 +1,15 @@
import datetime
import logging
import pytest import pytest
import pylistenbrainz
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from config import plugins from config import plugins
from funkwhale_api.contrib.listenbrainz import funkwhale_ready
from funkwhale_api.favorites import models as favorites_models
from funkwhale_api.history import models as history_models from funkwhale_api.history import models as history_models
@ -33,3 +42,124 @@ def test_listenbrainz_submit_listen(logged_in_client, mocker, factories):
listening = history_models.Listening.objects.get(user=logged_in_client.user) listening = history_models.Listening.objects.get(user=logged_in_client.user)
handler.assert_called_once_with(listening=listening, conf=None) handler.assert_called_once_with(listening=listening, conf=None)
# why conf=none ? # why conf=none ?
def test_sync_listenings_from_listenbrainz(factories, mocker, caplog):
logger = logging.getLogger("plugins")
caplog.set_level(logging.INFO)
logger.addHandler(caplog.handler)
user = factories["users.User"]()
factories["music.Track"](mbid="f89db7f8-4a1f-4228-a0a1-e7ba028b7476")
track = factories["music.Track"](mbid="54c60860-f43d-484e-b691-7ab7ec8de559")
factories["history.Listening"](
creation_date=datetime.datetime.fromtimestamp(1871, timezone.utc), track=track
)
conf = {
"user_name": user.username,
"user_token": "user_tolkien",
"sync_listenings": True,
}
listens = [
pylistenbrainz.Listen(
track_name="test",
artist_name="artist_test",
recording_mbid="f89db7f8-4a1f-4228-a0a1-e7ba028b7476",
additional_info={"submission_client": "not funkwhale"},
listened_at=-3124224000,
),
pylistenbrainz.Listen(
track_name="test2",
artist_name="artist_test2",
recording_mbid="54c60860-f43d-484e-b691-7ab7ec8de559",
additional_info={"submission_client": "Funkwhale ListenBrainz plugin"},
listened_at=1871,
),
pylistenbrainz.Listen(
track_name="test3",
artist_name="artist_test3",
listened_at=0,
),
]
mocker.patch.object(
funkwhale_ready.tasks.pylistenbrainz.ListenBrainz,
"get_listens",
return_value=listens,
)
funkwhale_ready.sync_listenings_from_listenbrainz(user, conf)
assert history_models.Listening.objects.filter(
track__mbid="f89db7f8-4a1f-4228-a0a1-e7ba028b7476"
).exists()
assert "Listen with ts 1871 skipped because already in db" in caplog.text
assert "Received listening doesn't have a mbid" in caplog.text
def test_sync_favorites_from_listenbrainz(factories, mocker, caplog):
logger = logging.getLogger("plugins")
caplog.set_level(logging.INFO)
logger.addHandler(caplog.handler)
user = factories["users.User"]()
factories["music.Track"](mbid="195565db-65f9-4d0d-b347-5f0c85509528")
factories["music.Track"](mbid="54c60860-f43d-484e-b691-7ab7ec8de559")
track = factories["music.Track"](mbid="c5af5351-dbbf-4481-b52e-a480b6c57986")
favorite = factories["favorites.TrackFavorite"](track=track)
conf = {
"user_name": user.username,
"user_token": "user_tolkien",
"sync_favorites": True,
}
feedbacks = {
"count": 5,
"feedback": [
{
"created": 1701116226,
"recording_mbid": "195565db-65f9-4d0d-b347-5f0c85509528",
"score": 1,
"user_id": user.username,
},
{
"created": 1701116214,
"recording_mbid": "c5af5351-dbbf-4481-b52e-a480b6c57986",
"score": 0,
"user_id": user.username,
},
{
"created": 1690775094,
"recording_mbid": "c878ef2f-c08d-4a81-a047-f2a9f978cec7",
"score": -1,
"user_id": user.username,
},
{
"created": 1690775093,
"recording_mbid": "1fd02cf2-7247-4715-8862-c378ec1965d2 ",
"score": 1,
"user_id": user.username,
},
],
"offset": 0,
"total_count": 4,
}
mocker.patch.object(
funkwhale_ready.tasks.pylistenbrainz.ListenBrainz,
"get_user_feedback",
return_value=feedbacks,
)
funkwhale_ready.sync_favorites_from_listenbrainz(user, conf)
assert favorites_models.TrackFavorite.objects.filter(
track__mbid="195565db-65f9-4d0d-b347-5f0c85509528"
).exists()
with pytest.raises(deleted.DoesNotExist):
favorite.refresh_from_db()

View File

@ -1,49 +1,15 @@
import datetime import datetime
import logging
import pylistenbrainz import pylistenbrainz
import pytest import pytest
from django.utils import timezone
from funkwhale_api.contrib.listenbrainz import tasks from funkwhale_api.contrib.listenbrainz import tasks
from funkwhale_api.history import models as history_models from funkwhale_api.history import models as history_models
def test_import_listenbrainz_listenings(factories, mocker): def test_trigger_listening_sync_with_listenbrainz():
factories["music.Track"](mbid="f89db7f8-4a1f-4228-a0a1-e7ba028b7476") # to do
factories["music.Track"](mbid="54c60860-f43d-484e-b691-7ab7ec8de559") pass
listens = [
pylistenbrainz.utils.Listen(
track_name="test",
artist_name="artist_test",
recording_mbid="f89db7f8-4a1f-4228-a0a1-e7ba028b7476",
additional_info={"submission_client": "not funkwhale"},
listened_at=datetime.datetime.fromtimestamp(-3124224000),
),
pylistenbrainz.utils.Listen(
track_name="test2",
artist_name="artist_test2",
recording_mbid="54c60860-f43d-484e-b691-7ab7ec8de559",
additional_info={"submission_client": "Funkwhale ListenBrainz plugin"},
listened_at=datetime.datetime.fromtimestamp(1871),
),
pylistenbrainz.utils.Listen(
track_name="test3",
artist_name="artist_test3",
listened_at=0,
),
]
mocker.patch.object(
tasks.pylistenbrainz.ListenBrainz, "get_listens", return_value=listens
)
user = factories["users.User"]()
tasks.import_listenbrainz_listenings(user, "user_name", ts=0)
history_models.Listening.objects.filter(
track__mbid="f89db7f8-4a1f-4228-a0a1-e7ba028b7476"
).exists()
assert not history_models.Listening.objects.filter(
track__mbid="54c60860-f43d-484e-b691-7ab7ec8de559"
).exists()