feat(v3): add music search and subscription UI

This commit is contained in:
jxxghp
2026-08-07 21:50:22 +08:00
parent 7ea14bc919
commit 6afa2bdc8a
36 changed files with 812 additions and 80 deletions
+7 -1
View File
@@ -34,7 +34,7 @@ describe('recommendSources', () => {
it('creates the complete built-in source contract', () => {
const sources = createBuiltInRecommendSources(translate)
expect(sources).toHaveLength(15)
expect(sources).toHaveLength(16)
expect(sources[0]).toEqual({
apipath: 'recommend/tmdb_trending',
linkurl: '/browse/recommend/tmdb_trending?title=translated:recommend.trendingNow',
@@ -60,6 +60,12 @@ describe('recommendSources', () => {
title: 'translated:recommend.anilistPopularThisSeason',
type: 'translated:recommend.categoryAnime',
})
expect(sources).toContainEqual({
apipath: 'recommend/music_weekly',
linkurl: '/browse/recommend/music_weekly?title=translated:recommend.listenBrainzWeekly',
title: 'translated:recommend.listenBrainzWeekly',
type: 'translated:recommend.categoryMusic',
})
expect(
sources.filter(source => source.type === 'translated:recommend.categoryAnime').map(source => source.apipath),
).toEqual([
+16 -2
View File
@@ -31,6 +31,7 @@ export const PERMISSION_FEATURE = {
SEARCH_RESOURCE: 'search.resource',
SUBSCRIBE_MOVIE: 'subscribe.movie',
SUBSCRIBE_TV: 'subscribe.tv',
SUBSCRIBE_MUSIC: 'subscribe.music',
SUBSCRIBE_CALENDAR: 'subscribe.calendar',
SUBSCRIBE_SHARE: 'subscribe.share',
MANAGE_WORKFLOW: 'manage.workflow',
@@ -81,6 +82,14 @@ export const USER_PERMISSION_FEATURES: UserPermissionFeatureOption[] = [
icon: 'mdi-television',
path: '/subscribe/tv',
},
{
key: PERMISSION_FEATURE.SUBSCRIBE_MUSIC,
permission: 'subscribe',
titleKey: 'dialog.userAddEdit.permissions.features.musicSubscribe',
descriptionKey: 'dialog.userAddEdit.permissions.featureDescriptions.musicSubscribe',
icon: 'mdi-music-note',
path: '/subscribe/music',
},
{
key: PERMISSION_FEATURE.SUBSCRIBE_CALENDAR,
permission: 'subscribe',
@@ -155,7 +164,9 @@ function isRecord(value: unknown): value is Record<string, unknown> {
}
/** 规整用户权限数据,兼容没有 features 字段的历史用户。 */
export function normalizeUserPermissions(permissions: Partial<UserPermissions> | null | undefined = {}): UserPermissions {
export function normalizeUserPermissions(
permissions: Partial<UserPermissions> | null | undefined = {},
): UserPermissions {
const permissionData = permissions ?? {}
const rawFeatures = isRecord(permissionData.features) ? permissionData.features : {}
const features = Object.fromEntries(
@@ -173,7 +184,10 @@ export function normalizeUserPermissions(permissions: Partial<UserPermissions> |
}
/** 构造权限检查上下文,统一超级管理员标记、分类权限与功能权限字段。 */
export function buildUserPermissionContext(isSuperuser: boolean, permissions: Partial<UserPermissions> = {}): UserPermissionContext {
export function buildUserPermissionContext(
isSuperuser: boolean,
permissions: Partial<UserPermissions> = {},
): UserPermissionContext {
return {
is_superuser: isSuperuser,
...normalizeUserPermissions(permissions),
+6
View File
@@ -42,6 +42,12 @@ export function createBuiltInRecommendSources(t: Translate): RecommendViewSource
title: t('recommend.anilistPopularThisSeason'),
type: t('recommend.categoryAnime'),
},
{
apipath: 'recommend/music_weekly',
linkurl: '/browse/recommend/music_weekly?title=' + t('recommend.listenBrainzWeekly'),
title: t('recommend.listenBrainzWeekly'),
type: t('recommend.categoryMusic'),
},
{
apipath: 'recommend/tmdb_movies',
linkurl: '/browse/recommend/tmdb_movies?title=' + t('recommend.tmdbHotMovies'),