diff --git a/main/background.ts b/main/background.ts index d1da811..120837c 100644 --- a/main/background.ts +++ b/main/background.ts @@ -9,6 +9,7 @@ import { createPlaylist, getAlbumWithSongs, getAlbums, + getLibraryItems, getLibraryStats, getPlaylistWithSongs, getPlaylists, @@ -51,7 +52,7 @@ const initializeLibrary = async () => { await initializeData(settings.musicFolder); } } catch (error) { - console.error('Error initializing library:', error); + console.error("Error initializing library:", error); } }; @@ -113,43 +114,46 @@ const initializeLibrary = async () => { // @hiaaryan: Initialize Discord RPC const client = new Client({ - clientId: "1243707416588320800" + clientId: "1243707416588320800", }); -ipcMain.on("set-rpc-state", async (_, { details, state, seek, duration, cover }) => { - let startTimestamp, endTimestamp; +ipcMain.on( + "set-rpc-state", + async (_, { details, state, seek, duration, cover }) => { + let startTimestamp, endTimestamp; - if (duration && seek) { - const now = Math.ceil(Date.now()); - startTimestamp = now - (seek * 1000); - endTimestamp = now + ((duration - seek) * 1000); - } + if (duration && seek) { + const now = Math.ceil(Date.now()); + startTimestamp = now - seek * 1000; + endTimestamp = now + (duration - seek) * 1000; + } - const setActivity = { - details, - state, - largeImageKey: cover, - instance: false, - type: 2, - startTimestamp: startTimestamp, - endTimestamp: endTimestamp, - buttons: [ - { label: "Support Project", url: "https://github.com/hiaaryan/wora" }, - ] - }; - - if (!client.isConnected) { - try { - await client.login(); - } catch (error) { - console.error('Error logging into Discord:', error); + const setActivity = { + details, + state, + largeImageKey: cover, + instance: false, + type: 2, + startTimestamp: startTimestamp, + endTimestamp: endTimestamp, + buttons: [ + { label: "Support Project", url: "https://github.com/hiaaryan/wora" }, + ], + }; + + if (!client.isConnected) { + try { + await client.login(); + } catch (error) { + console.error("Error logging into Discord:", error); + } } - } - if (client.isConnected) { - client.user.setActivity(setActivity); - } -}); + if (client.isConnected) { + client.user.setActivity(setActivity); + } + }, +); // @hiaaryan: Called to Rescan Library ipcMain.handle("rescanLibrary", async () => { @@ -267,6 +271,11 @@ ipcMain.handle("getRandomLibraryItems", async () => { return libraryItems; }); +ipcMain.handle("getLibraryItems", async () => { + const libraryItems = await getLibraryItems(); + return libraryItems; +}); + ipcMain.handle("updatePlaylist", async (_, data: any) => { const playlist = await updatePlaylist(data); return playlist; @@ -294,7 +303,10 @@ ipcMain.handle("updateSettings", async (_, data: any) => { }); ipcMain.handle("uploadProfilePicture", async (_, file) => { - const uploadsDir = path.join(app.getPath("userData"), "utilities/uploads/profile"); + const uploadsDir = path.join( + app.getPath("userData"), + "utilities/uploads/profile", + ); if (!fs.existsSync(uploadsDir)) { fs.mkdirSync(uploadsDir, { recursive: true }); } @@ -308,7 +320,10 @@ ipcMain.handle("uploadProfilePicture", async (_, file) => { }); ipcMain.handle("uploadPlaylistCover", async (_, file) => { - const uploadsDir = path.join(app.getPath("userData"), "utilities/uploads/playlists"); + const uploadsDir = path.join( + app.getPath("userData"), + "utilities/uploads/playlists", + ); if (!fs.existsSync(uploadsDir)) { fs.mkdirSync(uploadsDir, { recursive: true }); } diff --git a/main/helpers/db/connectDB.ts b/main/helpers/db/connectDB.ts index 3ba191d..00fae6c 100644 --- a/main/helpers/db/connectDB.ts +++ b/main/helpers/db/connectDB.ts @@ -6,16 +6,29 @@ import path from "path"; import { BetterSQLite3Database, drizzle } from "drizzle-orm/better-sqlite3"; import * as schema from "./schema"; import { sqlite } from "./createDB"; -import { app } from 'electron'; +import { app } from "electron"; const db: BetterSQLite3Database = drizzle(sqlite, { schema }); -const APP_DATA = app.getPath('userData'); -const ART_DIR = path.join(APP_DATA, 'utilities/uploads/covers'); +const APP_DATA = app.getPath("userData"); +const ART_DIR = path.join(APP_DATA, "utilities/uploads/covers"); const audioExtensions = [ - ".mp3", ".mpeg", ".opus", ".ogg", ".oga", ".wav", ".aac", - ".caf", ".m4a", ".m4b", ".mp4", ".weba", ".webm", ".dolby", ".flac" + ".mp3", + ".mpeg", + ".opus", + ".ogg", + ".oga", + ".wav", + ".aac", + ".caf", + ".m4a", + ".m4b", + ".mp4", + ".weba", + ".webm", + ".dolby", + ".flac", ]; const imageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"]; @@ -25,7 +38,11 @@ function findFirstImageInDirectory(dir: string): string | null { for (const file of files) { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); - if (stat.isFile() && imageExtensions.includes(path.extname(file).toLowerCase())) { + if ( + stat.isFile() && + imageExtensions.includes(path.extname(file).toLowerCase()) && + path.basename(filePath)[0] !== "." + ) { return filePath; } } @@ -38,9 +55,13 @@ function readFilesRecursively(dir: string): string[] { list.forEach((file) => { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); + console.log(path.basename(filePath)); if (stat && stat.isDirectory()) { results = results.concat(readFilesRecursively(filePath)); - } else if (audioExtensions.includes(path.extname(filePath).toLowerCase())) { + } else if ( + audioExtensions.includes(path.extname(filePath).toLowerCase()) && + path.basename(filePath)[0] !== "." + ) { results.push(filePath); } }); @@ -50,7 +71,9 @@ function readFilesRecursively(dir: string): string[] { export const getLibraryStats = async () => { const songCount = await db.select({ count: sql`count(*)` }).from(songs); const albumCount = await db.select({ count: sql`count(*)` }).from(albums); - const playlistCount = await db.select({ count: sql`count(*)` }).from(playlists); + const playlistCount = await db + .select({ count: sql`count(*)` }) + .from(playlists); return { songs: songCount[0].count, @@ -197,7 +220,10 @@ export const isSongFavorite = async (file: string) => { if (!song) return false; const isFavourite = await db.query.playlistSongs.findFirst({ - where: and(eq(playlistSongs.playlistId, 1), eq(playlistSongs.songId, song.id)), + where: and( + eq(playlistSongs.playlistId, 1), + eq(playlistSongs.songId, song.id), + ), }); return !!isFavourite; @@ -207,7 +233,9 @@ export const addToFavourites = async (songId: number) => { const existingEntry = await db .select() .from(playlistSongs) - .where(and(eq(playlistSongs.playlistId, 1), eq(playlistSongs.songId, songId))); + .where( + and(eq(playlistSongs.playlistId, 1), eq(playlistSongs.songId, songId)), + ); if (!existingEntry[0]) { await db.insert(playlistSongs).values({ @@ -217,7 +245,9 @@ export const addToFavourites = async (songId: number) => { } else { await db .delete(playlistSongs) - .where(and(eq(playlistSongs.playlistId, 1), eq(playlistSongs.songId, songId))); + .where( + and(eq(playlistSongs.playlistId, 1), eq(playlistSongs.songId, songId)), + ); } }; @@ -252,7 +282,10 @@ export const searchDB = async (query: string) => { export const addSongToPlaylist = async (playlistId: number, songId: number) => { const checkIfExists = await db.query.playlistSongs.findFirst({ - where: and(eq(playlistSongs.playlistId, playlistId), eq(playlistSongs.songId, songId)), + where: and( + eq(playlistSongs.playlistId, playlistId), + eq(playlistSongs.songId, songId), + ), }); if (checkIfExists) return false; @@ -265,10 +298,18 @@ export const addSongToPlaylist = async (playlistId: number, songId: number) => { return true; }; -export const removeSongFromPlaylist = async (playlistId: number, songId: number) => { +export const removeSongFromPlaylist = async ( + playlistId: number, + songId: number, +) => { await db .delete(playlistSongs) - .where(and(eq(playlistSongs.playlistId, playlistId), eq(playlistSongs.songId, songId))); + .where( + and( + eq(playlistSongs.playlistId, playlistId), + eq(playlistSongs.songId, songId), + ), + ); return true; }; @@ -292,12 +333,29 @@ export const getRandomLibraryItems = async () => { }; }; +export const getLibraryItems = async () => { + const albumsList = await db + .select() + .from(albums) + .orderBy(sql`RANDOM()`); + + const songsList = await db.query.songs.findMany({ + with: { album: true }, + //orderBy: sql`RANDOM()`, + }); + + return { + albums: albumsList, + songs: songsList, + }; +}; + export const initializeData = async (musicFolder: string) => { const currentFiles = readFilesRecursively(musicFolder); const dbFiles = await db.select().from(songs); const deletedFiles = dbFiles.filter( - (dbFile) => !currentFiles.includes(dbFile.filePath) + (dbFile) => !currentFiles.includes(dbFile.filePath), ); if (deletedFiles.length > 0) { @@ -311,9 +369,16 @@ export const initializeData = async (musicFolder: string) => { for (const file of currentFiles) { const dbFile = dbFiles.find((dbFile) => dbFile.filePath === file); - const metadata = await parseFile(file, { - skipPostHeaders: true, - }); + + let metadata; + + try { + metadata = await parseFile(file, { + skipPostHeaders: true, + }); + } catch (error) { + continue; + } let artPath; @@ -323,7 +388,10 @@ export const initializeData = async (musicFolder: string) => { if (albumImage) { const imageData = fs.readFileSync(albumImage); const imageExt = path.extname(albumImage).slice(1); - const hash = require('crypto').createHash('md5').update(imageData).digest('hex'); + const hash = require("crypto") + .createHash("md5") + .update(imageData) + .digest("hex"); artPath = path.join(ART_DIR, `${hash}.${imageExt}`); if (!fs.existsSync(artPath)) { @@ -334,8 +402,11 @@ export const initializeData = async (musicFolder: string) => { const cover = selectCover(metadata.common.picture); if (cover) { - const hash = require('crypto').createHash('md5').update(cover.data).digest('hex'); - artPath = path.join(ART_DIR, `${hash}.${cover.format.split('/')[1]}`); + const hash = require("crypto") + .createHash("md5") + .update(cover.data) + .digest("hex"); + artPath = path.join(ART_DIR, `${hash}.${cover.format.split("/")[1]}`); if (!fs.existsSync(artPath)) { await fs.promises.mkdir(ART_DIR, { recursive: true }); @@ -370,7 +441,7 @@ export const initializeData = async (musicFolder: string) => { // @hiaaryan: Update Album if Artist or Cover is different if ( album.artist !== - (metadata.common.albumartist || metadata.common.artist) || + (metadata.common.albumartist || metadata.common.artist) || album.year !== metadata.common.year || album.cover !== artPath ) { @@ -451,11 +522,8 @@ export const initializeData = async (musicFolder: string) => { .where(eq(settings.id, 1)); if (existingSettings[0]) { - await db - .update(settings) - .set({ musicFolder }) - .where(eq(settings.id, 1)); + await db.update(settings).set({ musicFolder }).where(eq(settings.id, 1)); } else { await db.insert(settings).values({ musicFolder }); } -}; \ No newline at end of file +}; diff --git a/renderer/components/main/navbar.tsx b/renderer/components/main/navbar.tsx index 87e6808..17aa01e 100644 --- a/renderer/components/main/navbar.tsx +++ b/renderer/components/main/navbar.tsx @@ -143,7 +143,9 @@ const Navbar = () => { - + @@ -151,7 +153,7 @@ const Navbar = () => {

{settings && settings.name ? settings.name : "Wora User"}

-
+
@@ -221,7 +223,6 @@ const Navbar = () => {

Theme: {theme}

-
@@ -230,7 +231,8 @@ const Navbar = () => { + onValueChange={setSearch} + /> {loading && (
@@ -247,22 +249,20 @@ const Navbar = () => { className="text-black dark:text-white" >
- {(item.type === "Playlist" || - item.type === "Album") && ( -
- {item.name} -
- )} + {(item.type === "Playlist" || item.type === "Album") && ( +
+ {item.name} +
+ )}

{item.name} - - ({item.type}) - + ({item.type})

{item.type === "Playlist" diff --git a/renderer/pages/songs.tsx b/renderer/pages/songs.tsx new file mode 100644 index 0000000..4a42c1a --- /dev/null +++ b/renderer/pages/songs.tsx @@ -0,0 +1,54 @@ +import React, { useEffect, useState } from "react"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import AlbumCard from "@/components/ui/album"; +import Songs from "@/components/ui/songs"; + +export default function SongList() { + const [library, setLibrary] = useState([]); + + useEffect(() => { + window.ipc.invoke("getLibraryItems").then((response) => { + setLibrary(response); + }); + }, []); + + return ( +

+
+
+
Songs
+
Your Entire Catalogue.
+
+ {/*library?.albums && library.albums.length > 5 && ( + + +
+ + {library.albums.map((album: any, index: number) => ( + + + + ))} + +
+ +
+ )*/} +
+ +
+
+
+ ); +}