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,6 +1,7 @@
import { IpcInvokeHandlers, IpcToMainEmitHandlers } from '../src-shared/interfaces/ipc.interface.js'
import { download } from './ipc/DownloadHandler.ipc.js'
import { scanIssues } from './ipc/issue-scan/IssueScanHandler.ipc.js'
import { addChart, getChartsBySearchTerm, removeAllCharts, removeChart, removeCharts } from './ipc/LibraryHandler.ipc.js'
import { getSettings, setSettings } from './ipc/SettingsHandler.ipc.js'
import { downloadUpdate, getCurrentVersion, getUpdateAvailable, quitAndInstall, retryUpdate } from './ipc/UpdateHandler.ipc.js'
import { getPlatform, getThemeColors, isMaximized, maximize, minimize, openUrl, quit, restore, showFile, showFolder, showOpenDialog, toggleDevTools } from './ipc/UtilHandlers.ipc.js'
@@ -14,6 +15,10 @@ export function getIpcInvokeHandlers(): IpcInvokeHandlers {
isMaximized,
showOpenDialog,
getThemeColors,
addChart,
removeChart,
removeCharts,
getChartsBySearchTerm,
}
}
@@ -33,5 +38,6 @@ export function getIpcToMainEmitHandlers(): IpcToMainEmitHandlers {
showFile,
showFolder,
scanIssues,
removeAllCharts,
}
}

View File

@@ -0,0 +1,18 @@
import { DataSource } from 'typeorm'
import { Chart } from './entities/Chart.js'
import { Init1743124434920 } from './migrations/1743124434920-init.js'
const migrations = [Init1743124434920]
const entities = [Chart]
export const dataSource = new DataSource({
type: "sqlite",
database: "library.sqlite",
entities: entities,
// Configure migrations to use a folder that contains your migration files:
migrations: migrations,
// Keep synchronize off when using migrations in production
synchronize: false,
logging: true,
migrationsRun: true,
})

View File

@@ -0,0 +1,125 @@
import { ChartData } from 'src-shared/interfaces/search.interface.js'
import { dataSource } from './dataSource.js'
import { Chart } from './entities/Chart.js'
import { Like } from 'typeorm'
export class DatabaseService {
async insertChart(chartData: ChartData): Promise<ChartData> {
try {
if (!dataSource.isInitialized) {
await dataSource.initialize()
}
const chartRepository = dataSource.getRepository(Chart)
// if one already exist dont create
const existingChart = await chartRepository.findOneBy({ md5: chartData.md5 })
if (existingChart) {
return existingChart as unknown as ChartData
}
const newChart = chartRepository.create({
name: chartData.name!,
album: chartData.album!,
artist: chartData.artist!,
genre: chartData.genre!,
year: chartData.year!,
charter: chartData.charter!,
md5: chartData.md5,
hasVideoBackground: chartData.hasVideoBackground,
})
return await chartRepository.save(newChart) as unknown as ChartData
} catch (error) {
console.error('Error inserting chart:', error)
throw error
}
}
async removeChart(md5: string): Promise<void> {
try {
if (!dataSource.isInitialized) {
await dataSource.initialize()
}
const chartRepository = dataSource.getRepository(Chart)
await chartRepository.delete({ md5 })
} catch (error) {
console.error('Error removing chart:', error)
throw error
}
}
async removeCharts(charts: ChartData[]): Promise<void> {
try {
if (!dataSource.isInitialized) {
await dataSource.initialize()
}
const chartRepository = dataSource.getRepository(Chart)
// delete the array of charts provided using querybulilder
charts.forEach(async chart => {
console.log('removing chart:', chart.name)
await chartRepository.delete({ md5: chart.md5 })
})
} catch (error) {
console.error('Error removing charts:', error)
throw error
}
}
async getChartsBySearchTerm(searchTerm?: string): Promise<ChartData[]> {
try {
if (!dataSource.isInitialized) {
await dataSource.initialize()
}
const chartRepository = dataSource.getRepository(Chart)
let charts: Chart[]
if (searchTerm) {
const likeSearchTerm = `%${searchTerm}%`
charts = await chartRepository.find({
where: [
{ name: Like(likeSearchTerm) },
{ album: Like(likeSearchTerm) },
{ artist: Like(likeSearchTerm) },
{ genre: Like(likeSearchTerm) },
{ year: Like(likeSearchTerm) },
{ charter: Like(likeSearchTerm) },
],
})
} else {
charts = await chartRepository.find()
}
return charts as unknown as ChartData[]
} catch (error) {
console.error('Error fetching charts by search term:', error)
throw error
}
}
async removeAllCharts(): Promise<void> {
try {
if (!dataSource.isInitialized) {
await dataSource.initialize()
}
const chartRepository = dataSource.getRepository(Chart)
await chartRepository.clear()
} catch (error) {
console.error('Error removing all charts:', error)
throw error
}
}
}
export const databaseService = new DatabaseService()

View File

@@ -0,0 +1,31 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@Entity()
export class Chart {
@PrimaryGeneratedColumn('uuid')
id: string
@Column()
md5: string
@Column()
hasVideoBackground: boolean
@Column()
charter: string
@Column()
name: string
@Column()
artist: string
@Column()
album: string
@Column()
genre: string
@Column()
year: string
}

View File

@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class Init1743124434920 implements MigrationInterface {
name = 'Init1743124434920'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "chart" ("id" varchar PRIMARY KEY NOT NULL, "md5" varchar NOT NULL, "hasVideoBackground" boolean NOT NULL, "charter" varchar NOT NULL, "name" varchar NOT NULL, "artist" varchar NOT NULL, "album" varchar NOT NULL, "genre" varchar NOT NULL, "year" varchar NOT NULL)`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "chart"`);
}
}

View File

@@ -0,0 +1,10 @@
## Migrations
In order to create a new migration, there is a some steps to go through.
1. Run ``npm run migration:add --name <migration name>`` This currently work for Windows machines. If using Linux or Mac run this instead ``npx typeorm-ts-node-esm migration:generate ./src-electron/database/migrations/<migration name> -d ./src-electron/database/dataSource.ts``
2. Go to ``./src-electron/database/dataSource.ts`` and add the newly
generated migration to the migrations array and entity variables. In that way it will automatically apply the latest changes to the database on startup.
## The database
A Sqlite database file is automatically created on startup named Library, it will be placed in the same directory as the executable.

View File

@@ -0,0 +1,47 @@
import { databaseService } from '../database/databaseService.js'
import { ChartData } from 'src-shared/interfaces/search.interface.js'
export async function addChart(chartData: ChartData): Promise<ChartData> {
try {
return await databaseService.insertChart(chartData)
} catch (error) {
console.error('Error in addChartHandler:', error)
throw error
}
}
export async function removeChart(md5: string): Promise<void> {
try {
await databaseService.removeChart(md5)
} catch (error) {
console.error('Error in removeChartHandler:', error)
throw error
}
}
export async function removeCharts(charts: ChartData[]): Promise<void> {
try {
await databaseService.removeCharts(charts)
} catch (error) {
console.error('Error in removeChartsHandler:', error)
throw error
}
}
export async function removeAllCharts(): Promise<void> {
try {
await databaseService.removeAllCharts()
} catch (error) {
console.error('Error in removeAllChartsHandler:', error)
throw error
}
}
export async function getChartsBySearchTerm(searchTerm?: string): Promise<ChartData[]> {
try {
return await databaseService.getChartsBySearchTerm(searchTerm)
} catch (error) {
console.error('Error in getChartsHandler:', error)
throw error
}
}

View File

@@ -3,12 +3,13 @@ import electronUnhandled from 'electron-unhandled'
import windowStateKeeper from 'electron-window-state'
import * as path from 'path'
import * as url from 'url'
import "reflect-metadata"
import { IpcFromMainEmitEvents } from '../src-shared/interfaces/ipc.interface.js'
import { dataPath } from '../src-shared/Paths.js'
import { settings } from './ipc/SettingsHandler.ipc.js'
import { retryUpdate } from './ipc/UpdateHandler.ipc.js'
import { getIpcInvokeHandlers, getIpcToMainEmitHandlers } from './IpcHandler.js'
import { dataSource } from './database/dataSource.js'
electronUnhandled({ showDialog: true, logger: err => console.log('Error: Unhandled Rejection:', err) })
@@ -26,6 +27,14 @@ app.on('ready', async () => {
if (!isDevBuild) {
retryUpdate()
}
// Initialize the database
dataSource.initialize().then(() => {
console.log('Database initialized')
}
).catch(error => {
console.error('Error initializing database:', error)
})
})
/**

View File

@@ -25,6 +25,10 @@ const electronApi: ContextBridgeApi = {
isMaximized: getInvoker('isMaximized'),
showOpenDialog: getInvoker('showOpenDialog'),
getThemeColors: getInvoker('getThemeColors'),
addChart: getInvoker('addChart'),
removeChart: getInvoker('removeChart'),
removeCharts: getInvoker('removeCharts'),
getChartsBySearchTerm: getInvoker('getChartsBySearchTerm'),
},
emit: {
download: getEmitter('download'),
@@ -41,6 +45,7 @@ const electronApi: ContextBridgeApi = {
showFolder: getEmitter('showFolder'),
showFile: getEmitter('showFile'),
scanIssues: getEmitter('scanIssues'),
removeAllCharts: getEmitter('removeAllCharts'),
},
on: {
errorLog: getListenerAdder('errorLog'),