Merge branch 'master' into develop

This commit is contained in:
Eliot Berriot 2019-06-19 11:24:49 +02:00
commit 371dc01205
No known key found for this signature in database
GPG Key ID: DD6965E2476E5C27
11 changed files with 59 additions and 17 deletions

View File

@ -12,7 +12,7 @@ def guess_mimetype(f):
t = magic.from_buffer(f.read(b), mime=True) t = magic.from_buffer(f.read(b), mime=True)
if not t.startswith("audio/"): if not t.startswith("audio/"):
# failure, we try guessing by extension # failure, we try guessing by extension
mt, _ = mimetypes.guess_type(f.path) mt, _ = mimetypes.guess_type(f.name)
if mt: if mt:
t = mt t = mt
return t return t

View File

@ -36,3 +36,12 @@ def test_get_audio_file_data(name, expected):
result = utils.get_audio_file_data(f) result = utils.get_audio_file_data(f)
assert result == expected assert result == expected
def test_guess_mimetype_dont_crash_with_s3(factories, mocker, settings):
"""See #857"""
settings.DEFAULT_FILE_STORAGE = "funkwhale_api.common.storage.ASCIIS3Boto3Storage"
mocker.patch("magic.from_buffer", return_value="none")
f = factories["music.Upload"].build(audio_file__filename="test.mp3")
assert utils.guess_mimetype(f.audio_file) == "audio/mpeg"

View File

@ -0,0 +1 @@
Added copy-to-clipboard button with Subsonic password input (#814)

View File

@ -0,0 +1 @@
Fixed broken translation on home and track detail page (#833)

View File

@ -0,0 +1 @@
Fixed secondary menus truncated on narrow screens (#855)

View File

@ -0,0 +1 @@
Fix broken upload for specific files when using S3 storage (#857)

View File

@ -68,12 +68,7 @@
<div class="ui list"> <div class="ui list">
<div class="item"> <div class="item">
<i class="tag icon"></i> <i class="tag icon"></i>
<div <div class="content" v-html="musicbrainzItem"></div>
class="content"
v-translate="{url: musicbrainzUrl}"
translate-context="Content/Home/List item/Verb">
Get quality metadata about your music thanks to <a href="%{ url }" target="_blank">MusicBrainz</a>
</div>
</div> </div>
<div class="item"> <div class="item">
<i class="plus icon"></i> <i class="plus icon"></i>
@ -147,6 +142,10 @@ export default {
return { return {
title: this.$pgettext('Head/Home/Title', "Welcome") title: this.$pgettext('Head/Home/Title', "Welcome")
} }
},
musicbrainzItem () {
let msg = this.$pgettext('Content/Home/List item/Verb', 'Get quality metadata about your music thanks to <a href="%{ url }" target="_blank">MusicBrainz</a>')
return this.$gettextInterpolate(msg, {url: this.musicbrainzUrl})
} }
} }
} }

View File

@ -24,7 +24,12 @@
</div> </div>
<template v-if="subsonicEnabled"> <template v-if="subsonicEnabled">
<div v-if="token" class="field"> <div v-if="token" class="field">
<password-input v-model="token" /> <password-input
ref="passwordInput"
v-model="token"
:key="token"
:copy-button="true"
:default-show="showToken"/>
</div> </div>
<dangerous-button <dangerous-button
v-if="token" v-if="token"
@ -69,7 +74,8 @@ export default {
errors: [], errors: [],
success: false, success: false,
isLoading: false, isLoading: false,
successMessage: '' successMessage: '',
showToken: false
} }
}, },
created () { created () {
@ -98,6 +104,7 @@ export default {
let self = this let self = this
let url = `users/users/${this.$store.state.auth.username}/subsonic-token/` let url = `users/users/${this.$store.state.auth.username}/subsonic-token/`
return axios.post(url, {}).then(response => { return axios.post(url, {}).then(response => {
self.showToken = true
self.token = response.data['subsonic_api_token'] self.token = response.data['subsonic_api_token']
self.isLoading = false self.isLoading = false
self.success = true self.success = true

View File

@ -10,20 +10,37 @@
<span @click="showPassword = !showPassword" :title="labels.title" class="ui icon button"> <span @click="showPassword = !showPassword" :title="labels.title" class="ui icon button">
<i class="eye icon"></i> <i class="eye icon"></i>
</span> </span>
<button v-if="copyButton" @click.prevent="copy" class="ui icon button" :title="labels.copy">
<i class="copy icon"></i>
</button>
</div> </div>
</template> </template>
<script> <script>
function copyStringToClipboard (str) {
// cf https://techoverflow.net/2018/03/30/copying-strings-to-the-clipboard-using-pure-javascript/
let el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style = {position: 'absolute', left: '-9999px'};
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
}
export default { export default {
props: ['value', 'index'], props: ['value', 'index', 'defaultShow', 'copyButton'],
data () { data () {
return { return {
showPassword: false showPassword: this.defaultShow || false,
} }
}, },
computed: { computed: {
labels () { labels () {
return { return {
title: this.$pgettext('Content/Settings/Button.Tooltip/Verb', 'Show/hide password') title: this.$pgettext('Content/Settings/Button.Tooltip/Verb', 'Show/hide password'),
copy: this.$pgettext('*/*/Button.Label/Short, Verb', 'Copy')
} }
}, },
passwordInputType () { passwordInputType () {
@ -32,6 +49,11 @@ export default {
} }
return 'password' return 'password'
} }
},
methods: {
copy () {
copyStringToClipboard(this.value)
}
} }
} }
</script> </script>

View File

@ -14,11 +14,7 @@
<i class="circular inverted music orange icon"></i> <i class="circular inverted music orange icon"></i>
<div class="content"> <div class="content">
{{ track.title }} {{ track.title }}
<div class="sub header"> <div class="sub header" v-html="subtitle"></div>
<div translate-context="Content/Track/Paragraph"
v-translate="{album: track.album.title, artist: track.artist.name, albumUrl: albumUrl, artistUrl: artistUrl}"
>From album <a class="internal" href="%{ albumUrl }">%{ album }</a> by <a class="internal" href="%{ artistUrl }">%{ artist }</a></div>
</div>
</div> </div>
</h2> </h2>
<div class="header-buttons"> <div class="header-buttons">
@ -230,6 +226,10 @@ export default {
")" ")"
) )
}, },
subtitle () {
let msg = this.$pgettext('Content/Track/Paragraph', 'From album <a class="internal" href="%{ albumUrl }">%{ album }</a> by <a class="internal" href="%{ artistUrl }">%{ artist }</a>')
return this.$gettextInterpolate(msg, {album: this.track.album.title, artist: this.track.artist.name, albumUrl: this.albumUrl, artistUrl: this.artistUrl})
}
}, },
watch: { watch: {
id() { id() {

View File

@ -131,6 +131,7 @@ body {
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
border: none; border: none;
overflow-y: auto;
.ui.item { .ui.item {
border: none; border: none;
border-bottom-style: none; border-bottom-style: none;