diff --git a/src/app/SidebarNav.vue b/src/app/SidebarNav.vue index 8fdb47d..3e10fc8 100644 --- a/src/app/SidebarNav.vue +++ b/src/app/SidebarNav.vue @@ -47,6 +47,10 @@ Radio + + Files + + diff --git a/src/library/file/Files.vue b/src/library/file/Files.vue new file mode 100644 index 0000000..837e548 --- /dev/null +++ b/src/library/file/Files.vue @@ -0,0 +1,118 @@ + + diff --git a/src/shared/api.ts b/src/shared/api.ts index 65fb020..b494fdf 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -73,6 +73,16 @@ export interface PodcastEpisode { description: string } +export interface Directory { + id: string + name: string + tracks: Track[] + directories: { + id: string + name: string + }[] +} + export interface Playlist { id: string name: string @@ -366,6 +376,55 @@ export class API { return this.fetch('rest/deletePodcastChannel', { id }) } + async getDirectory(path: string): Promise { + const parts = path.split('/') + const musicFolderId = parts[0] + const id = parts.length > 1 ? parts[parts.length - 1] : '' + + if (musicFolderId === '') { + const response = await this.fetch('rest/getMusicFolders') + const items = response?.musicFolders?.musicFolder ?? [] + return { + id: '', + name: '', + tracks: [], + directories: items.map((item: any) => ({ id: `${item?.id}`, name: item?.name, })), + } + } + + if (id === '') { + const response = await this.fetch('rest/getIndexes', { musicFolderId }) + const directories = (response?.indexes?.index ?? []).flatMap((item: any) => [ + ...(item?.artist || []).map((i: any) => ({ id: i.id, name: i.name })) + ]) + const tracks = (response?.indexes?.child ?? []).map(this.normalizeTrack, this) + return { + id: musicFolderId, + name: musicFolderId, + tracks, + directories + } + } + + const response = await this.fetch('rest/getMusicDirectory', { id }) + const items = response?.directory?.child ?? [] + + const directories = items + .filter((item: any) => item.isDir) + .map((item: any) => ({ id: item.id, name: item.title })) + + const tracks = items + .filter((item: any) => !item.isDir && item.type === 'music' && item?.duration > 0) + .map(this.normalizeTrack, this) + + return { + id: response.directory.id, + name: response.directory.name, + tracks, + directories, + } + } + async scan(): Promise { return this.fetch('rest/startScan') } diff --git a/src/shared/components/Icon.vue b/src/shared/components/Icon.vue index e2a1296..d8a4bc2 100644 --- a/src/shared/components/Icon.vue +++ b/src/shared/components/Icon.vue @@ -23,6 +23,8 @@ import iconDiscover from '@iconify-icons/bi/card-text' import iconDownload from '@iconify-icons/bi/download' import iconEdit from '@iconify-icons/bi/pencil-square' + import iconFiles from '@iconify-icons/bi/files' + import iconFolder from '@iconify-icons/bi/folder' import iconHeart from '@iconify-icons/bi/heart' import iconHeartFill from '@iconify-icons/bi/heart-fill' import iconLibrary from '@iconify-icons/bi/collection' @@ -54,6 +56,8 @@ discover: iconDiscover.body, download: iconDownload.body, edit: iconEdit.body, + files: iconFiles.body, + folder: iconFolder.body, heart: iconHeart.body, 'heart-fill': iconHeartFill.body, library: iconLibrary.body, diff --git a/src/shared/router.ts b/src/shared/router.ts index 1c26f09..b444f76 100644 --- a/src/shared/router.ts +++ b/src/shared/router.ts @@ -17,6 +17,7 @@ import PlaylistLibrary from '@/library/playlist/PlaylistLibrary.vue' import SearchResult from '@/library/search/SearchResult.vue' import { AuthService } from '@/auth/service' import ArtistTracks from '@/library/artist/ArtistTracks.vue' +import Files from '@/library/file/Files.vue' export function setupRouter(auth: AuthService) { const router = new Router({ @@ -118,6 +119,12 @@ export function setupRouter(auth: AuthService) { component: PodcastDetails, props: true, }, + { + name: 'files', + path: '/files/:path*', + component: Files, + props: true, + }, { name: 'playlists', path: '/playlists/:sort?',