Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • funkwhale/funkwhale
  • Luclu7/funkwhale
  • mbothorel/funkwhale
  • EorlBruder/funkwhale
  • tcit/funkwhale
  • JocelynDelalande/funkwhale
  • eneiluj/funkwhale
  • reg/funkwhale
  • ButterflyOfFire/funkwhale
  • m4sk1n/funkwhale
  • wxcafe/funkwhale
  • andybalaam/funkwhale
  • jcgruenhage/funkwhale
  • pblayo/funkwhale
  • joshuaboniface/funkwhale
  • n3ddy/funkwhale
  • gegeweb/funkwhale
  • tohojo/funkwhale
  • emillumine/funkwhale
  • Te-k/funkwhale
  • asaintgenis/funkwhale
  • anoadragon453/funkwhale
  • Sakada/funkwhale
  • ilianaw/funkwhale
  • l4p1n/funkwhale
  • pnizet/funkwhale
  • dante383/funkwhale
  • interfect/funkwhale
  • akhardya/funkwhale
  • svfusion/funkwhale
  • noplanman/funkwhale
  • nykopol/funkwhale
  • roipoussiere/funkwhale
  • Von/funkwhale
  • aurieh/funkwhale
  • icaria36/funkwhale
  • floreal/funkwhale
  • paulwalko/funkwhale
  • comradekingu/funkwhale
  • FurryJulie/funkwhale
  • Legolars99/funkwhale
  • Vierkantor/funkwhale
  • zachhats/funkwhale
  • heyjake/funkwhale
  • sn0w/funkwhale
  • jvoisin/funkwhale
  • gordon/funkwhale
  • Alexander/funkwhale
  • bignose/funkwhale
  • qasim.ali/funkwhale
  • fakegit/funkwhale
  • Kxze/funkwhale
  • stenstad/funkwhale
  • creak/funkwhale
  • Kaze/funkwhale
  • Tixie/funkwhale
  • IISergII/funkwhale
  • lfuelling/funkwhale
  • nhaddag/funkwhale
  • yoasif/funkwhale
  • ifischer/funkwhale
  • keslerm/funkwhale
  • flupe/funkwhale
  • petitminion/funkwhale
  • ariasuni/funkwhale
  • ollie/funkwhale
  • ngaumont/funkwhale
  • techknowlogick/funkwhale
  • Shleeble/funkwhale
  • theflyingfrog/funkwhale
  • jonatron/funkwhale
  • neobrain/funkwhale
  • eorn/funkwhale
  • KokaKiwi/funkwhale
  • u1-liquid/funkwhale
  • marzzzello/funkwhale
  • sirenwatcher/funkwhale
  • newer027/funkwhale
  • codl/funkwhale
  • Zwordi/funkwhale
  • gisforgabriel/funkwhale
  • iuriatan/funkwhale
  • simon/funkwhale
  • bheesham/funkwhale
  • zeoses/funkwhale
  • accraze/funkwhale
  • meliurwen/funkwhale
  • divadsn/funkwhale
  • Etua/funkwhale
  • sdrik/funkwhale
  • Soran/funkwhale
  • kuba-orlik/funkwhale
  • cristianvogel/funkwhale
  • Forceu/funkwhale
  • jeff/funkwhale
  • der_scheibenhacker/funkwhale
  • owlnical/funkwhale
  • jovuit/funkwhale
  • SilverFox15/funkwhale
  • phw/funkwhale
  • mayhem/funkwhale
  • sridhar/funkwhale
  • stromlin/funkwhale
  • rrrnld/funkwhale
  • nitaibezerra/funkwhale
  • jaller94/funkwhale
  • pcouy/funkwhale
  • eduxstad/funkwhale
  • codingHahn/funkwhale
  • captain/funkwhale
  • polyedre/funkwhale
  • leishenailong/funkwhale
  • ccritter/funkwhale
  • lnceballosz/funkwhale
  • fpiesche/funkwhale
  • Fanyx/funkwhale
  • markusblogde/funkwhale
  • Firobe/funkwhale
  • devilcius/funkwhale
  • freaktechnik/funkwhale
  • blopware/funkwhale
  • cone/funkwhale
  • thanksd/funkwhale
  • vachan-maker/funkwhale
  • bbenti/funkwhale
  • tarator/funkwhale
  • prplecake/funkwhale
  • DMarzal/funkwhale
  • lullis/funkwhale
  • hanacgr/funkwhale
  • albjeremias/funkwhale
  • xeruf/funkwhale
  • llelite/funkwhale
  • RoiArthurB/funkwhale
  • cloo/funkwhale
  • nztvar/funkwhale
  • Keunes/funkwhale
  • petitminion/funkwhale-petitminion
  • m-idler/funkwhale
  • SkyLeite/funkwhale
140 results
Select Git revision
Show changes
from funkwhale_api import plugins
from . import scrobbler
plugin = plugins.get_plugin("fw_scrobbler")
# https://listenbrainz.org/lastfm-proxy
DEFAULT_SCROBBLER_URL = "http://post.audioscrobbler.com"
@plugin.hooks.connect("history.listening.created")
def forward_to_scrobblers(listening, plugin_conf, **kwargs):
if plugin_conf["user"] is None:
raise plugins.Skip()
username = plugin_conf["user"]["settings"].get("service__username")
password = plugin_conf["user"]["settings"].get("service__password")
url = plugin_conf["user"]["settings"].get("service__url", DEFAULT_SCROBBLER_URL)
if username and password:
plugin.logger.info("Forwarding scrobbler to %s", url)
session = plugin.get_requests_session()
session_key, _, scrobble_url = scrobbler.handshake_v1(
session=session, url=url, username=username, password=password
)
scrobbler.submit_scrobble_v1(
session=session,
track=listening.track,
scrobble_time=listening.creation_date,
session_key=session_key,
scrobble_url=scrobble_url,
)
else:
plugin.logger.debug("No scrobbler configuration for user, skipping")
@plugin.hooks.connect("history.listening.now")
def forward_to_now_playing(track, user, plugin_conf, **kwargs):
if plugin_conf["user"] is None:
raise plugins.Skip()
username = plugin_conf["user"]["settings"].get("service__username")
password = plugin_conf["user"]["settings"].get("service__password")
url = plugin_conf["user"]["settings"].get("service__url", DEFAULT_SCROBBLER_URL)
if username and password:
plugin.logger.info("Forwarding scrobbler to %s", url)
session = plugin.get_requests_session()
session_key, now_playing_url, _ = scrobbler.handshake_v1(
session=session, url=url, username=username, password=password
)
scrobbler.submit_now_playing_v1(
session=session,
track=track,
session_key=session_key,
now_playing_url=now_playing_url,
)
else:
plugin.logger.debug("No scrobbler configuration for user, skipping")
import hashlib
import time
import time
from funkwhale_api import plugins
from . import scrobbler
# https://github.com/jlieth/legacy-scrobbler
plugin = plugins.get_plugin("fw_scrobbler")
class ScrobblerException(Exception):
pass
def handshake_v1(session, url, username, password):
timestamp = str(int(time.time())).encode("utf-8")
password_hash = hashlib.md5(password.encode("utf-8")).hexdigest()
auth = hashlib.md5(password_hash.encode("utf-8") + timestamp).hexdigest()
params = {
"hs": "true",
"p": "1.2",
"c": plugin.name,
"v": plugin.version,
"u": username,
"t": timestamp,
"a": auth,
}
session = plugin.get_requests_session()
plugin.logger.debug(
"Performing scrobbler handshake for username %s at %s", username, url
)
handshake_response = session.get(url, params=params)
# process response
result = handshake_response.text.split("\n")
if len(result) >= 4 and result[0] == "OK":
session_key = result[1]
nowplaying_url = result[2]
scrobble_url = result[3]
elif result[0] == "BANNED":
raise ScrobblerException("BANNED")
elif result[0] == "BADAUTH":
raise ScrobblerException("BADAUTH")
elif result[0] == "BADTIME":
raise ScrobblerException("BADTIME")
else:
raise ScrobblerException(handshake_response.text)
plugin.logger.debug("Handshake successful, scrobble url: %s", scrobble_url)
return session_key, nowplaying_url, scrobble_url
def submit_scrobble_v1(session, scrobble_time, track, session_key, scrobble_url):
payload = get_scrobble_payload(track, scrobble_time)
plugin.logger.debug("Sending scrobble with payload %s", payload)
payload["s"] = session_key
response = session.post(scrobble_url, payload)
response.raise_for_status()
if response.text.startswith("OK"):
return
elif response.text.startswith("BADSESSION"):
raise ScrobblerException("Remote server says the session is invalid")
else:
raise ScrobblerException(response.text)
plugin.logger.debug("Scrobble successfull!")
def submit_now_playing_v1(session, track, session_key, now_playing_url):
payload = get_scrobble_payload(track, date=None, suffix="")
plugin.logger.debug("Sending now playing with payload %s", payload)
payload["s"] = session_key
response = session.post(now_playing_url, payload)
response.raise_for_status()
if response.text.startswith("OK"):
return
elif response.text.startswith("BADSESSION"):
raise ScrobblerException("Remote server says the session is invalid")
else:
raise ScrobblerException(response.text)
plugin.logger.debug("Now playing successfull!")
def get_scrobble_payload(track, date, suffix="[0]"):
"""
Documentation available at https://web.archive.org/web/20190531021725/https://www.last.fm/api/submissions
"""
upload = track.uploads.filter(duration__gte=0).first()
data = {
"a{}".format(suffix): track.artist.name,
"t{}".format(suffix): track.title,
"l{}".format(suffix): upload.duration if upload else 0,
"b{}".format(suffix): track.album.title or "",
"n{}".format(suffix): track.position or "",
"m{}".format(suffix): str(track.mbid) or "",
"o{}".format(suffix): "P", # Source: P = chosen by user
}
if date:
data["i{}".format(suffix)] = int(date.timestamp())
return data
...@@ -24,6 +24,7 @@ from aioresponses import aioresponses ...@@ -24,6 +24,7 @@ from aioresponses import aioresponses
from dynamic_preferences.registries import global_preferences_registry from dynamic_preferences.registries import global_preferences_registry
from rest_framework.test import APIClient, APIRequestFactory from rest_framework.test import APIClient, APIRequestFactory
from funkwhale_api import plugins
from funkwhale_api.activity import record from funkwhale_api.activity import record
from funkwhale_api.federation import actors from funkwhale_api.federation import actors
from funkwhale_api.moderation import mrf from funkwhale_api.moderation import mrf
...@@ -422,3 +423,23 @@ def clear_license_cache(db): ...@@ -422,3 +423,23 @@ def clear_license_cache(db):
licenses._cache = None licenses._cache = None
yield yield
licenses._cache = None licenses._cache = None
@pytest.fixture
def plugin_class():
class DummyPlugin(plugins.Plugin):
path = "noop"
return DummyPlugin
@pytest.fixture
def plugin(plugin_class):
return plugin_class("test", "test")
@pytest.fixture
def plugins_conf(mocker):
plugins_conf = mocker.patch("funkwhale_api.plugins.generate_plugins_conf")
return plugins_conf.return_value
from funkwhale_api import plugins
from funkwhale_api.federation import serializers as federation_serializers from funkwhale_api.federation import serializers as federation_serializers
from funkwhale_api.history import serializers from funkwhale_api.history import serializers
from funkwhale_api.music import serializers as music_serializers from funkwhale_api.music import serializers as music_serializers
...@@ -18,3 +19,27 @@ def test_listening_serializer(factories, to_api_date): ...@@ -18,3 +19,27 @@ def test_listening_serializer(factories, to_api_date):
serializer = serializers.ListeningSerializer(listening) serializer = serializers.ListeningSerializer(listening)
assert serializer.data == expected assert serializer.data == expected
def test_listening_create(factories, to_api_date, mocker, now):
user = factories["users.User"]()
track = factories["music.Track"]()
payload = {"track": track.pk}
on_commit = mocker.patch("funkwhale_api.common.utils.on_commit")
request = mocker.Mock(plugins_conf=mocker.Mock())
serializer = serializers.ListeningWriteSerializer(
data=payload, context={"request": request, "user": user}
)
assert serializer.is_valid(raise_exception=True) is True
listening = serializer.save()
assert serializer.instance.user == user
assert serializer.instance.track == track
on_commit.assert_called_once_with(
plugins.hooks.dispatch,
"history.listening.created",
listening=listening,
plugins_conf=request.plugins_conf,
)
...@@ -2,6 +2,8 @@ import pytest ...@@ -2,6 +2,8 @@ import pytest
from django.urls import reverse from django.urls import reverse
from funkwhale_api import plugins
@pytest.mark.parametrize("level", ["instance", "me", "followers"]) @pytest.mark.parametrize("level", ["instance", "me", "followers"])
def test_privacy_filter(preferences, level, factories, api_client): def test_privacy_filter(preferences, level, factories, api_client):
...@@ -11,3 +13,20 @@ def test_privacy_filter(preferences, level, factories, api_client): ...@@ -11,3 +13,20 @@ def test_privacy_filter(preferences, level, factories, api_client):
response = api_client.get(url) response = api_client.get(url)
assert response.status_code == 200 assert response.status_code == 200
assert response.data["count"] == 0 assert response.data["count"] == 0
def test_now(factories, logged_in_api_client, plugins_conf, mocker):
track = factories["music.Track"]()
url = reverse("api:v1:history:listenings-now")
on_commit = mocker.patch("funkwhale_api.common.utils.on_commit")
response = logged_in_api_client.post(url, {"track": track.pk})
on_commit.assert_called_once_with(
plugins.hooks.dispatch,
"history.listening.now",
track=track,
user=logged_in_api_client.user,
plugins_conf=plugins_conf,
)
assert response.status_code == 204
import pytest
from funkwhale_api import plugins
from funkwhale_api.plugins import models
def test_plugin_ready_calls_load(mocker, plugin):
load = mocker.spy(plugin, "load")
plugin.ready()
load.assert_called_once_with()
def test_get_plugin_not_found(mocker):
get_app_config = mocker.patch(
"django.apps.apps.get_app_config", side_effect=LookupError
)
with pytest.raises(plugins.PluginNotFound):
plugins.get_plugin("noop")
get_app_config.assert_called_once_with("noop")
def test_get_plugin_not_plugin(mocker):
get_app_config = mocker.spy(plugins.apps.apps, "get_app_config")
with pytest.raises(plugins.PluginNotFound):
plugins.get_plugin("music")
get_app_config.assert_called_once_with("music")
def test_get_plugin_valid(mocker):
get_app_config = mocker.patch("django.apps.apps.get_app_config")
get_app_config.return_value = mocker.Mock(_is_funkwhale_plugin=True)
assert plugins.get_plugin("test") is get_app_config.return_value
get_app_config.assert_called_once_with("test")
def test_plugin_attributes(plugin):
assert isinstance(plugin.hooks, plugins.HookRegistry)
assert isinstance(plugin.settings, plugins.SettingRegistry)
assert isinstance(plugin.user_settings, plugins.SettingRegistry)
def test_plugin_hook_connect(plugin):
@plugin.hooks.connect("hook_name")
def handler(**kwargs):
pass
assert plugin.hooks["hook_name"] == handler
def test_plugin_user_settings_register(plugin):
@plugin.user_settings.register
class TestSetting(plugins.config.StringSetting):
section = plugins.config.SettingSection("test")
name = "test_setting"
default = ""
assert plugin.user_settings["test__test_setting"] == TestSetting
def test_plugin_settings_register(plugin):
@plugin.settings.register
class TestSetting(plugins.config.StringSetting):
section = plugins.config.SettingSection("test")
name = "test_setting"
default = ""
assert plugin.settings["test__test_setting"] == TestSetting
def test_get_all_plugins(mocker):
pl1 = mocker.Mock(_is_funkwhale_plugin=True)
pl2 = mocker.Mock(_is_funkwhale_plugin=True)
app = mocker.Mock(_is_funkwhale_plugin=False)
mocker.patch("django.apps.apps.get_app_configs", return_value=[pl1, pl2, app])
all_plugins = plugins.get_all_plugins()
assert all_plugins == [pl1, pl2]
def test_generate_plugins_conf(factories, plugin_class):
plugin1 = plugin_class("test1", "test1")
plugin2 = plugin_class("test3", "test3")
plugin3 = plugin_class("test4", "test4")
# this one is enabled
plugin1_db_conf = factories["plugins.Plugin"](name=plugin1.name)
# this one is disabled at the plugin level, so it shouldn't appear in the final conf
factories["plugins.Plugin"](name=plugin3.name, is_enabled=False)
# this one doesn't match any registered app
factories["plugins.Plugin"](name="noop")
expected = [{"obj": plugin1, "settings": plugin1_db_conf.config, "user": None}]
conf = plugins.generate_plugins_conf([plugin1, plugin2, plugin3])
assert conf == expected
def test_update_plugins_conf_with_user_settings(factories, plugin_class):
plugin1 = plugin_class("test1", "test1")
plugin2 = plugin_class("test2", "test2")
plugin3 = plugin_class("test3", "test3")
user = factories["users.User"]()
# user has enabled this plugin and has custom settings
plugin1_user_conf = factories["plugins.UserPlugin"](
plugin__name=plugin1.name, user=user
)
# plugin is disabled by user
plugin2_user_conf = factories["plugins.UserPlugin"](
plugin__name=plugin2.name, user=user, is_enabled=False
)
# Plugin is enabled by another user
plugin3_user_conf = factories["plugins.UserPlugin"](plugin__name=plugin3.name)
expected = [
{
"obj": plugin1,
"settings": plugin1_user_conf.plugin.config,
"user": {"id": user.pk, "settings": plugin1_user_conf.config},
},
{"obj": plugin2, "settings": plugin2_user_conf.plugin.config, "user": None},
{"obj": plugin3, "settings": plugin3_user_conf.plugin.config, "user": None},
]
conf = plugins.generate_plugins_conf([plugin1, plugin2, plugin3])
assert plugins.update_plugins_conf_with_user_settings(conf, user=user) == expected
def test_update_plugins_conf_with_user_settings_anonymous(factories, plugin_class):
plugin1 = plugin_class("test1", "test1")
plugin2 = plugin_class("test2", "test2")
plugin3 = plugin_class("test3", "test3")
plugin1_db_conf = factories["plugins.Plugin"](name=plugin1.name)
plugin2_db_conf = factories["plugins.Plugin"](name=plugin2.name)
plugin3_db_conf = factories["plugins.Plugin"](name=plugin3.name)
expected = [
{"obj": plugin1, "settings": plugin1_db_conf.config, "user": None},
{"obj": plugin2, "settings": plugin2_db_conf.config, "user": None},
{"obj": plugin3, "settings": plugin3_db_conf.config, "user": None},
]
conf = plugins.generate_plugins_conf([plugin1, plugin2, plugin3])
assert plugins.update_plugins_conf_with_user_settings(conf, user=None) == expected
def test_attach_plugins_conf(mocker):
request = mocker.Mock()
generate_plugins_conf = mocker.patch.object(plugins, "generate_plugins_conf")
get_all_plugins = mocker.patch.object(plugins, "get_all_plugins")
plugins.attach_plugins_conf(request)
generate_plugins_conf.assert_called_once_with(plugins=get_all_plugins.return_value)
assert request.plugins_conf == generate_plugins_conf.return_value
def test_attach_plugin_noop_if_plugins_disabled(mocker, preferences):
preferences["plugins__enabled"] = False
request = mocker.Mock()
plugins.attach_plugins_conf(request)
assert request.plugins_conf is None
import pytest
from funkwhale_api import plugins
from funkwhale_api.common import authentication as common_authentication
from funkwhale_api.subsonic import authentication as subsonic_authentication
@pytest.mark.parametrize(
"authentication_class, base_class, patched_method",
[
(
common_authentication.SessionAuthentication,
common_authentication.BaseSessionAuthentication,
"authenticate",
),
(
common_authentication.JSONWebTokenAuthentication,
common_authentication.authentication.JSONWebTokenAuthentication,
"authenticate",
),
(
common_authentication.JSONWebTokenAuthenticationQS,
common_authentication.authentication.BaseJSONWebTokenAuthentication,
"authenticate",
),
(
common_authentication.OAuth2Authentication,
common_authentication.BaseOAuth2Authentication,
"authenticate",
),
(
common_authentication.BearerTokenHeaderAuth,
common_authentication.authentication.BaseJSONWebTokenAuthentication,
"authenticate",
),
(
subsonic_authentication.SubsonicAuthentication,
subsonic_authentication.SubsonicAuthentication,
"perform_authentication",
),
],
)
def test_authentication_calls_update_plugins_conf_with_user_settings(
authentication_class, base_class, patched_method, mocker, api_request
):
request = api_request.get("/")
plugins_conf = mocker.Mock()
setattr(request, "plugins_conf", plugins_conf)
auth = (mocker.Mock(), None)
authentication = authentication_class()
base_class_authenticate = mocker.patch.object(
base_class, patched_method, return_value=auth
)
update_plugins_conf_with_user_settings = mocker.patch.object(
plugins, "update_plugins_conf_with_user_settings"
)
authentication.authenticate(request)
update_plugins_conf_with_user_settings.assert_called_once_with(
plugins_conf, user=auth[0]
)
base_class_authenticate.assert_called_once_with(request)
import pytest
from funkwhale_api.plugins import config
@pytest.mark.parametrize(
"payload, expected",
[
({"test__test_value1": "hello"}, {"test__test_value1": "hello"}),
(
{"noop": "noop", "test__test_value1": "hello"},
{"test__test_value1": "hello"},
),
(
{"test__test_value1": "hello", "test__test_value2": "world"},
{"test__test_value1": "hello", "test__test_value2": "world"},
),
],
)
def test_validate_config(payload, expected):
test_section = config.SettingSection("test")
class TestSetting1(config.StringSetting):
name = "test_value1"
section = test_section
default = ""
class TestSetting2(config.StringSetting):
name = "test_value2"
section = test_section
default = ""
final = config.validate_config(payload, settings=[TestSetting1, TestSetting2])
assert final == expected
def test_validate_config_boolean():
test_section = config.SettingSection("test")
class TestSetting(config.BooleanSetting):
name = "test_value"
section = test_section
default = False
final = config.validate_config({"test__test_value": True}, settings=[TestSetting])
assert final == {"test__test_value": True}
def test_validate_config_number():
test_section = config.SettingSection("test")
class TestSetting(config.IntSetting):
name = "test_value"
section = test_section
default = 12
final = config.validate_config({"test__test_value": 12}, settings=[TestSetting])
assert final == {"test__test_value": 12}
from django.http import HttpResponse
from funkwhale_api.plugins import middleware
def test_attach_plugins_conf_middleware(mocker):
attach_plugins_conf = mocker.patch("funkwhale_api.plugins.attach_plugins_conf")
get_response = mocker.Mock()
get_response.return_value = mocker.Mock(status_code=200)
request = mocker.Mock(path="/")
m = middleware.AttachPluginsConfMiddleware(get_response)
assert m(request) == get_response.return_value
attach_plugins_conf.assert_called_once_with(request)
import pytest
from funkwhale_api import plugins
def test_hooks_register():
hook = plugins.Hook("history.listenings.created", providing_args=["listening"])
plugins.hooks.register(hook)
assert plugins.hooks["history.listenings.created"] == hook
def test_hooks_dispatch(mocker, plugin_class):
plugin1 = plugin_class("test1", "test1")
plugin2 = plugin_class("test2", "test2")
hook = plugins.Hook("history.listenings.created", providing_args=["listening"])
plugins.hooks.register(hook)
handler1 = mocker.stub()
handler2 = mocker.stub()
plugin1.hooks.connect("history.listenings.created")(handler1)
plugin2.hooks.connect("history.listenings.created")(handler2)
plugins_conf = [
{"obj": plugin1, "user": {"hello": "world"}, "settings": {"foo": "bar"}},
{"obj": plugin2},
]
plugins.hooks.dispatch(
"history.listenings.created", listening="test", plugins_conf=plugins_conf
)
handler1.assert_called_once_with(listening="test", plugin_conf=plugins_conf[0])
handler2.assert_called_once_with(listening="test", plugin_conf=plugins_conf[1])
def test_hooks_dispatch_exception_fail_loudly_false(mocker, plugin_class, settings):
settings.PLUGINS_FAIL_LOUDLY = False
plugin1 = plugin_class("test1", "test1")
plugin2 = plugin_class("test2", "test2")
hook = plugins.Hook("history.listenings.created", providing_args=["listening"])
plugins.hooks.register(hook)
handler1 = mocker.stub()
handler2 = mocker.Mock(side_effect=Exception("hello"))
plugin1.hooks.connect("history.listenings.created")(handler1)
plugin2.hooks.connect("history.listenings.created")(handler2)
plugins_conf = [
{"obj": plugin1, "user": {"hello": "world"}, "settings": {"foo": "bar"}},
{"obj": plugin2},
]
plugins.hooks.dispatch(
"history.listenings.created", listening="test", plugins_conf=plugins_conf
)
handler1.assert_called_once_with(listening="test", plugin_conf=plugins_conf[0])
handler2.assert_called_once_with(listening="test", plugin_conf=plugins_conf[1])
def test_hooks_dispatch_exception_fail_loudly_true(mocker, plugin_class, settings):
settings.PLUGINS_FAIL_LOUDLY = True
plugin1 = plugin_class("test1", "test1")
plugin2 = plugin_class("test2", "test2")
hook = plugins.Hook("history.listenings.created", providing_args=["listening"])
plugins.hooks.register(hook)
handler1 = mocker.Mock(side_effect=Exception("hello"))
handler2 = mocker.stub()
plugin1.hooks.connect("history.listenings.created")(handler1)
plugin2.hooks.connect("history.listenings.created")(handler2)
plugins_conf = [
{"obj": plugin1, "user": {"hello": "world"}, "settings": {"foo": "bar"}},
{"obj": plugin2},
]
with pytest.raises(Exception, match=r".*hello.*"):
plugins.hooks.dispatch(
"history.listenings.created", listening="test", plugins_conf=plugins_conf
)
handler1.assert_called_once_with(listening="test", plugin_conf=plugins_conf[0])
handler2.assert_not_called()
...@@ -67,6 +67,7 @@ services: ...@@ -67,6 +67,7 @@ services:
- "CACHE_URL=redis://redis:6379/0" - "CACHE_URL=redis://redis:6379/0"
- "STATIC_ROOT=/staticfiles" - "STATIC_ROOT=/staticfiles"
- "MEDIA_ROOT=/data/media" - "MEDIA_ROOT=/data/media"
- "PLUGINS_FAIL_LOUDLY=True"
depends_on: depends_on:
- postgres - postgres
...@@ -95,6 +96,7 @@ services: ...@@ -95,6 +96,7 @@ services:
- "DATABASE_URL=postgresql://postgres@postgres/postgres" - "DATABASE_URL=postgresql://postgres@postgres/postgres"
- "CACHE_URL=redis://redis:6379/0" - "CACHE_URL=redis://redis:6379/0"
- "MEDIA_ROOT=/data/media" - "MEDIA_ROOT=/data/media"
- "PLUGINS_FAIL_LOUDLY=True"
volumes: volumes:
- ./api:/app - ./api:/app
- "${MUSIC_DIRECTORY_SERVE_PATH-./data/music}:/music:ro" - "${MUSIC_DIRECTORY_SERVE_PATH-./data/music}:/music:ro"
......
...@@ -266,7 +266,8 @@ export default { ...@@ -266,7 +266,8 @@ export default {
soundsCache: [], soundsCache: [],
soundId: null, soundId: null,
playTimeout: null, playTimeout: null,
nextTrackPreloaded: false nextTrackPreloaded: false,
nowPlayingTimeout: null,
} }
}, },
mounted() { mounted() {
...@@ -394,7 +395,7 @@ export default { ...@@ -394,7 +395,7 @@ export default {
}, },
onunlock: function () { onunlock: function () {
if (self.$store.state.player.playing) { if (self.$store.state.player.playing) {
self.soundId = self.sound.play(self.soundId) self.soundId = sound.play(self.soundId)
} }
}, },
onload: function () { onload: function () {
...@@ -408,6 +409,11 @@ export default { ...@@ -408,6 +409,11 @@ export default {
}) })
}, },
onplay: function () { onplay: function () {
if (trackData.id === self.currentTrack.id) {
self.nowPlayingTimeout = setTimeout(() => {
self.$store.dispatch('player/nowPlaying', trackData)
}, 5000)
}
self.$store.commit('player/isLoadingAudio', false) self.$store.commit('player/isLoadingAudio', false)
self.$store.commit('player/resetErrorCount') self.$store.commit('player/resetErrorCount')
self.$store.commit('player/errored', false) self.$store.commit('player/errored', false)
...@@ -713,6 +719,9 @@ export default { ...@@ -713,6 +719,9 @@ export default {
watch: { watch: {
currentTrack: { currentTrack: {
async handler (newValue, oldValue) { async handler (newValue, oldValue) {
if (this.nowPlayingTimeout) {
clearTimeout(this.nowPlayingTimeout)
}
if (newValue === oldValue) { if (newValue === oldValue) {
return return
} }
...@@ -746,6 +755,9 @@ export default { ...@@ -746,6 +755,9 @@ export default {
if (newValue === true) { if (newValue === true) {
this.soundId = this.currentSound.play(this.soundId) this.soundId = this.currentSound.play(this.soundId)
} else { } else {
if (this.nowPlayingTimeout) {
clearTimeout(this.nowPlayingTimeout)
}
this.currentSound.pause(this.soundId) this.currentSound.pause(this.soundId)
} }
} else { } else {
......
...@@ -126,6 +126,14 @@ export default { ...@@ -126,6 +126,14 @@ export default {
logger.default.error('Could not record track in history') logger.default.error('Could not record track in history')
}) })
}, },
nowPlaying ({commit, rootState}, trackData) {
if (!rootState.auth.authenticated) {
return
}
return axios.post('history/listenings/now/', {'track': trackData.id}).then((response) => {}, (response) => {
logger.default.error('Could not set track as now playing')
})
},
trackEnded ({dispatch, rootState}, track) { trackEnded ({dispatch, rootState}, track) {
dispatch('trackListened', track) dispatch('trackListened', track)
let queueState = rootState.queue let queueState = rootState.queue
......