fix(front):only request playlist.libfollow if not my playlist NOCHANGELOG

This commit is contained in:
petitminion 2025-05-21 16:28:53 +00:00
parent c9600fd0ac
commit 81b40be7f5
14 changed files with 103 additions and 83 deletions

View File

@ -189,4 +189,4 @@ def filter_files(files, allowed_extensions):
def get_search_url(query, page_size, page): def get_search_url(query, page_size, page):
q = urllib.parse.urlencode({"q": query}) q = urllib.parse.urlencode({"q": query})
return f"https://archive.org/advancedsearch.php?{q}&sort[]=addeddate+desc&rows={page_size}\ return f"https://archive.org/advancedsearch.php?{q}&sort[]=addeddate+desc&rows={page_size}\
&page={page}&output=json&mediatype=audio" &page={page}&output=json"

View File

@ -122,7 +122,7 @@ store.dispatch('auth/fetchUser')
.responsive { .responsive {
display: grid !important; display: grid !important;
grid-template-rows: min-content; grid-template-rows: min-content;
min-height: calc(100vh - 64px); min-height: 100vh;
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
grid-template-columns: 300px 1fr; grid-template-columns: 300px 1fr;

View File

@ -108,7 +108,7 @@ const labels = computed(() => ({
const isOpen = ref(false) const isOpen = ref(false)
const playlistFollowInfo = computed(() => { const playlistLibraryFollowInfo = computed(() => {
const playlist = props.playlist; const playlist = props.playlist;
if (!playlist) return null; if (!playlist) return null;
@ -268,13 +268,13 @@ const playlistFollowInfo = computed(() => {
{{ obj.label }} {{ obj.label }}
</PopoverItem> </PopoverItem>
<PopoverItem <PopoverItem
v-if="playlist && playlistFollowInfo" v-if="playlist && playlistLibraryFollowInfo && store.state.auth.profile && playlist.actor.full_username != store.state.auth.fullUsername"
:title="playlistFollowInfo.tooltip" :title="playlistLibraryFollowInfo.tooltip"
:icon="playlistFollowInfo.icon" :icon="playlistLibraryFollowInfo.icon"
:disabled="playlistFollowInfo.disabled" :disabled="playlistLibraryFollowInfo.disabled"
@click.stop.prevent="requestPlaylistUploadsAccess(playlist)" @click.stop.prevent="requestPlaylistUploadsAccess(playlist)"
> >
{{ playlistFollowInfo.label }} {{ playlistLibraryFollowInfo.label }}
</PopoverItem> </PopoverItem>
</template> </template>
</Popover> </Popover>

View File

@ -7,7 +7,7 @@ import useReport from '~/composables/moderation/useReport'
import { useStore } from '~/store' import { useStore } from '~/store'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue' import { computed } from 'vue'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { generateTrackCreditString, getArtistCoverUrl } from '~/utils/utils' import { generateTrackCreditString, getArtistCoverUrl } from '~/utils/utils'
@ -50,7 +50,6 @@ const props = withDefaults(defineProps<Props>(), {
account: null account: null
}) })
const modal = ref()
const show = useVModel(props, 'show', emit) const show = useVModel(props, 'show', emit)
@ -94,11 +93,8 @@ const labels = computed(() => ({
<template> <template>
<Modal <Modal
ref="modal"
v-model="show" v-model="show"
:title="track.title" :title="track.title"
:scrolling="true"
class="scrolling-track-options"
> >
<div class="header"> <div class="header">
<div class="ui large centered rounded image"> <div class="ui large centered rounded image">

View File

@ -67,7 +67,14 @@ const submitAndScan = async () => {
:class="['ui form', {loading: isLoading}]" :class="['ui form', {loading: isLoading}]"
@submit.prevent="submit" @submit.prevent="submit"
> >
<h3>{{ plugin.label }}</h3> <h2>{{ plugin.label }}</h2>
<Alert blue>
<Layout flex>
<p><i class="bi bi-info-circle-fill" /></p>
<Layout
stack
no-gap
>
<sanitized-html <sanitized-html
v-if="plugin.description" v-if="plugin.description"
:html="description" :html="description"
@ -77,10 +84,13 @@ const submitAndScan = async () => {
:href="plugin.homepage" :href="plugin.homepage"
target="_blank" target="_blank"
> >
<i class="external icon" /> <i class="bi bi-box-arrow-up-right" />
{{ t('components.auth.Plugin.link.documentation') }} {{ t('components.auth.Plugin.link.documentation') }}
</a> </a>
</template> </template>
</Layout>
</Layout>
</Alert>
<Alert <Alert
v-if="errors.length > 0" v-if="errors.length > 0"
red red

View File

@ -2,7 +2,7 @@
import type { OrderingProps } from '~/composables/navigation/useOrdering' import type { OrderingProps } from '~/composables/navigation/useOrdering'
import type { RouteRecordName } from 'vue-router' import type { RouteRecordName } from 'vue-router'
import type { OrderingField } from '~/store/ui' import type { OrderingField } from '~/store/ui'
import type { Track } from '~/types' import type { UserTrackFavorite } from '~/types'
import { computed, onMounted, reactive, ref, watch } from 'vue' import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -53,7 +53,7 @@ const sharedLabels = useSharedLabels()
const { onOrderingUpdate, orderingString, paginateBy, ordering, orderingDirection } = useOrdering(props) const { onOrderingUpdate, orderingString, paginateBy, ordering, orderingDirection } = useOrdering(props)
const results = reactive<Track[]>([]) const results = reactive<UserTrackFavorite[]>([])
const nextLink = ref() const nextLink = ref()
const previousLink = ref() const previousLink = ref()
const count = ref(0) const count = ref(0)
@ -66,7 +66,7 @@ const fetchFavorites = async () => {
page: page.value, page: page.value,
page_size: paginateBy.value, page_size: paginateBy.value,
ordering: orderingString.value, ordering: orderingString.value,
scope: store.state.auth.fullUsername scope: "me"
} }
const measureLoading = logger.time('Loading user favorites') const measureLoading = logger.time('Loading user favorites')
@ -76,8 +76,8 @@ const fetchFavorites = async () => {
results.length = 0 results.length = 0
results.push(...response.data.results) results.push(...response.data.results)
for (const track of results) { for (const trackfavorite of results) {
store.commit('favorites/track', { id: track.id, value: true }) store.commit('favorites/track', { id: trackfavorite.track.id, value: true })
} }
count.value = response.data.count count.value = response.data.count
@ -225,7 +225,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
:search="true" :search="true"
:show-artist="true" :show-artist="true"
:show-album="true" :show-album="true"
:tracks="results" :tracks="results.map(r => r.track)"
/> />
</Layout> </Layout>
<Alert <Alert

View File

@ -2,19 +2,19 @@
import type { Library } from '~/types' import type { Library } from '~/types'
import { ref, reactive, onMounted, watch } from 'vue' import { ref, reactive, onMounted, watch } from 'vue'
import { useStore } from '~/store'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import axios from 'axios' import axios from 'axios'
import LibraryCard from '~/views/content/remote/Card.vue'
import Button from '~/components/ui/Button.vue' import Button from '~/components/ui/Button.vue'
import Section from '~/components/ui/Section.vue' import Section from '~/components/ui/Section.vue'
import Loader from '~/components/ui/Loader.vue' import Loader from '~/components/ui/Loader.vue'
import Alert from '~/components/ui/Alert.vue' import Alert from '~/components/ui/Alert.vue'
import Spacer from '~/components/ui/Spacer.vue' import Spacer from '~/components/ui/Spacer.vue'
import ActorLink from '~/components/common/ActorLink.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
import Layout from '../ui/Layout.vue'
interface Events { interface Events {
(e: 'loaded', libraries: Library[]): void (e: 'loaded', libraries: Library[]): void
@ -26,7 +26,6 @@ interface Props {
} }
const { t } = useI18n() const { t } = useI18n()
const store = useStore()
const emit = defineEmits<Events>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
@ -67,7 +66,6 @@ watch(() => props.url, () => {
<Section <Section
align-left align-left
:h2="title" :h2="title"
:columns-per-item="3"
> >
<Loader <Loader
v-if="isLoading" v-if="isLoading"
@ -80,14 +78,21 @@ watch(() => props.url, () => {
> >
{{ t('components.federation.LibraryWidget.empty.noMatch') }} {{ t('components.federation.LibraryWidget.empty.noMatch') }}
</Alert> </Alert>
<library-card <Layout
v-if="!isLoading && libraries.length > 0"
flex
>
{{ t('components.federation.LibraryWidget.main') }}
<template
v-for="library in libraries" v-for="library in libraries"
:key="library.uuid" :key="library.uuid"
:display-scan="false" >
:display-follow="store.state.auth.authenticated && library.actor.full_username != store.state.auth.fullUsername" <ActorLink
:initial-library="library" :actor="library.actor"
:display-copy-fid="true" discrete
/> />
</template>
</Layout>
<template v-if="nextPage"> <template v-if="nextPage">
<Spacer /> <Spacer />
<Button <Button

View File

@ -1414,7 +1414,8 @@
}, },
"empty": { "empty": {
"noMatch": "No matching library." "noMatch": "No matching library."
} },
"main": "This audio object is present in the audio collection of"
} }
}, },
"forms": { "forms": {
@ -1445,7 +1446,7 @@
}, },
"header": { "header": {
"episodes": "Episodes", "episodes": "Episodes",
"libraries": "User libraries", "libraries": "On the network",
"tracks": "Tracks" "tracks": "Tracks"
}, },
"meta": { "meta": {

View File

@ -68,6 +68,7 @@ export type Cover = components['schemas']['CoverField']
export type RateLimitStatus = components['schemas']['RateLimit']['scopes'][number] export type RateLimitStatus = components['schemas']['RateLimit']['scopes'][number]
export type PaginatedAlbumList = components['schemas']['PaginatedAlbumList'] export type PaginatedAlbumList = components['schemas']['PaginatedAlbumList']
export type PaginatedChannelList = components['schemas']['PaginatedChannelList'] export type PaginatedChannelList = components['schemas']['PaginatedChannelList']
export type UserTrackFavorite = components['schemas']['UserTrackFavorite']
export type Artist = components['schemas']['Artist'] export type Artist = components['schemas']['Artist']

View File

@ -155,7 +155,7 @@ const categories = computed(() => [
type: 'playlists', type: 'playlists',
label: t('views.Search.label.playlists'), label: t('views.Search.label.playlists'),
more: '/library/playlists/', more: '/library/playlists/',
endpoint: '/TODO' endpoint: '/playlists'
}, },
{ {
type: 'radios', type: 'radios',

View File

@ -1,9 +1,8 @@
import type { Track, Album, ArtistCredit, QueueItemSource } from '~/types' import type { Track, Album, ArtistCredit, QueueItemSource } from '~/types'
import type { components } from '~/generated/types' import type { components } from '~/generated/types'
import { useStore } from '~/store'
import type { QueueTrack } from '~/composables/audio/queue' import type { QueueTrack } from '~/composables/audio/queue'
import store from '~/store'
const store = useStore()
export function generateTrackCreditString (track: Track | Album | null): string | null { export function generateTrackCreditString (track: Track | Album | null): string | null {
if (!track || !track.artist_credit || track.artist_credit.length === 0) { if (!track || !track.artist_credit || track.artist_credit.length === 0) {
@ -51,5 +50,15 @@ const getSimpleArtistCover = (artist: components['schemas']['SimpleChannelArtist
* @param artist: a simple artist * @param artist: a simple artist
* @param field: the size you want * @param field: the size you want
*/ */
export const getSimpleArtistCoverUrl = (artist: components['schemas']['SimpleChannelArtist'] | components['schemas']['Artist'] | components['schemas']['ArtistWithAlbums'], field: 'original' | 'small_square_crop' | 'medium_square_crop' | 'large_square_crop') => export const getSimpleArtistCoverUrl = (
store.getters['instance/absoluteUrl'](getSimpleArtistCover(artist)(field)) artist: components['schemas']['SimpleChannelArtist'] | components['schemas']['Artist'] | components['schemas']['ArtistWithAlbums'],
field: 'original' | 'small_square_crop' | 'medium_square_crop' | 'large_square_crop'
): string | null => {
const coverGetter = getSimpleArtistCover(artist);
if (!coverGetter) return null;
const cover = coverGetter(field);
if (!cover) return null;
return store.getters['instance/absoluteUrl'](cover);
};

View File

@ -6,7 +6,10 @@ import { computed, ref } from 'vue'
import axios from 'axios' import axios from 'axios'
import Layout from '~/components/ui/Layout.vue'
import Alert from '~/components/ui/Alert.vue' import Alert from '~/components/ui/Alert.vue'
import Input from '~/components/ui/Input.vue'
import Link from '~/components/ui/Link.vue'
import Button from '~/components/ui/Button.vue' import Button from '~/components/ui/Button.vue'
interface Props { interface Props {
@ -56,15 +59,14 @@ const submit = async () => {
class="main" class="main"
> >
<h2>{{ labels.changePassword }}</h2> <h2>{{ labels.changePassword }}</h2>
<form <Layout
v-if="!success" v-if="!success"
class="ui form" form
@submit.prevent="submit()" @submit.prevent="submit()"
> >
<Alert <Alert
v-if="errors.length > 0" v-if="errors.length > 0"
role="alert" red
class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
{{ t('views.auth.PasswordResetConfirm.header.failure') }} {{ t('views.auth.PasswordResetConfirm.header.failure') }}
@ -83,26 +85,33 @@ const submit = async () => {
<Input <Input
v-model="newPassword" v-model="newPassword"
password password
label="t('views.auth.PasswordResetConfirm.label.newPassword')" :label="t('views.auth.PasswordResetConfirm.label.newPassword')"
/> />
</div> </div>
<router-link :to="{path: '/login'}"> <Layout flex>
<Link
solid
secondary
:to="{path: '/login'}"
>
{{ t('views.auth.PasswordResetConfirm.link.back') }} {{ t('views.auth.PasswordResetConfirm.link.back') }}
</router-link> </Link>
<Button <Button
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']"
type="submit" type="submit"
auto auto
primary
> >
{{ t('views.auth.PasswordResetConfirm.button.update') }} {{ t('views.auth.PasswordResetConfirm.button.update') }}
</Button> </Button>
</Layout>
</template> </template>
<template v-else> <template v-else>
<p> <p>
{{ t('views.auth.PasswordResetConfirm.message.requestSent') }} {{ t('views.auth.PasswordResetConfirm.message.requestSent') }}
</p> </p>
</template> </template>
</form> </Layout>
<Alert <Alert
v-else v-else
green green

View File

@ -7,6 +7,7 @@ import axios from 'axios'
import PluginForm from '~/components/auth/Plugin.vue' import PluginForm from '~/components/auth/Plugin.vue'
import Layout from '~/components/ui/Layout.vue' import Layout from '~/components/ui/Layout.vue'
import Loader from '~/components/ui/Loader.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
@ -46,13 +47,10 @@ fetchData()
main main
stack stack
> >
<h2>{{ labels.title }}</h2> <h1>{{ labels.title }}</h1>
<div <Loader
v-if="isLoading" v-if="isLoading"
class="ui inverted active dimmer" />
>
<div class="ui loader" />
</div>
<template v-if="plugins && plugins.length > 0"> <template v-if="plugins && plugins.length > 0">
<plugin-form <plugin-form

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Actor } from '~/types' import type { Actor } from '~/types'
import LibraryWidget from '~/components/federation/LibraryWidget.vue'
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue' import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
import ChannelForm from '~/components/audio/ChannelForm.vue' import ChannelForm from '~/components/audio/ChannelForm.vue'
import { ref } from 'vue' import { ref } from 'vue'
@ -59,14 +58,6 @@ const createForm = ref()
</div> </div>
</h2> </h2>
<channels-widget :filters="{scope: `actor:${object?.full_username}`}" /> <channels-widget :filters="{scope: `actor:${object?.full_username}`}" />
<h2 class="ui with-actions header">
{{ t('views.auth.ProfileOverview.header.libraries') }}
</h2>
<library-widget :url="`federation/actors/${object?.full_username}/libraries/`">
<template #title>
{{ t('views.auth.ProfileOverview.header.sharedLibraries') }}
</template>
</library-widget>
</div> </div>
<Modal <Modal