feat: Add explicit status for Navidrome and OpenSubsonic (#1220)

* add navidrome explicit status

* add ExplicitStatus enum and support opensubsonic

* add explicit status to cards
This commit is contained in:
Lyall
2025-10-26 13:48:45 +00:00
committed by GitHub
parent 68f242d208
commit 4dd52b0cef
16 changed files with 131 additions and 10 deletions
@@ -245,6 +245,7 @@ const normalizeSong = (
discNumber: (item.ParentIndexNumber && item.ParentIndexNumber) || 1,
discSubtitle: null,
duration: item.RunTimeTicks / 10000,
explicitStatus: null,
gain:
item.NormalizationGain !== undefined
? {
@@ -317,6 +318,7 @@ const normalizeAlbum = (
comment: null,
createdAt: item.DateCreated,
duration: item.RunTimeTicks / 10000,
explicitStatus: null,
genres: item.GenreItems?.map((entry) => ({
id: entry.Id,
imageUrl: null,
+2
View File
@@ -13,6 +13,7 @@ export enum NDAlbumListSort {
ALBUM_ARTIST = 'album_artist',
ARTIST = 'artist',
DURATION = 'duration',
EXPLICIT_STATUS = 'explicitStatus',
NAME = 'name',
PLAY_COUNT = 'play_count',
PLAY_DATE = 'play_date',
@@ -46,6 +47,7 @@ export enum NDSongListSort {
CHANNELS = 'channels',
COMMENT = 'comment',
DURATION = 'duration',
EXPLICIT_STATUS = 'explicitStatus',
FAVORITED = 'starred_at',
GENRE = 'genre',
ID = 'id',
@@ -7,6 +7,7 @@ import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import {
Album,
AlbumArtist,
ExplicitStatus,
Genre,
LibraryItem,
Playlist,
@@ -164,6 +165,12 @@ const normalizeSong = (
discNumber: item.discNumber,
discSubtitle: item.discSubtitle ? item.discSubtitle : null,
duration: item.duration * 1000,
explicitStatus:
item.explicitStatus === 'e'
? ExplicitStatus.EXPLICIT
: item.explicitStatus === 'c'
? ExplicitStatus.CLEAN
: null,
gain:
item.rgAlbumGain || item.rgTrackGain
? { album: item.rgAlbumGain, track: item.rgTrackGain }
@@ -237,6 +244,12 @@ const normalizeAlbum = (
comment: item.comment || null,
createdAt: item.createdAt.split('T')[0],
duration: item.duration !== undefined ? item.duration * 1000 : null,
explicitStatus:
item.explicitStatus === 'e'
? ExplicitStatus.EXPLICIT
: item.explicitStatus === 'c'
? ExplicitStatus.CLEAN
: null,
genres: (item.genres || []).map((genre) => ({
id: genre.id,
imageUrl: null,
@@ -134,6 +134,7 @@ const album = z.object({
coverArtPath: z.string().optional(), // Removed after v0.48.0
createdAt: z.string(),
duration: z.number().optional(),
explicitStatus: z.string().optional(),
fullText: z.string(),
genre: z.string(),
genres: z.array(genre).nullable(),
@@ -200,6 +201,7 @@ const song = z.object({
discSubtitle: z.string().optional(),
duration: z.number(),
embedArtPath: z.string().optional(),
explicitStatus: z.string().optional(),
externalInfoUpdatedAt: z.string().optional(),
externalUrl: z.string().optional(),
fullText: z.string(),
@@ -5,6 +5,7 @@ import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import {
Album,
AlbumArtist,
ExplicitStatus,
Genre,
LibraryItem,
Playlist,
@@ -146,6 +147,12 @@ const normalizeSong = (
discNumber: item.discNumber || 1,
discSubtitle: null,
duration: item.duration ? item.duration * 1000 : 0,
explicitStatus:
item.explicitStatus === 'explicit'
? ExplicitStatus.EXPLICIT
: item.explicitStatus === 'clean'
? ExplicitStatus.CLEAN
: null,
gain:
item.replayGain && (item.replayGain.albumGain || item.replayGain.trackGain)
? {
@@ -247,6 +254,12 @@ const normalizeAlbum = (
comment: null,
createdAt: item.created,
duration: item.duration * 1000,
explicitStatus:
item.explicitStatus === 'explicit'
? ExplicitStatus.EXPLICIT
: item.explicitStatus === 'clean'
? ExplicitStatus.CLEAN
: null,
genres: getGenres(item),
id: item.id.toString(),
imagePlaceholderUrl: null,
@@ -95,6 +95,7 @@ const song = z.object({
created: z.string(),
discNumber: z.number(),
duration: z.number().optional(),
explicitStatus: z.string().optional(),
genre: z.string().optional(),
genres: z.array(genreItem).optional(),
id,
@@ -125,6 +126,7 @@ const album = z.object({
coverArt: z.string(),
created: z.string(),
duration: z.number(),
explicitStatus: z.string().optional(),
genre: z.string().optional(),
genres: z.array(genreItem).optional(),
id,
+15
View File
@@ -129,6 +129,11 @@ export const sortOrderMap: SortOrderMap = {
},
};
export enum ExplicitStatus {
CLEAN = 'CLEAN',
EXPLICIT = 'EXPLICIT',
}
export enum ExternalSource {
LASTFM = 'LASTFM',
MUSICBRAINZ = 'MUSICBRAINZ',
@@ -160,6 +165,7 @@ export type Album = {
comment: null | string;
createdAt: string;
duration: null | number;
explicitStatus: ExplicitStatus | null;
genres: Genre[];
id: string;
imagePlaceholderUrl: null | string;
@@ -332,6 +338,7 @@ export type Song = {
discNumber: number;
discSubtitle: null | string;
duration: number;
explicitStatus: ExplicitStatus | null;
gain: GainInfo | null;
genres: Genre[];
id: string;
@@ -394,6 +401,7 @@ export enum AlbumListSort {
COMMUNITY_RATING = 'communityRating',
CRITIC_RATING = 'criticRating',
DURATION = 'duration',
EXPLICIT_STATUS = 'explicitStatus',
FAVORITED = 'favorited',
NAME = 'name',
PLAY_COUNT = 'playCount',
@@ -441,6 +449,7 @@ export const albumListSortMap: AlbumListSortMap = {
communityRating: JFAlbumListSort.COMMUNITY_RATING,
criticRating: JFAlbumListSort.CRITIC_RATING,
duration: undefined,
explicitStatus: undefined,
favorited: undefined,
name: JFAlbumListSort.NAME,
playCount: JFAlbumListSort.PLAY_COUNT,
@@ -458,6 +467,7 @@ export const albumListSortMap: AlbumListSortMap = {
communityRating: undefined,
criticRating: undefined,
duration: NDAlbumListSort.DURATION,
explicitStatus: NDAlbumListSort.EXPLICIT_STATUS,
favorited: NDAlbumListSort.STARRED,
name: NDAlbumListSort.NAME,
playCount: NDAlbumListSort.PLAY_COUNT,
@@ -476,6 +486,7 @@ export const albumListSortMap: AlbumListSortMap = {
communityRating: undefined,
criticRating: undefined,
duration: undefined,
explicitStatus: undefined,
favorited: undefined,
name: undefined,
playCount: undefined,
@@ -497,6 +508,7 @@ export enum SongListSort {
CHANNELS = 'channels',
COMMENT = 'comment',
DURATION = 'duration',
EXPLICIT_STATUS = 'explicitStatus',
FAVORITED = 'favorited',
GENRE = 'genre',
ID = 'id',
@@ -562,6 +574,7 @@ export const songListSortMap: SongListSortMap = {
channels: undefined,
comment: undefined,
duration: JFSongListSort.DURATION,
explicitStatus: undefined,
favorited: undefined,
genre: undefined,
id: undefined,
@@ -582,6 +595,7 @@ export const songListSortMap: SongListSortMap = {
channels: NDSongListSort.CHANNELS,
comment: NDSongListSort.COMMENT,
duration: NDSongListSort.DURATION,
explicitStatus: NDSongListSort.EXPLICIT_STATUS,
favorited: NDSongListSort.FAVORITED,
genre: NDSongListSort.GENRE,
id: NDSongListSort.ID,
@@ -602,6 +616,7 @@ export const songListSortMap: SongListSortMap = {
channels: undefined,
comment: undefined,
duration: undefined,
explicitStatus: undefined,
favorited: undefined,
genre: undefined,
id: undefined,
+2 -1
View File
@@ -1,5 +1,6 @@
import { AppRoute } from '@ts-rest/core';
import { ReactNode } from 'react';
import { TFunction } from 'react-i18next';
import { Song } from 'src/main/features/core/lyrics/netease';
import {
@@ -40,7 +41,7 @@ export type CardRoute = {
export type CardRow<T> = {
arrayProperty?: string;
format?: (value: T) => ReactNode;
format?: (value: T, t: TFunction) => ReactNode;
property: keyof T;
route?: CardRoute;
};