Add database for library

This commit is contained in:
Myx
2025-03-28 04:46:29 +01:00
committed by Myx
parent c0cfca39a2
commit 35fd50c728
17 changed files with 382 additions and 80 deletions

View File

@@ -1,22 +1,22 @@
<div class="border-t border-t-neutral p-2 flex gap-2 items-center max-w-full justify-between">
<div class="flex items-center gap-[8px]">
{{ (this.libraryService.tracks$ | async)?.length ?? 0 }} Songs
<button class="btn btn-sm btn-primary" (click)="fileInput.click()">Import Setlist</button>
@if ((this.libraryService.selectedSongs$ | async)!.length > 0) {
<button class="btn btn-sm btn-primary" (click)="exportSelected()">Export Selected</button>
<button class="btn btn-sm btn-primary" (click)="exportSelected()">Export Selected Setlist</button>
} @else {
<button class="btn btn-sm btn-primary" (click)="exportPlaylist()">Export</button>
<button class="btn btn-sm btn-primary" (click)="exportLibrary()">Export Setlist</button>
}
<button class="btn btn-sm btn-primary" (click)="importPlaylist()">Import</button>
<input type="file" #fileInput accept=".library" class="hidden" (change)="onFileSelected($event)" />
@if ((this.libraryService.selectedSongs$ | async)!.length === 0) {
<button type="button" class="btn btn-sm min-w-[108px] hover:btn-error" (click)="this.libraryService.clearPlaylist()">
<button type="button" class="btn btn-sm min-w-[108px] hover:btn-error" (click)="this.libraryService.clearLibrary()">
<i class="bi bi-trash"></i>
Delete all
</button>
} @else {
<button type="button" class="btn btn-sm min-w-[108px] hover:btn-error" (click)="this.libraryService.removeFromPlaylist()">
<button type="button" class="btn btn-sm min-w-[108px] hover:btn-error" (click)="this.libraryService.removeFromLibrary()">
<i class="bi bi-trash"></i>
Delete selected
</button>

View File

@@ -1,6 +1,7 @@
import { Component, ElementRef, ViewChild } from '@angular/core'
import { DownloadService } from '../../../core/services/download.service'
import { LibraryService } from 'src-angular/app/core/services/library.service'
import { ChartData } from 'src-shared/interfaces/search.interface'
@Component({
selector: 'app-library-bar',
@@ -12,7 +13,7 @@ export class LibraryBarComponent {
constructor(public libraryService: LibraryService, public downloadService: DownloadService) { }
exportPlaylist() {
exportLibrary() {
this.libraryService.storeLibrary()
}
@@ -20,29 +21,24 @@ export class LibraryBarComponent {
this.libraryService.storeSelectedSongs()
}
importPlaylist() {
this.libraryfileInput.nativeElement.click()
}
onFileSelected(event: Event) {
async onFileSelected(event: Event) {
const input = event.target as HTMLInputElement
if (input.files && input.files.length > 0) {
const file = input.files[0]
const reader = new FileReader()
reader.onload = () => {
try {
const importedTracks = JSON.parse(reader.result as string)
if (Array.isArray(importedTracks)) {
this.libraryService.downloadLibrary(importedTracks)
} else {
console.error('Invalid file format')
}
} catch (error) {
console.error('Error parsing file:', error)
}
}
reader.readAsText(file)
if (!input.files?.length)
return
const file = input.files[0]
try {
const fileContent = await file.text()
const json = JSON.parse(fileContent)
const chartData = json as ChartData[]
this.libraryService.downloadLibrary(chartData)
} catch (error) {
console.error('Error reading or parsing the file:', error)
}
this.libraryfileInput.nativeElement.value = ''
}
}

View File

@@ -7,7 +7,7 @@
(keyup)="filterSongs()" />
</div>
<div
*ngIf="filteredSongs.length > 0"
*ngIf="songs.length > 0"
class="basis-2/3 flex-1 overflow-y-auto scrollbar scrollbar-w-2 scrollbar-h-2 scrollbar-track-base-300 scrollbar-thumb-neutral scrollbar-thumb-rounded-full h-[calc(100vh-164px)]">
<table id="resultTable" class="table table-zebra table-pin-rows" [class.table-xs]="settingsService.isCompactTable">
<thead>
@@ -36,7 +36,7 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let song of filteredSongs; trackBy: trackByFn">
<tr *ngFor="let song of songs; trackBy: trackByFn">
<td>
<input
type="checkbox"
@@ -55,13 +55,6 @@
</table>
</div>
<div
class="align-center h-[calc(100vh-169.5px)]"
*ngIf="songs.length > 1 && filteredSongs.length < 1"
style="display: flex; justify-content: center; align-items: center">
<p class="text-center" style="font-size: 1.5rem">No songs found!</p>
</div>
<div class="flex align-center justify-center items-center h-[calc(100vh-169.5px)]" *ngIf="songs.length < 1 && filteredSongs.length === 0">
<div class="flex align-center justify-center items-center h-[calc(100vh-169.5px)]" *ngIf="songs.length < 1">
<p class="text-center" style="font-size: 1.5rem">No songs added!</p>
</div>

View File

@@ -16,7 +16,6 @@ export class LibraryTableComponent implements OnInit, OnDestroy {
songs: ChartData[] = []
sortDirection: 'asc' | 'desc' = 'asc'
sortColumn: SortColumn = null
filteredSongs: ChartData[] = []
searchTerm: string = ''
allRowsSelected: boolean = false
subscriptions: Subscription[] = []
@@ -45,10 +44,9 @@ export class LibraryTableComponent implements OnInit, OnDestroy {
this.libraryService.tracks$
.subscribe(tracks => {
this.songs = tracks
this.filterSongs()
})
)
this.filteredSongs = [...this.songs]
this.subscriptions.push(
this.libraryService.selectedSongs$
.subscribe(songs =>
@@ -58,20 +56,11 @@ export class LibraryTableComponent implements OnInit, OnDestroy {
}
filterSongs(): void {
const term = this.searchTerm.toLowerCase()
this.filteredSongs = this.songs.filter(
song =>
song.name?.toLowerCase().includes(term) ||
song.artist?.toLowerCase().includes(term) ||
song.album?.toLowerCase().includes(term) ||
song.genre?.toLowerCase().includes(term) ||
song.year?.toLowerCase().includes(term) ||
song.charter?.toLowerCase().includes(term)
)
this.libraryService.getChartsBySearchTerm(this.searchTerm)
}
onColClicked(column: SortColumn) {
if (this.filteredSongs.length === 0) { return }
if (this.songs.length === 0) { return }
if (this.sortColumn !== column) {
this.sortColumn = column
@@ -84,7 +73,7 @@ export class LibraryTableComponent implements OnInit, OnDestroy {
}
if (this.sortColumn) {
this.filteredSongs.sort((a, b) => {
this.songs.sort((a, b) => {
const valueA = a[this.sortColumn! as keyof ChartData]
const valueB = b[this.sortColumn! as keyof ChartData]
@@ -116,7 +105,7 @@ export class LibraryTableComponent implements OnInit, OnDestroy {
this.allRowsSelected = !this.allRowsSelected
if (this.allRowsSelected) {
this.filteredSongs.forEach(song => this.libraryService.addToSelectedSongs(song))
this.songs.forEach(song => this.libraryService.addToSelectedSongs(song))
} else {
this.libraryService.clearSelectedSongs()
}

View File

@@ -2,8 +2,7 @@ import { Injectable, Injector } from '@angular/core'
import { BehaviorSubject } from 'rxjs'
import { ChartData } from 'src-shared/interfaces/search.interface'
import { DownloadService } from './download.service'
const LibraryStorageIdentifyer: string = "library"
import { StorageService } from './storage.service'
@Injectable({
providedIn: 'root',
@@ -17,42 +16,39 @@ export class LibraryService {
private _downloadService: DownloadService | null = null
constructor(private injector: Injector) {
const library = localStorage.getItem(LibraryStorageIdentifyer)
if (library) {
this._tracks.next(JSON.parse(library))
}
constructor(private injector: Injector, private storageService: StorageService) {
this.storageService.getChartsBySearchTerm().then(library => {
if (library) {
this._tracks.next(library)
}
})
}
private get downloadService(): DownloadService {
if (!this._downloadService) {
this._downloadService = this.injector.get(DownloadService)
}
return this._downloadService
}
getPlaylist() {
return this._tracks.value
return this._downloadService
}
libraryAdd(chart: ChartData) {
const updatedTracks = [...this._tracks.value, chart]
this._tracks.next(updatedTracks)
localStorage.setItem(LibraryStorageIdentifyer, JSON.stringify(updatedTracks))
this.storageService.addChart(chart)
}
downloadLibrary(songs: ChartData[]) {
songs.forEach(track => {
if (!this._tracks.value.includes(track)) {
this.downloadService.addDownload(track)
}
this.downloadService.addDownload(track)
})
}
storeLibrary() {
const fakeLink = document.createElement('a')
const file = new Blob([JSON.stringify(this._tracks.value)], { type: 'application/json' })
fakeLink.href = URL.createObjectURL(file)
fakeLink.download = 'songs.library'
fakeLink.click()
@@ -61,6 +57,7 @@ export class LibraryService {
storeSelectedSongs() {
const fakeLink = document.createElement('a')
const file = new Blob([JSON.stringify(this._selectedSongs.value)], { type: 'application/json' })
fakeLink.href = URL.createObjectURL(file)
fakeLink.download = 'selected.library'
fakeLink.click()
@@ -80,16 +77,28 @@ export class LibraryService {
this._selectedSongs.next([])
}
removeFromPlaylist() {
this._selectedSongs.value.forEach(selectedSong => {
const updatedTracks = this._tracks.value.filter(track => track !== selectedSong)
removeFromLibrary() {
this._selectedSongs.value.forEach((selectedSong: ChartData) => {
const updatedTracks = this._tracks.value.filter(track => track !== selectedSong) as ChartData[]
this._tracks.next(updatedTracks)
localStorage.setItem(LibraryStorageIdentifyer, JSON.stringify(updatedTracks))
this.storageService.removeChart(selectedSong.md5)
})
this.clearSelectedSongs()
}
clearPlaylist() {
this._tracks.next([])
localStorage.removeItem(LibraryStorageIdentifyer)
async clearLibrary() {
this.storageService.removeAllCharts()
this.clearSelectedSongs()
const library = await this.storageService.getChartsBySearchTerm()
this._tracks.next(library)
}
async getChartsBySearchTerm(searchTerm?: string): Promise<ChartData[]> {
const library = await this.storageService.getChartsBySearchTerm(searchTerm)
this._tracks.next(library)
return library
}
}

View File

@@ -0,0 +1,27 @@
import { Injectable } from '@angular/core'
import { ChartData } from 'src-shared/interfaces/search.interface'
@Injectable({
providedIn: 'root',
})
export class StorageService {
async addChart(chartData: ChartData): Promise<ChartData> {
return window.electron.invoke.addChart(chartData)
}
async removeChart(md5: string): Promise<void> {
return window.electron.invoke.removeChart(md5)
}
async removeCharts(charts: ChartData[]): Promise<void> {
return window.electron.invoke.removeCharts(charts)
}
async getChartsBySearchTerm(searchTerm?: string): Promise<ChartData[]> {
return window.electron.invoke.getChartsBySearchTerm(searchTerm)
}
async removeAllCharts(): Promise<void> {
return window.electron.emit.removeAllCharts()
}
}