diff --git a/src/api/subsonic.ts b/src/api/subsonic.ts index 8a22a699..47437720 100644 --- a/src/api/subsonic.ts +++ b/src/api/subsonic.ts @@ -71,6 +71,8 @@ export interface SubsonicAlbum { created?: string; /** Present on some servers (e.g. OpenSubsonic) for album-level rating. */ userRating?: number; + /** OpenSubsonic: true when the album is tagged as a compilation. */ + isCompilation?: boolean; } /** OpenSubsonic `artists` / `albumArtists` entries on a child song (may include `userRating`). */ diff --git a/src/locales/de.ts b/src/locales/de.ts index 44ac6989..c15c4e42 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -290,6 +290,12 @@ export const deTranslation = { yearTo: 'Bis', yearFilterClear: 'Jahresfilter zurücksetzen', yearFilterLabel: 'Jahr', + compilationLabel: 'Sampler', + compilationOnly: 'Nur Sampler', + compilationHide: 'Ohne Sampler', + compilationTooltipAll: 'Alle Alben · klicken: nur Sampler', + compilationTooltipOnly: 'Nur Sampler · klicken: Sampler ausblenden', + compilationTooltipHide: 'Sampler ausgeblendet · klicken: alle zeigen', select: 'Mehrfachauswahl', startSelect: 'Mehrfachauswahl aktivieren', cancelSelect: 'Abbrechen', diff --git a/src/locales/en.ts b/src/locales/en.ts index 305a5895..ceee2906 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -291,6 +291,12 @@ export const enTranslation = { yearTo: 'To', yearFilterClear: 'Clear year filter', yearFilterLabel: 'Year', + compilationLabel: 'Compilations', + compilationOnly: 'Only compilations', + compilationHide: 'Hide compilations', + compilationTooltipAll: 'All albums · click: only compilations', + compilationTooltipOnly: 'Only compilations · click: hide compilations', + compilationTooltipHide: 'Compilations hidden · click: show all', select: 'Multi-select', startSelect: 'Enable multi-select', cancelSelect: 'Cancel', diff --git a/src/locales/es.ts b/src/locales/es.ts index 927837fd..779187d4 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -291,6 +291,12 @@ export const esTranslation = { yearTo: 'Hasta', yearFilterClear: 'Limpiar filtro de año', yearFilterLabel: 'Año', + compilationLabel: 'Recopilatorios', + compilationOnly: 'Solo recopilatorios', + compilationHide: 'Ocultar recopilatorios', + compilationTooltipAll: 'Todos los álbumes · clic: solo recopilatorios', + compilationTooltipOnly: 'Solo recopilatorios · clic: ocultar recopilatorios', + compilationTooltipHide: 'Recopilatorios ocultos · clic: mostrar todo', select: 'Selección múltiple', startSelect: 'Activar selección múltiple', cancelSelect: 'Cancelar', diff --git a/src/locales/fr.ts b/src/locales/fr.ts index 28e4c256..5f271128 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -290,6 +290,12 @@ export const frTranslation = { yearTo: 'À', yearFilterClear: 'Effacer le filtre année', yearFilterLabel: 'Année', + compilationLabel: 'Compilations', + compilationOnly: 'Uniquement compilations', + compilationHide: 'Masquer compilations', + compilationTooltipAll: 'Tous les albums · clic : uniquement compilations', + compilationTooltipOnly: 'Uniquement compilations · clic : masquer compilations', + compilationTooltipHide: 'Compilations masquées · clic : tout afficher', select: 'Sélection multiple', startSelect: 'Activer la sélection multiple', cancelSelect: 'Annuler', diff --git a/src/locales/nb.ts b/src/locales/nb.ts index 605aa327..78f46604 100644 --- a/src/locales/nb.ts +++ b/src/locales/nb.ts @@ -290,6 +290,12 @@ export const nbTranslation = { yearTo: 'Til', yearFilterClear: 'Tøm år filteret', yearFilterLabel: 'År', + compilationLabel: 'Samleplater', + compilationOnly: 'Kun samleplater', + compilationHide: 'Skjul samleplater', + compilationTooltipAll: 'Alle album · klikk: kun samleplater', + compilationTooltipOnly: 'Kun samleplater · klikk: skjul samleplater', + compilationTooltipHide: 'Samleplater skjult · klikk: vis alle', select: 'Multivalg', startSelect: 'Aktiver multivalg', cancelSelect: 'Avbryt', diff --git a/src/locales/nl.ts b/src/locales/nl.ts index abddd629..3545c95c 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -289,6 +289,12 @@ export const nlTranslation = { yearTo: 'Tot', yearFilterClear: 'Jaarfilter wissen', yearFilterLabel: 'Jaar', + compilationLabel: 'Compilaties', + compilationOnly: 'Alleen compilaties', + compilationHide: 'Compilaties verbergen', + compilationTooltipAll: 'Alle albums · klik: alleen compilaties', + compilationTooltipOnly: 'Alleen compilaties · klik: compilaties verbergen', + compilationTooltipHide: 'Compilaties verborgen · klik: toon alles', select: 'Meervoudige selectie', startSelect: 'Meervoudige selectie inschakelen', cancelSelect: 'Annuleren', diff --git a/src/locales/ru.ts b/src/locales/ru.ts index ad1ddeac..bddb7ed0 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -301,6 +301,12 @@ export const ruTranslation = { yearTo: 'По', yearFilterClear: 'Сбросить год', yearFilterLabel: 'Год', + compilationLabel: 'Сборники', + compilationOnly: 'Только сборники', + compilationHide: 'Скрыть сборники', + compilationTooltipAll: 'Все альбомы · клик: только сборники', + compilationTooltipOnly: 'Только сборники · клик: скрыть сборники', + compilationTooltipHide: 'Сборники скрыты · клик: показать всё', select: 'Множественный выбор', startSelect: 'Включить множественный выбор', cancelSelect: 'Отмена', diff --git a/src/locales/zh.ts b/src/locales/zh.ts index edadac0f..dfc4c417 100644 --- a/src/locales/zh.ts +++ b/src/locales/zh.ts @@ -289,6 +289,12 @@ export const zhTranslation = { yearTo: '到', yearFilterClear: '清除年份筛选', yearFilterLabel: '年份', + compilationLabel: '合辑', + compilationOnly: '仅合辑', + compilationHide: '隐藏合辑', + compilationTooltipAll: '所有专辑 · 点击:仅合辑', + compilationTooltipOnly: '仅合辑 · 点击:隐藏合辑', + compilationTooltipHide: '已隐藏合辑 · 点击:显示全部', select: '多选', startSelect: '启用多选', cancelSelect: '取消', diff --git a/src/pages/Albums.tsx b/src/pages/Albums.tsx index 15b2b678..afc9d1bc 100644 --- a/src/pages/Albums.tsx +++ b/src/pages/Albums.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useCallback, useRef } from 'react'; +import React, { useEffect, useState, useCallback, useRef, useMemo } from 'react'; import AlbumCard from '../components/AlbumCard'; import GenreFilterBar from '../components/GenreFilterBar'; import { getAlbumList, getAlbumsByGenre, getAlbum, SubsonicAlbum, buildDownloadUrl } from '../api/subsonic'; @@ -11,9 +11,10 @@ import { invoke } from '@tauri-apps/api/core'; import { join } from '@tauri-apps/api/path'; import { showToast } from '../utils/toast'; import { useZipDownloadStore } from '../store/zipDownloadStore'; -import { X, CheckSquare2, Download, HardDriveDownload, ListMusic } from 'lucide-react'; +import { X, CheckSquare2, Download, HardDriveDownload, ListMusic, Disc3 } from 'lucide-react'; type SortType = 'alphabeticalByName' | 'alphabeticalByArtist'; +type CompFilter = 'all' | 'only' | 'hide'; const PAGE_SIZE = 30; const CURRENT_YEAR = new Date().getFullYear(); @@ -44,6 +45,7 @@ export default function Albums() { const [selectedGenres, setSelectedGenres] = useState([]); const [yearFrom, setYearFrom] = useState(''); const [yearTo, setYearTo] = useState(''); + const [compFilter, setCompFilter] = useState('all'); const observerTarget = useRef(null); // ── Multi-selection ────────────────────────────────────────────────────── @@ -68,9 +70,19 @@ export default function Albums() { setSelectedIds(new Set()); }; - const selectedAlbums = albums.filter(a => selectedIds.has(a.id)); + const visibleAlbums = useMemo(() => { + if (compFilter === 'all') return albums; + if (compFilter === 'only') return albums.filter(a => a.isCompilation); + return albums.filter(a => !a.isCompilation); + }, [albums, compFilter]); + + const selectedAlbums = visibleAlbums.filter(a => selectedIds.has(a.id)); const openContextMenu = usePlayerStore(state => state.openContextMenu); + const cycleCompFilter = () => { + setCompFilter(v => v === 'all' ? 'only' : v === 'only' ? 'hide' : 'all'); + }; + const handleDownloadZips = async () => { if (selectedAlbums.length === 0) return; const folder = auth.downloadFolder || await requestDownloadFolder(); @@ -256,6 +268,26 @@ export default function Albums() { + + )} @@ -279,7 +311,7 @@ export default function Albums() { ) : ( <>
- {albums.map(a => ( + {visibleAlbums.map(a => (