Merge branch 'develop'
This commit is contained in:
commit
7df97263e5
14
CHANGELOG
14
CHANGELOG
|
@ -67,7 +67,7 @@ Instance-level moderation tools
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
This release includes a first set of moderation tools that will give more control
|
This release includes a first set of moderation tools that will give more control
|
||||||
to admins about the way their instance federate with other instance and accounts on the network.
|
to admins about the way their instance federates with other instance and accounts on the network.
|
||||||
Using these tools, it's now possible to:
|
Using these tools, it's now possible to:
|
||||||
|
|
||||||
- Browse known accounts and domains, and associated data (storage size, software version, etc.)
|
- Browse known accounts and domains, and associated data (storage size, software version, etc.)
|
||||||
|
@ -75,7 +75,7 @@ Using these tools, it's now possible to:
|
||||||
- Block or partially restrict interactions with any account or domain
|
- Block or partially restrict interactions with any account or domain
|
||||||
|
|
||||||
All those features are usable using a brand new "moderation" permission, meaning
|
All those features are usable using a brand new "moderation" permission, meaning
|
||||||
you can appoints one or nultiple moderators to help with this task.
|
you can appoint one or multiple moderators to help with this task.
|
||||||
|
|
||||||
I'd like to thank all Mastodon contributors, because some of the these tools are heavily
|
I'd like to thank all Mastodon contributors, because some of the these tools are heavily
|
||||||
inspired from what's being done in Mastodon. Thank you so much!
|
inspired from what's being done in Mastodon. Thank you so much!
|
||||||
|
@ -84,7 +84,7 @@ inspired from what's being done in Mastodon. Thank you so much!
|
||||||
Iframe widget to embed public tracks and albums [manual action required]
|
Iframe widget to embed public tracks and albums [manual action required]
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Funkwhale now support embedding a lightweight audio player on external websites
|
Funkwhale now supports embedding a lightweight audio player on external websites
|
||||||
for album and tracks that are available in public libraries. Important pages,
|
for album and tracks that are available in public libraries. Important pages,
|
||||||
such as artist, album and track pages also include OpenGraph tags that will
|
such as artist, album and track pages also include OpenGraph tags that will
|
||||||
enable previews on compatible apps (like sharing a Funkwhale track link on Mastodon
|
enable previews on compatible apps (like sharing a Funkwhale track link on Mastodon
|
||||||
|
@ -132,13 +132,13 @@ which should be ``/srv/funkwhale/front/dist`` by default, then reload your nginx
|
||||||
Alternative docker deployment method
|
Alternative docker deployment method
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Thanks to the awesome done by @thetarkus at https://github.com/thetarkus/docker-funkwhale,
|
Thanks to the awesome work done by @thetarkus at https://github.com/thetarkus/docker-funkwhale,
|
||||||
we're now able to provide an alternative and easier Docker deployment method!
|
we're now able to provide an alternative and easier Docker deployment method!
|
||||||
|
|
||||||
In contrast with our current, multi-container offer, this method integrates
|
In contrast with our current, multi-container offer, this method integrates
|
||||||
all Funkwhale processes and services (database, redis, etc.) into a single, easier to deploy container.
|
all Funkwhale processes and services (database, redis, etc.) into a single, easier to deploy container.
|
||||||
|
|
||||||
Both method will coexist in parallel, as each one has pros and cons. You can learn more
|
Both methods will coexist in parallel, as each one has pros and cons. You can learn more
|
||||||
about this exciting new deployment option by visiting https://docs.funkwhale.audio/installation/docker.html!
|
about this exciting new deployment option by visiting https://docs.funkwhale.audio/installation/docker.html!
|
||||||
|
|
||||||
Automatically load .env file
|
Automatically load .env file
|
||||||
|
@ -146,7 +146,7 @@ Automatically load .env file
|
||||||
|
|
||||||
On non-docker deployments, earlier versions required you to source
|
On non-docker deployments, earlier versions required you to source
|
||||||
the config/.env file before launching any Funkwhale command, with ``export $(cat config/.env | grep -v ^# | xargs)``
|
the config/.env file before launching any Funkwhale command, with ``export $(cat config/.env | grep -v ^# | xargs)``
|
||||||
This led to more complex and error prode deployment / setup.
|
This led to more complex and error prone deployment / setup.
|
||||||
|
|
||||||
This is not the case anymore, and Funkwhale will automatically load this file if it's available.
|
This is not the case anymore, and Funkwhale will automatically load this file if it's available.
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ Enable gzip compression [manual action suggested]
|
||||||
Gzip compression will be enabled on new instances by default
|
Gzip compression will be enabled on new instances by default
|
||||||
and will reduce the amount of bandwidth consumed by your instance.
|
and will reduce the amount of bandwidth consumed by your instance.
|
||||||
|
|
||||||
If you with to benefit from gzip compression on your instance,
|
If you want to benefit from gzip compression on your instance,
|
||||||
edit your reverse proxy virtualhost file (located at ``/etc/nginx/sites-available/funkwhale.conf``) and add the following snippet
|
edit your reverse proxy virtualhost file (located at ``/etc/nginx/sites-available/funkwhale.conf``) and add the following snippet
|
||||||
in the server block, then reload your nginx server::
|
in the server block, then reload your nginx server::
|
||||||
|
|
||||||
|
|
|
@ -41,15 +41,19 @@ Setup front-end only development environment
|
||||||
|
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
4. Launch the development server::
|
4. Compile the translations::
|
||||||
|
|
||||||
|
yarn i18n-compile
|
||||||
|
|
||||||
|
5. Launch the development server::
|
||||||
|
|
||||||
# this will serve the front-end on http://localhost:8000/front/
|
# this will serve the front-end on http://localhost:8000/front/
|
||||||
VUE_PORT=8000 yarn serve
|
VUE_PORT=8000 yarn serve
|
||||||
|
|
||||||
5. Make the front-end talk with an existing server (like https://demo.funkwhale.audio),
|
6. Make the front-end talk with an existing server (like https://demo.funkwhale.audio or https://open.audio),
|
||||||
by clicking on the corresponding link in the footer
|
by clicking on the corresponding link in the footer
|
||||||
|
|
||||||
6. Start hacking!
|
7. Start hacking!
|
||||||
|
|
||||||
Setup your development environment
|
Setup your development environment
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
|
@ -7,6 +7,7 @@ import uuid
|
||||||
|
|
||||||
import markdown
|
import markdown
|
||||||
import pendulum
|
import pendulum
|
||||||
|
import pydub
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.postgres.fields import JSONField
|
from django.contrib.postgres.fields import JSONField
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
@ -780,6 +781,15 @@ class Upload(models.Model):
|
||||||
"size": self.get_file_size(),
|
"size": self.get_file_size(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_audio_segment(self):
|
||||||
|
input = self.get_audio_file()
|
||||||
|
if not input:
|
||||||
|
return
|
||||||
|
|
||||||
|
input_format = utils.MIMETYPE_TO_EXTENSION[self.mimetype]
|
||||||
|
audio = pydub.AudioSegment.from_file(input, format=input_format)
|
||||||
|
return audio
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
if not self.mimetype:
|
if not self.mimetype:
|
||||||
if self.audio_file:
|
if self.audio_file:
|
||||||
|
@ -824,10 +834,9 @@ class Upload(models.Model):
|
||||||
0
|
0
|
||||||
] + ".{}".format(format)
|
] + ".{}".format(format)
|
||||||
version.audio_file.save(new_name, f)
|
version.audio_file.save(new_name, f)
|
||||||
utils.transcode_file(
|
utils.transcode_audio(
|
||||||
input=self.audio_file,
|
audio=self.get_audio_segment(),
|
||||||
output=version.audio_file,
|
output=version.audio_file,
|
||||||
input_format=utils.MIMETYPE_TO_EXTENSION[self.mimetype],
|
|
||||||
output_format=utils.MIMETYPE_TO_EXTENSION[mimetype],
|
output_format=utils.MIMETYPE_TO_EXTENSION[mimetype],
|
||||||
)
|
)
|
||||||
version.size = version.audio_file.size
|
version.size = version.audio_file.size
|
||||||
|
|
|
@ -75,5 +75,9 @@ def get_actor_from_request(request):
|
||||||
def transcode_file(input, output, input_format, output_format, **kwargs):
|
def transcode_file(input, output, input_format, output_format, **kwargs):
|
||||||
with input.open("rb"):
|
with input.open("rb"):
|
||||||
audio = pydub.AudioSegment.from_file(input, format=input_format)
|
audio = pydub.AudioSegment.from_file(input, format=input_format)
|
||||||
|
return transcode_audio(audio, output, output_format, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def transcode_audio(audio, output, output_format, **kwargs):
|
||||||
with output.open("wb"):
|
with output.open("wb"):
|
||||||
return audio.export(output, format=output_format, **kwargs)
|
return audio.export(output, format=output_format, **kwargs)
|
||||||
|
|
|
@ -374,6 +374,32 @@ def test_listen_transcode(factories, now, logged_in_api_client, mocker):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("serve_path", [("/host/music",), ("/app/music",)])
|
||||||
|
def test_listen_transcode_in_place(
|
||||||
|
serve_path, factories, now, logged_in_api_client, mocker, settings
|
||||||
|
):
|
||||||
|
settings.MUSIC_DIRECTORY_PATH = "/app/music"
|
||||||
|
settings.MUSIC_DIRECTORY_SERVE_PATH = serve_path
|
||||||
|
upload = factories["music.Upload"](
|
||||||
|
import_status="finished",
|
||||||
|
library__actor__user=logged_in_api_client.user,
|
||||||
|
audio_file=None,
|
||||||
|
source="file://" + os.path.join(DATA_DIR, "test.ogg"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert upload.get_audio_segment()
|
||||||
|
|
||||||
|
url = reverse("api:v1:listen-detail", kwargs={"uuid": upload.track.uuid})
|
||||||
|
handle_serve = mocker.spy(views, "handle_serve")
|
||||||
|
response = logged_in_api_client.get(url, {"to": "mp3"})
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
handle_serve.assert_called_once_with(
|
||||||
|
upload, user=logged_in_api_client.user, format="mp3"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_create_library(factories, logged_in_api_client):
|
def test_user_can_create_library(factories, logged_in_api_client):
|
||||||
actor = logged_in_api_client.user.create_actor()
|
actor = logged_in_api_client.user.create_actor()
|
||||||
url = reverse("api:v1:libraries-list")
|
url = reverse("api:v1:libraries-list")
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Make Apache configuration file work with 0.18 changes (#667)
|
|
@ -0,0 +1 @@
|
||||||
|
Hide pagination when there is only one page of results (#681)
|
|
@ -0,0 +1 @@
|
||||||
|
Fix transcoding of in-place imported tracks (#688)
|
|
@ -5,3 +5,38 @@ Next release notes
|
||||||
|
|
||||||
Those release notes refer to the current development branch and are reset
|
Those release notes refer to the current development branch and are reset
|
||||||
after each release.
|
after each release.
|
||||||
|
|
||||||
|
Fix Apache configuration file for 0.18 [manual action required]
|
||||||
|
----------------------------------------------------------
|
||||||
|
|
||||||
|
The way front is served has changed since 0.18. The Apache configuration can't serve 0.18 properly, leading to blank screens.
|
||||||
|
|
||||||
|
If you are on an Apache setup, you will have to replace the `<Location "/api">` block with the following::
|
||||||
|
|
||||||
|
<Location "/">
|
||||||
|
# similar to nginx 'client_max_body_size 100M;'
|
||||||
|
LimitRequestBody 104857600
|
||||||
|
|
||||||
|
ProxyPass ${funkwhale-api}/
|
||||||
|
ProxyPassReverse ${funkwhale-api}/
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
And add some more `ProxyPass` directives so that the `Alias` part of your configuration file looks this way::
|
||||||
|
|
||||||
|
ProxyPass "/front" "!"
|
||||||
|
Alias /front /srv/funkwhale/front/dist
|
||||||
|
|
||||||
|
ProxyPass "/media" "!"
|
||||||
|
Alias /media /srv/funkwhale/data/media
|
||||||
|
|
||||||
|
ProxyPass "/staticfiles" "!"
|
||||||
|
Alias /staticfiles /srv/funkwhale/data/static
|
||||||
|
|
||||||
|
In case you are using custom css and theming, you also need to match this block::
|
||||||
|
|
||||||
|
ProxyPass "/settings.json" "!"
|
||||||
|
Alias /settings.json /srv/funkwhale/custom/settings.json
|
||||||
|
|
||||||
|
ProxyPass "/custom" "!"
|
||||||
|
Alias /custom /srv/funkwhale/custom
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,6 @@ Define MUSIC_DIRECTORY_PATH /srv/funkwhale/data/music
|
||||||
# Tell the api that the client is using https
|
# Tell the api that the client is using https
|
||||||
RequestHeader set X-Forwarded-Proto "https"
|
RequestHeader set X-Forwarded-Proto "https"
|
||||||
|
|
||||||
DocumentRoot /srv/funkwhale/front/dist
|
|
||||||
|
|
||||||
FallbackResource /index.html
|
|
||||||
|
|
||||||
# Configure Proxy settings
|
# Configure Proxy settings
|
||||||
# ProxyPreserveHost pass the original Host header to the backend server
|
# ProxyPreserveHost pass the original Host header to the backend server
|
||||||
ProxyVia On
|
ProxyVia On
|
||||||
|
@ -71,12 +67,12 @@ Define MUSIC_DIRECTORY_PATH /srv/funkwhale/data/music
|
||||||
# Activating WebSockets
|
# Activating WebSockets
|
||||||
ProxyPass "/api/v1/activity" ${funkwhale-api-ws}/api/v1/activity
|
ProxyPass "/api/v1/activity" ${funkwhale-api-ws}/api/v1/activity
|
||||||
|
|
||||||
<Location "/api">
|
<Location "/">
|
||||||
# similar to nginx 'client_max_body_size 100M;'
|
# similar to nginx 'client_max_body_size 100M;'
|
||||||
LimitRequestBody 104857600
|
LimitRequestBody 104857600
|
||||||
|
|
||||||
ProxyPass ${funkwhale-api}/api
|
ProxyPass ${funkwhale-api}/
|
||||||
ProxyPassReverse ${funkwhale-api}/api
|
ProxyPassReverse ${funkwhale-api}/
|
||||||
</Location>
|
</Location>
|
||||||
<Location "/federation">
|
<Location "/federation">
|
||||||
ProxyPass ${funkwhale-api}/federation
|
ProxyPass ${funkwhale-api}/federation
|
||||||
|
@ -94,8 +90,13 @@ Define MUSIC_DIRECTORY_PATH /srv/funkwhale/data/music
|
||||||
ProxyPassReverse ${funkwhale-api}/.well-known/
|
ProxyPassReverse ${funkwhale-api}/.well-known/
|
||||||
</Location>
|
</Location>
|
||||||
|
|
||||||
|
ProxyPass "/front" "!"
|
||||||
|
Alias /front /srv/funkwhale/front/dist
|
||||||
|
|
||||||
|
ProxyPass "/media" "!"
|
||||||
Alias /media /srv/funkwhale/data/media
|
Alias /media /srv/funkwhale/data/media
|
||||||
|
|
||||||
|
ProxyPass "/staticfiles" "!"
|
||||||
Alias /staticfiles /srv/funkwhale/data/static
|
Alias /staticfiles /srv/funkwhale/data/static
|
||||||
|
|
||||||
# Setting appropriate access levels to serve frontend
|
# Setting appropriate access levels to serve frontend
|
||||||
|
|
|
@ -63,6 +63,37 @@ easy:
|
||||||
This is a warning, not an error, and it can be safely ignored.
|
This is a warning, not an error, and it can be safely ignored.
|
||||||
Never run the ``makemigrations`` command yourself.
|
Never run the ``makemigrations`` command yourself.
|
||||||
|
|
||||||
|
Upgrading the Postgres container
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
With some Funkwhale releases, it is recommended to upgrade the version of the
|
||||||
|
Postgres database server container. For example, Funkwhale 0.17 recommended
|
||||||
|
Postgres 9.4, but Funkwhale 0.18 recommends Postgres 11. When upgrading
|
||||||
|
Postgres, it is not sufficient to change the container referenced in
|
||||||
|
``docker-compose.yml``. New major versions of Postgres cannot read the databases
|
||||||
|
created by older major versions. The data has to be exported from a running
|
||||||
|
instance of the old version and imported by the new version.
|
||||||
|
|
||||||
|
Thankfully, there is a Docker container available to automate this process. You
|
||||||
|
can use the following snippet to upgrade your database in ``./postgres``,
|
||||||
|
keeping a backup of the old version in ``./postgres-old``:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
# Replace "9.4" and "11" with the versions you are migrating between.
|
||||||
|
export OLD_POSTGRES=9.4
|
||||||
|
export NEW_POSTGRES=11
|
||||||
|
docker-compose stop postgres
|
||||||
|
docker run --rm \
|
||||||
|
-v `pwd`/data/postgres:/var/lib/postgresql/${OLD_POSTGRES}/data \
|
||||||
|
-v `pwd`/data/postgres-new:/var/lib/postgresql/${NEW_POSTGRES}/data \
|
||||||
|
tianon/postgres-upgrade:${OLD_POSTGRES}-to-${NEW_POSTGRES}
|
||||||
|
# Add back the access control rule that doesn't survive the upgrade
|
||||||
|
echo "host all all all trust" | sudo tee -a ./postgres-new/pg_hba.conf
|
||||||
|
# Swap over to the new database
|
||||||
|
mv ./data/postgres ./data/postgres-old
|
||||||
|
mv ./data/postgres-new ./data/postgres
|
||||||
|
|
||||||
|
|
||||||
Non-docker setup
|
Non-docker setup
|
||||||
----------------
|
----------------
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ui pagination menu" role="navigation" :aria-label="labels.pagination">
|
<div v-if='maxPage > 1' class="ui pagination menu" role="navigation" :aria-label="labels.pagination">
|
||||||
<a href
|
<a href
|
||||||
:disabled="current - 1 < 1"
|
:disabled="current - 1 < 1"
|
||||||
@click.prevent.stop="selectPage(current - 1)"
|
@click.prevent.stop="selectPage(current - 1)"
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
{{ page }}
|
{{ page }}
|
||||||
</a href>
|
</a href>
|
||||||
<div v-else class="disabled item">
|
<div v-else class="disabled item">
|
||||||
...
|
…
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a href
|
<a href
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default {
|
||||||
},
|
},
|
||||||
title () {
|
title () {
|
||||||
if (this.playable) {
|
if (this.playable) {
|
||||||
return this.$gettext('Play now')
|
return this.$gettext('Play...')
|
||||||
} else {
|
} else {
|
||||||
if (this.track) {
|
if (this.track) {
|
||||||
return this.$gettext('This track is not available in any library you have access to')
|
return this.$gettext('This track is not available in any library you have access to')
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
<translate>We cannot load this track</translate>
|
<translate>We cannot load this track</translate>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="hasNext && playing && $store.state.player.errorCount < $store.state.player.maxConsecutiveErrors">
|
<p v-if="hasNext && playing && $store.state.player.errorCount < $store.state.player.maxConsecutiveErrors">
|
||||||
<translate>The next track will play automatically in a few seconds...</translate>
|
<translate>The next track will play automatically in a few seconds…</translate>
|
||||||
<i class="loading spinner icon"></i>
|
<i class="loading spinner icon"></i>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<img class="ui big circular image" v-else v-lazy="$store.getters['instance/absoluteUrl'](profile.avatar.square_crop)" />
|
<img class="ui big circular image" v-else v-lazy="$store.getters['instance/absoluteUrl'](profile.avatar.square_crop)" />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{ profile.username }}
|
{{ profile.username }}
|
||||||
<div class="sub header" v-translate="{date: signupDate}">Registered since %{ date }</div>
|
<div class="sub header" v-translate="{date: signupDate}">Member since %{ date }</div>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="ui basic green label">
|
<div class="ui basic green label">
|
||||||
|
|
|
@ -68,8 +68,7 @@
|
||||||
<translate>Change my password</translate>
|
<translate>Change my password</translate>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
<translate>Changing your password will also change your Subsonic API password if you have requested one.</translate>
|
<translate>Changing your password will also change your Subsonic API password if you have requested one.</translate> <translate>You will have to update your password on your clients that use this password.</translate>
|
||||||
<translate>You will have to update your password on your clients that use this password.</translate>
|
|
||||||
</div>
|
</div>
|
||||||
<form class="ui form" @submit.prevent="submitPassword()">
|
<form class="ui form" @submit.prevent="submitPassword()">
|
||||||
<div v-if="passwordError" class="ui negative message">
|
<div v-if="passwordError" class="ui negative message">
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
<translate>The Subsonic API is not available on this Funkwhale instance.</translate>
|
<translate>The Subsonic API is not available on this Funkwhale instance.</translate>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<translate>Funkwhale is compatible with other music players that support the Subsonic API.</translate>
|
<translate>Funkwhale is compatible with other music players that support the Subsonic API.</translate> <translate>You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.</translate>
|
||||||
<translate>You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.</translate>
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<translate>However, accessing Funkwhale from those clients require a separate password you can set below.</translate>
|
<translate>However, accessing Funkwhale from those clients require a separate password you can set below.</translate>
|
||||||
|
|
|
@ -168,7 +168,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
labels () {
|
labels () {
|
||||||
return {
|
return {
|
||||||
searchPlaceholder: this.$gettext('Search by domain, username, bio...')
|
searchPlaceholder: this.$gettext('Search by domain, username, bio…')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actionFilters () {
|
actionFilters () {
|
||||||
|
|
|
@ -148,7 +148,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
labels () {
|
labels () {
|
||||||
return {
|
return {
|
||||||
searchPlaceholder: this.$gettext('Search by name...')
|
searchPlaceholder: this.$gettext('Search by name…')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actionFilters () {
|
actionFilters () {
|
||||||
|
|
|
@ -22,11 +22,11 @@
|
||||||
<div v-else class="ui list">
|
<div v-else class="ui list">
|
||||||
<div class="ui item" v-if="object.silence_activity">
|
<div class="ui item" v-if="object.silence_activity">
|
||||||
<i class="feed icon"></i>
|
<i class="feed icon"></i>
|
||||||
<div class="content"><translate>Silence activity</translate></div>
|
<div class="content"><translate>Mute activity</translate></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui item" v-if="object.silence_notifications">
|
<div class="ui item" v-if="object.silence_notifications">
|
||||||
<i class="bell icon"></i>
|
<i class="bell icon"></i>
|
||||||
<div class="content"><translate>Silence notifications</translate></div>
|
<div class="content"><translate>Mute notifications</translate></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui item" v-if="object.reject_media">
|
<div class="ui item" v-if="object.reject_media">
|
||||||
<i class="file icon"></i>
|
<i class="file icon"></i>
|
||||||
|
|
|
@ -112,7 +112,7 @@ export default {
|
||||||
blockAllHelp: this.$gettext("Block everything from this account or domain. This will prevent any interaction with the entity, and purge related content (uploads, libraries, follows, etc.)"),
|
blockAllHelp: this.$gettext("Block everything from this account or domain. This will prevent any interaction with the entity, and purge related content (uploads, libraries, follows, etc.)"),
|
||||||
silenceActivity: {
|
silenceActivity: {
|
||||||
help: this.$gettext("Hide account or domain content, except from followers."),
|
help: this.$gettext("Hide account or domain content, except from followers."),
|
||||||
label: this.$gettext("Silence activity"),
|
label: this.$gettext("Mute activity"),
|
||||||
},
|
},
|
||||||
silenceNotifications: {
|
silenceNotifications: {
|
||||||
help: this.$gettext("Prevent account or domain from triggering notifications, except from followers."),
|
help: this.$gettext("Prevent account or domain from triggering notifications, except from followers."),
|
||||||
|
|
|
@ -38,9 +38,11 @@ export default {
|
||||||
labels () {
|
labels () {
|
||||||
let libraryFollowMessage = this.$gettext('%{ username } followed your library "%{ library }"')
|
let libraryFollowMessage = this.$gettext('%{ username } followed your library "%{ library }"')
|
||||||
let libraryAcceptFollowMessage = this.$gettext('%{ username } accepted your follow on library "%{ library }"')
|
let libraryAcceptFollowMessage = this.$gettext('%{ username } accepted your follow on library "%{ library }"')
|
||||||
|
let libraryPendingFollowMessage = this.$gettext('%{ username } wants to follow your library "%{ library }"')
|
||||||
return {
|
return {
|
||||||
libraryFollowMessage,
|
libraryFollowMessage,
|
||||||
libraryAcceptFollowMessage,
|
libraryAcceptFollowMessage,
|
||||||
|
libraryPendingFollowMessage,
|
||||||
markRead: this.$gettext('Mark as read'),
|
markRead: this.$gettext('Mark as read'),
|
||||||
markUnread: this.$gettext('Mark as unread'),
|
markUnread: this.$gettext('Mark as unread'),
|
||||||
|
|
||||||
|
@ -55,19 +57,23 @@ export default {
|
||||||
if (a.type === 'Follow') {
|
if (a.type === 'Follow') {
|
||||||
if (a.object && a.object.type === 'music.Library') {
|
if (a.object && a.object.type === 'music.Library') {
|
||||||
let action = null
|
let action = null
|
||||||
|
let message = null
|
||||||
if (!a.related_object.approved) {
|
if (!a.related_object.approved) {
|
||||||
|
message = this.labels.libraryPendingFollowMessage
|
||||||
action = {
|
action = {
|
||||||
buttonClass: 'green',
|
buttonClass: 'green',
|
||||||
icon: 'check',
|
icon: 'check',
|
||||||
label: this.$gettext('Approve'),
|
label: this.$gettext('Approve'),
|
||||||
handler: () => { self.approveLibraryFollow(a.related_object) }
|
handler: () => { self.approveLibraryFollow(a.related_object) }
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
message = this.labels.libraryFollowMessage
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
action,
|
action,
|
||||||
detailUrl: {name: 'content.libraries.detail', params: {id: a.object.uuid}},
|
detailUrl: {name: 'content.libraries.detail', params: {id: a.object.uuid}},
|
||||||
message: this.$gettextInterpolate(
|
message: this.$gettextInterpolate(
|
||||||
this.labels.libraryFollowMessage,
|
message,
|
||||||
{username: this.username, library: a.object.name}
|
{username: this.username, library: a.object.name}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
labels () {
|
labels () {
|
||||||
return {
|
return {
|
||||||
copyTitle: this.$gettext('Copy tracks from current queue to playlist')
|
copyTitle: this.$gettext('Copy queued tracks to playlist')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
status () {
|
status () {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p v-else>
|
<p v-else>
|
||||||
<translate>No notifications yet.</translate>
|
<translate>No notification to show.</translate>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ui vertical aligned stripe segment">
|
<div class="ui vertical aligned stripe segment">
|
||||||
<div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
|
<div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
|
||||||
<div class="ui text loader"><translate>Loading remote libraries...</translate></div>
|
<div class="ui text loader"><translate>Loading remote libraries…</translate></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="ui text container">
|
<div v-else class="ui text container">
|
||||||
<h1 class="ui header"><translate>Remote libraries</translate></h1>
|
<h1 class="ui header"><translate>Remote libraries</translate></h1>
|
||||||
|
|
Loading…
Reference in New Issue