Unlock everything, add a donation link, credit Last.fm, nudge re-linking
- Every feature is free for everyone (the plan system stays for later); remaining limits are abuse guards - Optional website donation link (NEXT_PUBLIC_DONATION_URL). Not shown in the mobile apps, where tips to the developer generally need in-app purchase - Last.fm/ListenBrainz-sourced plays link to the user's profile, as Last.fm's API terms require - Connections track when they last saw activity; a scrobbler silent for over a week prompts re-linking Spotify (its link expires every 6 months) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { and, count, eq, ilike, isNotNull, or } from 'drizzle-orm';
|
||||
import { and, count, eq, ilike, inArray, isNotNull, or } from 'drizzle-orm';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import { sortFeed, type FeedItemDTO, type ProfileDTO } from '@musicshare/shared';
|
||||
import { sortFeed, sourceProfileUrl, type FeedItemDTO, type ProfileDTO } from '@musicshare/shared';
|
||||
import { db, schema } from '../db';
|
||||
import { requireUser } from '../lib/auth';
|
||||
import { entitlementsFor } from '../lib/entitlements';
|
||||
@@ -31,7 +31,7 @@ export async function userRoutes(app: FastifyInstance) {
|
||||
app.get<{ Params: { username: string } }>('/users/:username', async (req): Promise<ProfileDTO> => {
|
||||
const user = await findByUsername(req.params.username);
|
||||
const viewer = req.user;
|
||||
const [[followers], [following], [follow], [featured], nowPlaying, recent] = await Promise.all([
|
||||
const [[followers], [following], [follow], [featured], nowPlaying, recent, scrobblers] = await Promise.all([
|
||||
db.select({ n: count() }).from(schema.follows).where(eq(schema.follows.followeeId, user.id)),
|
||||
db.select({ n: count() }).from(schema.follows).where(eq(schema.follows.followerId, user.id)),
|
||||
viewer
|
||||
@@ -43,7 +43,16 @@ export async function userRoutes(app: FastifyInstance) {
|
||||
db.select().from(schema.featuredSongs).where(eq(schema.featuredSongs.userId, user.id)),
|
||||
getNowPlaying([user.id]),
|
||||
getRecentPlays(user.id),
|
||||
db
|
||||
.select({ provider: schema.connections.provider, externalUserId: schema.connections.externalUserId })
|
||||
.from(schema.connections)
|
||||
.where(and(eq(schema.connections.userId, user.id), inArray(schema.connections.provider, ['lastfm', 'listenbrainz']))),
|
||||
]);
|
||||
const attribution: ProfileDTO['attribution'] = {};
|
||||
for (const c of scrobblers) {
|
||||
const url = sourceProfileUrl(c.provider, c.externalUserId);
|
||||
if (url && (c.provider === 'lastfm' || c.provider === 'listenbrainz')) attribution[c.provider] = url;
|
||||
}
|
||||
const featuredTrack = featured ? (await getTrackDTOs([featured.trackId])).get(featured.trackId) : undefined;
|
||||
await watchUsers([user.id]);
|
||||
|
||||
@@ -60,6 +69,7 @@ export async function userRoutes(app: FastifyInstance) {
|
||||
: null,
|
||||
nowPlaying: nowPlaying[0] ?? null,
|
||||
recent,
|
||||
attribution,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user