Skip to content

Commit

Permalink
add file/folder support
Browse files Browse the repository at this point in the history
  • Loading branch information
tamland committed Oct 15, 2023
1 parent f3e1a1e commit ae7250d
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/app/SidebarNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
<Icon icon="radio" /> Radio
</router-link>

<router-link class="nav-link" :to="{name: 'files'}">
<Icon icon="files" /> Files
</router-link>

<PlaylistNav />
</nav>
</template>
Expand Down
118 changes: 118 additions & 0 deletions src/library/file/Files.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-2">
<h1 class="mb-0 mr-2 text-truncate">
Files
</h1>
</div>
<ContentLoader v-slot :loading="item === null">
<BaseTable>
<BaseTableHead />
<tbody class="text-break">
<tr v-if="path" @click="openParent">
<td>
<Icon icon="folder" />
</td>
<td colspan="2">
..
</td>
</tr>
<tr v-for="dir in item.directories" :key="dir.id" @click="openDirectory(dir.id)">
<td>
<Icon icon="folder" />
</td>
<td colspan="2">
{{ dir.name }}
</td>
</tr>
<tr
v-for="track in item.tracks" :key="track.id"
:class="{'active': track.id === playingTrackId}"
@click="playTrack(track)"
>
<CellTrackNumber :active="track.id === playingTrackId && isPlaying" :value="track.track" />
<CellTitle :track="track" />
<CellActions :track="track" />
</tr>
</tbody>
</BaseTable>
</ContentLoader>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { Directory, Track } from '@/shared/api'
import BaseTable from '@/library/track/BaseTable.vue'
import BaseTableHead from '@/library/track/BaseTableHead.vue'
import CellTrackNumber from '@/library/track/CellTrackNumber.vue'
import CellTitle from '@/library/track/CellTitle.vue'
import CellActions from '@/library/track/CellActions.vue'
import { orderBy } from 'lodash-es'
export default defineComponent({
components: {
BaseTable,
BaseTableHead,
CellTrackNumber,
CellTitle,
CellActions
},
props: {
path: { type: String, default: '' }
},
data() {
return {
item: null as null | Directory,
}
},
computed: {
isPlaying(): boolean {
return this.$store.getters['player/isPlaying']
},
playingTrackId(): any {
return this.$store.getters['player/trackId']
},
},
watch: {
path: {
immediate: true,
async handler(value: string) {
this.item = null
this.item = await this.$api.getDirectory(value)
this.item.tracks = orderBy(this.item.tracks, ['track', 'title'])
this.item.directories = orderBy(this.item.directories, 'name')
}
}
},
methods: {
async playNow() {
return this.$store.dispatch('player/playNow', {
tracks: this.item!.tracks,

Check warning on line 90 in src/library/file/Files.vue

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
})
},
async shuffleNow() {
return this.$store.dispatch('player/shuffleNow', {
tracks: this.item!.tracks,

Check warning on line 95 in src/library/file/Files.vue

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
})
},
async playTrack(track: Track) {
if (track.id === this.playingTrackId) {
return this.$store.dispatch('player/playPause')
}
const index = this.item!.tracks!.findIndex((x: any) => x.id === track.id)

Check warning on line 102 in src/library/file/Files.vue

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion

Check warning on line 102 in src/library/file/Files.vue

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
return this.$store.dispatch('player/playTrackList', {
index,
tracks: this.item!.tracks,
})
},
openDirectory(id: string) {
const path = this.path === '' ? id : [this.path, id].join('/')
this.$router.push({ path: `/files/${path}` })
},
openParent() {
const path = this.path.split('/').slice(0, -1).join('/')
this.$router.push({ path: `/files/${path}` })
},
}
})
</script>
59 changes: 59 additions & 0 deletions src/shared/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -366,6 +376,55 @@ export class API {
return this.fetch('rest/deletePodcastChannel', { id })
}

async getDirectory(path: string): Promise<Directory> {
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<void> {
return this.fetch('rest/startScan')
}
Expand Down
4 changes: 4 additions & 0 deletions src/shared/components/Icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions src/shared/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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?',
Expand Down

0 comments on commit ae7250d

Please sign in to comment.