Skip to content

Commit 714210e

Browse files
Fix manual add charter blacklist checks
1 parent f185115 commit 714210e

File tree

3 files changed

+148
-65
lines changed

3 files changed

+148
-65
lines changed

src/lib/validation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ export const playlistMutationSchema = z.discriminatedUnion("action", [
263263
songId: z.string(),
264264
requesterLogin: z.string().trim().min(2).max(25).optional(),
265265
title: z.string().min(1),
266+
authorId: z.number().optional(),
266267
artist: z.string().optional(),
267268
album: z.string().optional(),
268269
creator: z.string().optional(),
@@ -272,5 +273,6 @@ export const playlistMutationSchema = z.discriminatedUnion("action", [
272273
source: z.string(),
273274
sourceUrl: z.string().optional(),
274275
sourceId: z.number().optional(),
276+
candidateMatchesJson: z.string().optional(),
275277
}),
276278
]);

src/routes/dashboard/playlist.tsx

Lines changed: 100 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ type ManualSearchData = Pick<SearchResponse, "results">;
8989
type SearchResponse = {
9090
results: Array<{
9191
id: string;
92+
authorId?: number;
9293
title: string;
9394
artist?: string;
9495
album?: string;
@@ -523,72 +524,106 @@ function DashboardPlaylistPage() {
523524
<div>Tuning / Path</div>
524525
<div>Add</div>
525526
</div>
526-
{manualSearchQuery.data?.results?.map((song, index) => (
527-
<div
528-
key={song.id}
529-
className={`dashboard-playlist__manual-row grid grid-cols-[minmax(0,2.1fr)_minmax(0,1.3fr)_minmax(0,1fr)_96px] gap-4 border-t border-(--border) px-5 py-4 ${
530-
index % 2 === 0
531-
? "bg-(--panel-strong)"
532-
: "bg-(--panel-soft)"
533-
}`}
534-
>
535-
<div className="dashboard-playlist__manual-track min-w-0">
536-
<p className="truncate font-semibold text-(--text)">
537-
{song.title}
538-
</p>
539-
<p className="mt-1 truncate text-sm text-(--brand-deep)">
540-
{song.artist ?? "Unknown artist"}
541-
</p>
542-
</div>
543-
<div className="dashboard-playlist__manual-meta min-w-0">
544-
<p className="truncate text-sm text-(--text)">
545-
{song.album ?? "Unknown album"}
546-
</p>
547-
<p className="mt-1 truncate text-sm text-(--muted)">
548-
{song.creator
549-
? `Charted by ${song.creator}`
550-
: "Unknown creator"}
551-
</p>
552-
</div>
553-
<div className="dashboard-playlist__manual-extra min-w-0">
554-
<p className="truncate text-sm text-(--text)">
555-
{song.tuning ?? "No tuning info"}
556-
</p>
557-
<p className="mt-1 truncate text-sm text-(--muted)">
558-
{song.parts?.length
559-
? song.parts.join(", ")
560-
: "No path info"}
561-
</p>
562-
</div>
563-
<div className="dashboard-playlist__manual-add flex items-center justify-end">
564-
<Button
565-
size="sm"
566-
onClick={() =>
567-
mutation.mutate({
568-
action: "manualAdd",
569-
songId: song.id,
570-
requesterLogin:
571-
manualRequesterLogin.trim() || undefined,
572-
title: song.title,
573-
artist: song.artist,
574-
album: song.album,
575-
creator: song.creator,
576-
tuning: song.tuning,
577-
parts: song.parts,
578-
durationText: song.durationText,
579-
source: song.source,
580-
sourceUrl: song.sourceUrl,
581-
sourceId: song.sourceId,
582-
})
583-
}
584-
disabled={isManualAddPending(song.id)}
585-
>
586-
<Plus className="h-4 w-4" />
587-
Add
588-
</Button>
527+
{manualSearchQuery.data?.results?.map((song, index) => {
528+
const isBlacklistedCharter =
529+
song.authorId != null &&
530+
blacklistedCharterIds.has(song.authorId);
531+
532+
return (
533+
<div
534+
key={song.id}
535+
className={`dashboard-playlist__manual-row grid grid-cols-[minmax(0,2.1fr)_minmax(0,1.3fr)_minmax(0,1fr)_96px] gap-4 border-t border-(--border) px-5 py-4 ${
536+
index % 2 === 0
537+
? "bg-(--panel-strong)"
538+
: "bg-(--panel-soft)"
539+
} ${isBlacklistedCharter ? "opacity-55" : ""}`}
540+
>
541+
<div className="dashboard-playlist__manual-track min-w-0">
542+
<p className="truncate font-semibold text-(--text)">
543+
{song.title}
544+
</p>
545+
<p className="mt-1 truncate text-sm text-(--brand-deep)">
546+
{song.artist ?? "Unknown artist"}
547+
</p>
548+
</div>
549+
<div className="dashboard-playlist__manual-meta min-w-0">
550+
<p className="truncate text-sm text-(--text)">
551+
{song.album ?? "Unknown album"}
552+
</p>
553+
<div className="mt-1 flex flex-wrap items-center gap-2 text-sm text-(--muted)">
554+
<span>
555+
{song.creator
556+
? `Charted by ${song.creator}`
557+
: "Unknown creator"}
558+
</span>
559+
{isBlacklistedCharter ? (
560+
<Badge
561+
variant="outline"
562+
className="border-rose-400/40 bg-rose-500/10 text-rose-200"
563+
>
564+
Blacklisted
565+
</Badge>
566+
) : null}
567+
</div>
568+
</div>
569+
<div className="dashboard-playlist__manual-extra min-w-0">
570+
<p className="truncate text-sm text-(--text)">
571+
{song.tuning ?? "No tuning info"}
572+
</p>
573+
<p className="mt-1 truncate text-sm text-(--muted)">
574+
{song.parts?.length
575+
? song.parts.join(", ")
576+
: "No path info"}
577+
</p>
578+
</div>
579+
<div className="dashboard-playlist__manual-add flex items-center justify-end">
580+
<Button
581+
size="sm"
582+
onClick={() =>
583+
mutation.mutate({
584+
action: "manualAdd",
585+
songId: song.id,
586+
requesterLogin:
587+
manualRequesterLogin.trim() || undefined,
588+
title: song.title,
589+
authorId: song.authorId,
590+
artist: song.artist,
591+
album: song.album,
592+
creator: song.creator,
593+
tuning: song.tuning,
594+
parts: song.parts,
595+
durationText: song.durationText,
596+
source: song.source,
597+
sourceUrl: song.sourceUrl,
598+
sourceId: song.sourceId,
599+
candidateMatchesJson: JSON.stringify([
600+
{
601+
id: song.id,
602+
authorId: song.authorId,
603+
title: song.title,
604+
artist: song.artist,
605+
album: song.album,
606+
creator: song.creator,
607+
tuning: song.tuning,
608+
parts: song.parts ?? [],
609+
durationText: song.durationText,
610+
sourceUrl: song.sourceUrl,
611+
sourceId: song.sourceId,
612+
},
613+
]),
614+
})
615+
}
616+
disabled={
617+
isBlacklistedCharter || isManualAddPending(song.id)
618+
}
619+
>
620+
<Plus className="h-4 w-4" />
621+
Add
622+
</Button>
623+
</div>
589624
</div>
590-
</div>
591-
))}
625+
);
626+
})}
592627
</div>
593628
) : null}
594629
</CardContent>

src/workers/backend/index.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ class D1PlaylistCoordinator implements PlaylistCoordinator {
291291
}
292292

293293
async manualAdd(input: ManualAddInput): Promise<PlaylistMutationResult> {
294+
const db = getDb(this.env);
294295
const channel = await getDb(this.env).query.channels.findFirst({
295296
where: eq(channels.id, input.channelId),
296297
});
@@ -303,6 +304,51 @@ class D1PlaylistCoordinator implements PlaylistCoordinator {
303304
? await resolveTwitchUserForRequester(this.env, input.requesterLogin)
304305
: null;
305306

307+
const settings = await db.query.channelSettings.findFirst({
308+
where: eq(channelSettings.channelId, input.channelId),
309+
});
310+
const blacklist = await getChannelBlacklistByChannelId(
311+
this.env as unknown as never,
312+
input.channelId
313+
);
314+
315+
if (!settings) {
316+
throw new Error("Channel settings not found");
317+
}
318+
319+
const songAllowed = isSongAllowed({
320+
song: {
321+
id: input.song.id,
322+
artistId: undefined,
323+
authorId: input.song.authorId,
324+
title: input.song.title,
325+
artist: input.song.artist,
326+
album: input.song.album,
327+
creator: input.song.creator,
328+
tuning: input.song.tuning,
329+
parts: input.song.parts,
330+
durationText: input.song.durationText,
331+
sourceId: input.song.cdlcId,
332+
source: input.song.source,
333+
sourceUrl: input.song.sourceUrl,
334+
},
335+
settings,
336+
blacklistArtists: blacklist.blacklistArtists,
337+
blacklistCharters: blacklist.blacklistCharters,
338+
blacklistSongs: blacklist.blacklistSongs,
339+
setlistArtists: [],
340+
requester: {
341+
isBroadcaster: true,
342+
isModerator: false,
343+
isVip: false,
344+
isSubscriber: false,
345+
},
346+
});
347+
348+
if (!songAllowed.allowed) {
349+
throw new Error(songAllowed.reason ?? "That song is not allowed.");
350+
}
351+
306352
return this.addRequest({
307353
channelId: input.channelId,
308354
requestedByTwitchUserId: requester?.id ?? channel.twitchChannelId,

0 commit comments

Comments
 (0)