upd: port Listenbrainz

This commit is contained in:
Insert5StarName 2023-09-22 03:31:59 +02:00
parent 57c37a8938
commit 113a67077e
9 changed files with 210 additions and 3 deletions

View file

@ -32,6 +32,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #prefix><i class="ti ti-cake"></i></template>
</MkInput>
<MkInput v-model="profile.listenbrainz" manualSave>
<template #label>ListenBrainz</template>
<template #prefix><i class="ti ti-headphones"></i></template>
</MkInput>
<MkSelect v-model="profile.lang">
<template #label>{{ i18n.ts.language }}</template>
<option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option>
@ -132,6 +137,7 @@ const profile = reactive({
description: $i.description,
location: $i.location,
birthday: $i.birthday,
listenbrainz: $i?.listenbrainz,
lang: $i.lang,
isBot: $i.isBot,
isCat: $i.isCat,
@ -179,6 +185,7 @@ function save() {
location: profile.location || null,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
birthday: profile.birthday || null,
listenbrainz: profile.listenbrainz || null,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
lang: profile.lang || null,
isBot: !!profile.isBot,

View file

@ -137,6 +137,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;">
<XPhotos :key="user.id" :user="user"/>
<XActivity :key="user.id" :user="user"/>
<XListenBrainz
v-if="user.listenbrainz && listenbrainzdata"
:key="user.id"
:user="user"
style="margin-top: var(--margin)"
/>
</div>
</div>
</MkSpacer>
@ -166,7 +172,6 @@ import { confetti } from '@/scripts/confetti.js';
import MkNotes from '@/components/MkNotes.vue';
import { api } from '@/os.js';
import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
function calcAge(birthdate: string): number {
const date = new Date(birthdate);
const now = new Date();
@ -184,6 +189,7 @@ function calcAge(birthdate: string): number {
const XPhotos = defineAsyncComponent(() => import('./index.photos.vue'));
const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
const XListenBrainz = defineAsyncComponent(() => import("./index.listenbrainz.vue"));;
const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed;
@ -205,6 +211,24 @@ let isEditingMemo = $ref(false);
let moderationNote = $ref(props.user.moderationNote);
let editModerationNote = $ref(false);
let listenbrainzdata = false;
if (props.user.listenbrainz) {
try {
const response = await fetch(`https://api.listenbrainz.org/1/user/${props.user.listenbrainz}/playing-now`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
const data = await response.json();
if (data.payload.listens && data.payload.listens.length !== 0) {
listenbrainzdata = true;
}
} catch(err) {
listenbrainzdata = false;
}
}
watch($$(moderationNote), async () => {
await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote });
});

View file

@ -0,0 +1,138 @@
<template>
<MkContainer :foldable="true">
<template #header
><i
class="ti ti-headphones"
style="margin-right: 0.5em"
></i
>Music</template
>
<div style="padding: 8px">
<div class="flex">
<a :href="listenbrainz.musicbrainzurl">
<img class="image" :src="listenbrainz.img" :alt="listenbrainz.title" />
<div class="flex flex-col items-start">
<p class="text-sm font-bold">Now Playing: {{ listenbrainz.title }}</p>
<p class="text-xs font-medium">{{ listenbrainz.artist }}</p>
</div>
</a>
<a :href="listenbrainz.listenbrainzurl">
<div class="playicon">
<i class="ti ti-player-play-filled"></i>
</div>
</a>
</div>
</div>
</MkContainer>
</template>
<script lang="ts" setup>
import {} from "vue";
import * as misskey from "misskey-js";
import MkContainer from "@/components/MkContainer.vue";
const props = withDefaults(
defineProps<{
user: misskey.entities.User;
}>(),
{},
);
const listenbrainz = {title: '', artist: '', lastlisten: '', img: '', musicbrainzurl: '', listenbrainzurl: ''};
if (props.user.listenbrainz) {
const getLMData = async (title: string, artist: string) => {
const response = await fetch(`https://api.listenbrainz.org/1/metadata/lookup/?artist_name=${artist}&recording_name=${title}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
})
const data = await response.json();
if (!data.recording_name) {
return null;
}
const titler: string = data.recording_name;
const artistr: string = data.artist_credit_name;
const img: string = data.release_mbid ? `https://coverartarchive.org/release/${data.release_mbid}/front-250` : 'https://coverartarchive.org/img/big_logo.svg';
const musicbrainzurl: string = data.recording_mbid ? `https://musicbrainz.org/recording/${data.recording_mbid}` : '#';
const listenbrainzurl: string = data.recording_mbid ? `https://listenbrainz.org/player?recording_mbids=${data.recording_mbid}` : '#';
return [titler, artistr, img, musicbrainzurl, listenbrainzurl];
};
const response = await fetch(`https://api.listenbrainz.org/1/user/${props.user.listenbrainz}/playing-now`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
const data = await response.json();
if (data.payload.listens && data.payload.listens.length !== 0) {
const title: string = data.payload.listens[0].track_metadata.track_name;
const artist: string = data.payload.listens[0].track_metadata.artist_name;
const lastlisten: string = data.payload.listens[0].playing_now;
const img: string = 'https://coverartarchive.org/img/big_logo.svg';
await getLMData(title, artist).then((data) => {
if (!data) {
listenbrainz.title = title;
listenbrainz.img = img;
listenbrainz.artist = artist;
listenbrainz.lastlisten = lastlisten;
return;
} else {
listenbrainz.title = data[0];
listenbrainz.img = data[2];
listenbrainz.artist = data[1];
listenbrainz.lastlisten = lastlisten;
listenbrainz.musicbrainzurl = data[3];
listenbrainz.listenbrainzurl = data[4];
return;
}
});
}
}
</script>
<style lang="scss" scoped>
.flex {
display: flex;
align-items: center;
}
.flex a {
display: flex;
align-items: center;
text-decoration: none;
}
.image {
height: 4.8rem;
margin-right: 0.7rem;
}
.items-start {
align-items: flex-start;
}
.flex-col {
display: flex;
flex-direction: column;
}
.text-sm {
font-size: 0.875rem;
margin: 0;
margin-bottom: 0.3rem;
}
.font-bold {
font-weight: 700;
}
.text-xs {
font-size: 0.75rem;
margin: 0;
}
.font-medium {
font-weight: 500;
}
.playicon {
display: flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
font-size: 1.7rem;
padding-left: 3rem;
}
</style>