Merge branch 'develop' of dev.funkwhale.audio:funkwhale/funkwhale into develop
This commit is contained in:
commit
7a38a20925
|
@ -1223,6 +1223,7 @@ VERSATILEIMAGEFIELD_RENDITION_KEY_SETS = {
|
||||||
"attachment_square": [
|
"attachment_square": [
|
||||||
("original", "url"),
|
("original", "url"),
|
||||||
("medium_square_crop", "crop__200x200"),
|
("medium_square_crop", "crop__200x200"),
|
||||||
|
("large_square_crop", "crop__600x600"),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
VERSATILEIMAGEFIELD_SETTINGS = {
|
VERSATILEIMAGEFIELD_SETTINGS = {
|
||||||
|
|
|
@ -267,6 +267,13 @@ class Attachment(models.Model):
|
||||||
proxy_url = reverse("api:v1:attachments-proxy", kwargs={"uuid": self.uuid})
|
proxy_url = reverse("api:v1:attachments-proxy", kwargs={"uuid": self.uuid})
|
||||||
return federation_utils.full_url(proxy_url + "?next=medium_square_crop")
|
return federation_utils.full_url(proxy_url + "?next=medium_square_crop")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def download_url_large_square_crop(self):
|
||||||
|
if self.file:
|
||||||
|
return utils.media_url(self.file.crop["600x600"].url)
|
||||||
|
proxy_url = reverse("api:v1:attachments-proxy", kwargs={"uuid": self.uuid})
|
||||||
|
return federation_utils.full_url(proxy_url + "?next=large_square_crop")
|
||||||
|
|
||||||
|
|
||||||
class MutationAttachment(models.Model):
|
class MutationAttachment(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -297,6 +297,7 @@ class AttachmentSerializer(serializers.Serializer):
|
||||||
urls["source"] = o.url
|
urls["source"] = o.url
|
||||||
urls["original"] = o.download_url_original
|
urls["original"] = o.download_url_original
|
||||||
urls["medium_square_crop"] = o.download_url_medium_square_crop
|
urls["medium_square_crop"] = o.download_url_medium_square_crop
|
||||||
|
urls["large_square_crop"] = o.download_url_large_square_crop
|
||||||
return urls
|
return urls
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
|
|
@ -175,7 +175,7 @@ class AttachmentViewSet(
|
||||||
return r
|
return r
|
||||||
|
|
||||||
size = request.GET.get("next", "original").lower()
|
size = request.GET.get("next", "original").lower()
|
||||||
if size not in ["original", "medium_square_crop"]:
|
if size not in ["original", "medium_square_crop", "large_square_crop"]:
|
||||||
size = "original"
|
size = "original"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -429,7 +429,7 @@ def fetch(fetch_obj):
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
"Error while fetching actor outbox: %s", obj.actor.outbox.url
|
"Error while fetching actor outbox: %s", obj.actor.outbox_url
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if result.get("next_page"):
|
if result.get("next_page"):
|
||||||
|
|
|
@ -200,6 +200,9 @@ def test_attachment_serializer_existing_file(factories, to_api_date):
|
||||||
"medium_square_crop": federation_utils.full_url(
|
"medium_square_crop": federation_utils.full_url(
|
||||||
attachment.file.crop["200x200"].url
|
attachment.file.crop["200x200"].url
|
||||||
),
|
),
|
||||||
|
"large_square_crop": federation_utils.full_url(
|
||||||
|
attachment.file.crop["600x600"].url
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +230,9 @@ def test_attachment_serializer_remote_file(factories, to_api_date):
|
||||||
"medium_square_crop": federation_utils.full_url(
|
"medium_square_crop": federation_utils.full_url(
|
||||||
proxy_url + "?next=medium_square_crop"
|
proxy_url + "?next=medium_square_crop"
|
||||||
),
|
),
|
||||||
|
"large_square_crop": federation_utils.full_url(
|
||||||
|
proxy_url + "?next=large_square_crop"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Added a new, large thumbnail size for cover images (#1205
|
|
@ -0,0 +1 @@
|
||||||
|
Enforce authentication when viewing remote channels, profiles and libraries (#1210)
|
|
@ -6,8 +6,8 @@
|
||||||
<div class="ui six wide column current-track">
|
<div class="ui six wide column current-track">
|
||||||
<div class="ui basic segment" id="player">
|
<div class="ui basic segment" id="player">
|
||||||
<template v-if="currentTrack">
|
<template v-if="currentTrack">
|
||||||
<img ref="cover" alt="" v-if="currentTrack.cover && currentTrack.cover.urls.original" :src="$store.getters['instance/absoluteUrl'](currentTrack.cover.urls.original)">
|
<img ref="cover" alt="" v-if="currentTrack.cover && currentTrack.cover.urls.large_square_crop" :src="$store.getters['instance/absoluteUrl'](currentTrack.cover.urls.large_square_crop)">
|
||||||
<img ref="cover" alt="" v-else-if="currentTrack.album && currentTrack.album.cover && currentTrack.album.cover.urls.original" :src="$store.getters['instance/absoluteUrl'](currentTrack.album.cover.urls.original)">
|
<img ref="cover" alt="" v-else-if="currentTrack.album && currentTrack.album.cover && currentTrack.album.cover.urls.large_square_crop" :src="$store.getters['instance/absoluteUrl'](currentTrack.album.cover.urls.large_square_crop)">
|
||||||
<img class="ui image" alt="" v-else src="../assets/audio/default-cover.png">
|
<img class="ui image" alt="" v-else src="../assets/audio/default-cover.png">
|
||||||
<h1 class="ui header">
|
<h1 class="ui header">
|
||||||
<div class="content ellipsis">
|
<div class="content ellipsis">
|
||||||
|
|
|
@ -20,6 +20,15 @@
|
||||||
<button class="ui floating dropdown circular icon basic button" :title="labels.more" v-dropdown="{direction: 'downward'}">
|
<button class="ui floating dropdown circular icon basic button" :title="labels.more" v-dropdown="{direction: 'downward'}">
|
||||||
<i class="ellipsis vertical icon"></i>
|
<i class="ellipsis vertical icon"></i>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
<a
|
||||||
|
:href="object.fid"
|
||||||
|
v-if="domain != $store.getters['instance/domain']"
|
||||||
|
target="_blank"
|
||||||
|
class="basic item">
|
||||||
|
<i class="external icon"></i>
|
||||||
|
<translate :translate-params="{domain: domain}" translate-context="Content/*/Button.Label/Verb">View on %{ domain }</translate>
|
||||||
|
</a>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
v-if="isEmbedable"
|
v-if="isEmbedable"
|
||||||
|
@ -86,6 +95,7 @@ import EmbedWizard from "@/components/audio/EmbedWizard"
|
||||||
import Modal from '@/components/semantic/Modal'
|
import Modal from '@/components/semantic/Modal'
|
||||||
import ReportMixin from '@/components/mixins/Report'
|
import ReportMixin from '@/components/mixins/Report'
|
||||||
|
|
||||||
|
import {getDomain} from '@/utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [ReportMixin],
|
mixins: [ReportMixin],
|
||||||
|
@ -108,6 +118,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
domain () {
|
||||||
|
if (this.object) {
|
||||||
|
return getDomain(this.object.fid)
|
||||||
|
}
|
||||||
|
},
|
||||||
labels() {
|
labels() {
|
||||||
return {
|
return {
|
||||||
more: this.$pgettext('*/*/Button.Label/Noun', "More…"),
|
more: this.$pgettext('*/*/Button.Label/Noun', "More…"),
|
||||||
|
|
|
@ -57,6 +57,15 @@
|
||||||
<button class="ui floating dropdown icon button" ref="dropdown" v-dropdown>
|
<button class="ui floating dropdown icon button" ref="dropdown" v-dropdown>
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
<a
|
||||||
|
:href="object.fid"
|
||||||
|
v-if="domain != $store.getters['instance/domain']"
|
||||||
|
target="_blank"
|
||||||
|
class="basic item">
|
||||||
|
<i class="external icon"></i>
|
||||||
|
<translate :translate-params="{domain: domain}" translate-context="Content/*/Button.Label/Verb">View on %{ domain }</translate>
|
||||||
|
</a>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
role="button"
|
role="button"
|
||||||
v-if="publicLibraries.length > 0"
|
v-if="publicLibraries.length > 0"
|
||||||
|
@ -137,6 +146,8 @@ import RadioButton from "@/components/radios/Button"
|
||||||
import TagsList from "@/components/tags/List"
|
import TagsList from "@/components/tags/List"
|
||||||
import ReportMixin from '@/components/mixins/Report'
|
import ReportMixin from '@/components/mixins/Report'
|
||||||
|
|
||||||
|
import {getDomain} from '@/utils'
|
||||||
|
|
||||||
const FETCH_URL = "albums/"
|
const FETCH_URL = "albums/"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -205,6 +216,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
domain () {
|
||||||
|
if (this.object) {
|
||||||
|
return getDomain(this.object.fid)
|
||||||
|
}
|
||||||
|
},
|
||||||
isPlayable() {
|
isPlayable() {
|
||||||
return (
|
return (
|
||||||
this.object.albums.filter(a => {
|
this.object.albums.filter(a => {
|
||||||
|
|
|
@ -44,6 +44,14 @@
|
||||||
<button class="ui floating dropdown circular icon basic button" :title="labels.more" v-dropdown="{direction: 'downward'}">
|
<button class="ui floating dropdown circular icon basic button" :title="labels.more" v-dropdown="{direction: 'downward'}">
|
||||||
<i class="ellipsis vertical icon"></i>
|
<i class="ellipsis vertical icon"></i>
|
||||||
<div class="menu" style="right: 0; left: auto">
|
<div class="menu" style="right: 0; left: auto">
|
||||||
|
<a
|
||||||
|
:href="track.fid"
|
||||||
|
v-if="domain != $store.getters['instance/domain']"
|
||||||
|
target="_blank"
|
||||||
|
class="basic item">
|
||||||
|
<i class="external icon"></i>
|
||||||
|
<translate :translate-params="{domain: domain}" translate-context="Content/*/Button.Label/Verb">View on %{ domain }</translate>
|
||||||
|
</a>
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
v-if="publicLibraries.length > 0"
|
v-if="publicLibraries.length > 0"
|
||||||
|
@ -116,6 +124,7 @@
|
||||||
import time from "@/utils/time"
|
import time from "@/utils/time"
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import url from "@/utils/url"
|
import url from "@/utils/url"
|
||||||
|
import {getDomain} from '@/utils'
|
||||||
import logger from "@/logging"
|
import logger from "@/logging"
|
||||||
import PlayButton from "@/components/audio/PlayButton"
|
import PlayButton from "@/components/audio/PlayButton"
|
||||||
import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon"
|
import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon"
|
||||||
|
@ -190,6 +199,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
domain () {
|
||||||
|
if (this.track) {
|
||||||
|
return getDomain(this.track.fid)
|
||||||
|
}
|
||||||
|
},
|
||||||
publicLibraries () {
|
publicLibraries () {
|
||||||
return this.libraries.filter(l => {
|
return this.libraries.filter(l => {
|
||||||
return l.privacy_level === 'everyone'
|
return l.privacy_level === 'everyone'
|
||||||
|
|
|
@ -51,3 +51,9 @@ export function checkRedirectToLogin (store, router) {
|
||||||
router.push({name: 'login', query: {next: router.currentRoute.fullPath}})
|
router.push({name: 'login', query: {next: router.currentRoute.fullPath}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDomain (url) {
|
||||||
|
let parser = document.createElement("a")
|
||||||
|
parser.href = url
|
||||||
|
return parser.hostname
|
||||||
|
}
|
|
@ -9,6 +9,14 @@
|
||||||
<button class="ui pointing dropdown icon small basic right floated button" ref="dropdown" v-dropdown="{direction: 'downward'}" style="position: absolute; right: 1em; top: 1em;">
|
<button class="ui pointing dropdown icon small basic right floated button" ref="dropdown" v-dropdown="{direction: 'downward'}" style="position: absolute; right: 1em; top: 1em;">
|
||||||
<i class="ellipsis vertical icon"></i>
|
<i class="ellipsis vertical icon"></i>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
<a
|
||||||
|
:href="object.fid"
|
||||||
|
v-if="object.domain != $store.getters['instance/domain']"
|
||||||
|
target="_blank"
|
||||||
|
class="basic item">
|
||||||
|
<i class="external icon"></i>
|
||||||
|
<translate :translate-params="{domain: object.domain}" translate-context="Content/*/Button.Label/Verb">View on %{ domain }</translate>
|
||||||
|
</a>
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
class="basic item"
|
class="basic item"
|
||||||
|
@ -93,7 +101,12 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetch()
|
let authenticated = this.$store.state.auth.authenticated
|
||||||
|
if (!authenticated && this.domain && this.$store.getters['instance/domain'] != this.domain) {
|
||||||
|
this.$router.push({name: 'login', query: {next: this.$route.fullPath}})
|
||||||
|
} else {
|
||||||
|
this.fetch()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
beforeRouteUpdate (to, from, next) {
|
beforeRouteUpdate (to, from, next) {
|
||||||
to.meta.preserveScrollPosition = true
|
to.meta.preserveScrollPosition = true
|
||||||
|
|
|
@ -84,6 +84,14 @@
|
||||||
<i class="code icon"></i>
|
<i class="code icon"></i>
|
||||||
<translate translate-context="Content/*/Button.Label/Verb">Embed</translate>
|
<translate translate-context="Content/*/Button.Label/Verb">Embed</translate>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
:href="object.url"
|
||||||
|
v-if="object.actor.domain != $store.getters['instance/domain']"
|
||||||
|
target="_blank"
|
||||||
|
class="basic item">
|
||||||
|
<i class="external icon"></i>
|
||||||
|
<translate :translate-params="{domain: object.actor.domain}" translate-context="Content/*/Button.Label/Verb">View on %{ domain }</translate>
|
||||||
|
</a>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<a
|
<a
|
||||||
href=""
|
href=""
|
||||||
|
@ -270,6 +278,10 @@ export default {
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.fetchData()
|
await this.fetchData()
|
||||||
|
let authenticated = this.$store.state.auth.authenticated
|
||||||
|
if (!authenticated && this.$store.getters['instance/domain'] != this.object.actor.domain) {
|
||||||
|
this.$router.push({name: 'login', query: {next: this.$route.fullPath}})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
<button class="ui pointing dropdown icon small basic right floated button" ref="dropdown" v-dropdown="{direction: 'downward'}" style="position: absolute; right: 1em; top: 1em;">
|
<button class="ui pointing dropdown icon small basic right floated button" ref="dropdown" v-dropdown="{direction: 'downward'}" style="position: absolute; right: 1em; top: 1em;">
|
||||||
<i class="ellipsis vertical icon"></i>
|
<i class="ellipsis vertical icon"></i>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
<a
|
||||||
|
:href="object.fid"
|
||||||
|
v-if="object.actor.domain != $store.getters['instance/domain']"
|
||||||
|
target="_blank"
|
||||||
|
class="basic item">
|
||||||
|
<i class="external icon"></i>
|
||||||
|
<translate :translate-params="{domain: object.actor.domain}" translate-context="Content/*/Button.Label/Verb">View on %{ domain }</translate>
|
||||||
|
</a>
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
class="basic item"
|
class="basic item"
|
||||||
|
@ -148,6 +156,10 @@ export default {
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.fetchData()
|
await this.fetchData()
|
||||||
|
let authenticated = this.$store.state.auth.authenticated
|
||||||
|
if (!authenticated && this.$store.getters['instance/domain'] != this.object.actor.domain) {
|
||||||
|
this.$router.push({name: 'login', query: {next: this.$route.fullPath}})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
|
|
Loading…
Reference in New Issue