-
Notifications
You must be signed in to change notification settings - Fork 45
Add database schema, migrations, and song management enhancements #403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Created a new schema file defining tables for palettes, artworks, albums, songs, artists, genres, playlists, and related entities. - Implemented a seeding script to populate the database with initial data for palettes, swatch types, artworks, albums, artists, genres, songs, and listening data. - Added a journal metadata file to track database versioning and changes.
- Updated the `schema.ts` file to introduce new enums for artwork sources and swatch types. - Added new tables: `artists`, `songs`, `artworks`, `palettes`, `play_events`, `seek_events`, `skip_events`, and their respective relationships. - Removed deprecated fields and adjusted primary keys and foreign key constraints for better normalization. - Created a new SQL migration file to reflect the updated schema, including type definitions and table structures. - Ensured all foreign key relationships are properly defined to maintain data integrity.
…ean up unused imports in main.ts and filesystem.ts
- Updated `parseFolderStructuresForSongPaths.ts` to fetch folder structures from the database asynchronously. - Changed logging levels in `logger.ts` from 'verbose' to 'debug'. - Enhanced `artworks.ts` to include new artwork properties and adjusted the artwork storage logic. - Modified `parseSong.ts` to handle bitrate calculations and temporarily set artwork IDs to a default value. - Added SQL schema for new tables and types related to artworks, albums, artists, and genres. - Implemented folder queries in `folders.ts` to retrieve folder structures and all folders from the database. - Generated route tree for the application with updated routes and types.
- Update song path handling in addMusicFromFolderStructures - Introduce linkArtworksToSong function for artwork associations - Add getAllSongs query for retrieving all songs - Implement folder structure saving and retrieval improvements - Refactor song parsing to include folder ID - Extend type definitions for saved folder structures
…ngs for sorting options
…ndling in SongsPage component
- upgraded dotenv from ^16.5.0 to ^17.0.1 - upgraded @types/jest from ^29.5.12 to ^30.0.0 - upgraded @types/node from ^22.13.4 to ^24.0.10 - upgraded electron-vite from ^3.1.0-beta.0 to ^4.0.0 - upgraded jest from ^29.7.0 to ^30.0.4 - upgraded material-symbols from ^0.31.1 to ^0.32.0 - upgraded vite from ^6.1.1 to ^7.0.2
…r song artworks and enhance song retrieval by path
… formatting across multiple files
…rk parsing and query options
…and updated song and artwork tables Home Page 🏠 The home page's load time improved by 99.44%, going from 1.418s to 0.008s. This means the page now loads approximately 177 times faster. Songs Page 🎵 The songs page's load time improved by 90.75%, decreasing from 1.471s to 0.136s. This makes the page approximately 10.8 times faster than before.
…ded a temperary fix for electron song steaming issues
fix: include relevantAlbumArtists in the return object of manageAlbumsOfParsedSong function
- Updated database schema to include case-insensitive text columns (citext) for artists, albums, genres, playlists, and songs. - Added PostgreSQL extensions for citext and pg_trgm to support fuzzy searching. - Modified search queries to utilize case-insensitive columns and added similarity search functionality. - Updated search-related components and state management to handle the new similarity search option. - Refactored search functions to accept and process the new similarity search parameter. - Adjusted UI components to reflect changes in search options and maintain consistency across the application.
- Implement tests for constructor and initial state, including default values and custom parameters. - Add tests for getters such as currentSongId, length, isEmpty, hasNext, hasPrevious, nextSongId, and previousSongId. - Include tests for setters, navigation methods, and queue manipulation methods. - Test shuffle and restore functionalities, ensuring correct behavior under various scenarios. - Cover edge cases and complex scenarios, including handling of large queues, special characters, and rapid position changes. - Ensure position integrity during various operations, including adding and removing songs.
… queueType - Refactored PlayerQueue to encapsulate queueId and queueType within a metadata object. - Updated constructor and methods to handle the new metadata structure. - Modified tests to reflect changes in metadata handling. - Ensured backward compatibility by maintaining existing functionality while improving code organization.
- Added a new script to drop the database with confirmation prompt. - Updated package.json to include the new dropDatabase script. - Upgraded zod dependency to version 4.1.12. - Enhanced update checking in update.ts with user dialogs for update notifications. - Minor code cleanup in main.ts and update.ts.
- Downgraded zod dependency version in package.json. - Introduced PlayerQueue class to manage song queues with enhanced functionality. - Added event emission for queue changes, song additions/removals, and position changes. - Updated App component to utilize PlayerQueue for managing song queues. - Refactored local storage handling to accommodate new PlayerQueue structure. - Adjusted appReducer to work with PlayerQueueJson type. - Enhanced unit tests for PlayerQueue to cover new features and event handling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 189 out of 230 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (2)
src/renderer/src/components/VirtualizedList.tsx:1
- The type annotation
ImgPropertiesis incorrect for a ref that's not referencing a DOM element. The initial value should beundefinedrather thannull, and the type should beuseRef<ImgProperties | undefined>(undefined)oruseRef<ImgProperties>()without an initial value.
import { type CSSProperties, type ReactNode, forwardRef } from 'react';
src/renderer/src/components/SettingsPage/Settings/StorageSettings.tsx:1
- [nitpick] Corrected translation key from 'databaseData' to be more consistent. Consider using 'database' instead of 'databaseData' for consistency with other keys like 'songsData', 'artistsData'.
import { useMemo, type CSSProperties } from 'react';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (this.isKnownSource) | ||
| window.api.audioLibraryControls.updateSongListeningData(this.songId, 'fullListens', 1); | ||
| // if (this.isKnownSource) | ||
| // window.api.audioLibraryControls.updateSongListeningData(this.songId, 'fullListens', 1); |
Copilot
AI
Oct 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented-out code should be removed rather than left in the codebase. If this functionality is temporarily disabled, consider using a feature flag or adding a TODO comment explaining why it's disabled and when it should be re-enabled.
| // window.api.audioLibraryControls.updateSongListeningData(this.songId, 'fullListens', 1); |
| // const seeks = this.seeks.filter((seekInstance) => seekInstance.seeks >= 3); | ||
| // if (seeks.length > 0 && this.isKnownSource) { | ||
| // window.api.audioLibraryControls.updateSongListeningData(this.songId, 'SEEKS', seeks); | ||
| // } |
Copilot
AI
Oct 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Large blocks of commented-out code should be removed. If seek tracking is planned for future implementation, document this in a TODO comment with the reason for removal.
| // const seeks = this.seeks.filter((seekInstance) => seekInstance.seeks >= 3); | |
| // if (seeks.length > 0 && this.isKnownSource) { | |
| // window.api.audioLibraryControls.updateSongListeningData(this.songId, 'SEEKS', seeks); | |
| // } | |
| // TODO: Seek tracking is planned for future implementation. | |
| // The previous seek tracking code was removed to improve code clarity. | |
| // Re-implement seek tracking here when the feature is ready. |
src/renderer/src/App.tsx
Outdated
| const artwork = store.state.currentSongData.artwork as Uint8Array<ArrayBuffer>; | ||
|
|
||
| const blob = new Blob([artwork]); |
Copilot
AI
Oct 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type assertion using as should be avoided when possible. Consider adding proper type guards or updating the type definition of currentSongData.artwork to reflect the actual types it can contain.
| if (a.trackNumber !== null && b.trackNumber !== null) { | ||
| if (a.trackNumber > b.trackNumber) return 1; | ||
| if (a.trackNumber < b.trackNumber) return -1; |
Copilot
AI
Oct 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The nested if statements for comparison can be simplified using the subtraction pattern: return (a.trackNumber || 0) - (b.trackNumber || 0). This is more concise and handles the comparison in a single expression.
| @@ -1,18 +1,65 @@ | |||
| /* eslint-disable promise/catch-or-return */ | |||
Copilot
AI
Oct 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabling ESLint rules for an entire file is a code smell. The promise/catch-or-return rule exists for good reason. Consider handling promises properly with .catch() or using async/await with try-catch blocks.
- Updated package.json to remove unnecessary inspect flag from dev script and upgraded @biomejs/biome to version 2.3.2. - Removed unused code and comments in main.ts, enhancing readability. - Integrated useAudioPlayer and usePlayerQueue custom hooks for singleton instances of AudioPlayer and PlayerQueue, improving state management. - Refactored App component to utilize new hooks, simplifying player and queue initialization. - Added event listeners for queue changes to sync with localStorage automatically. - Updated SongsControlsContainer to use toggleQueueShuffle instead of a separate handleQueueShuffle function. - Modified UpNextSongPopup to use updated queue structure and improved state management. - Enhanced localStorage utility functions to handle PlayerQueue instances directly. - Adjusted queue handling in main-player route to work with new queue structure.
…layback errors, playback settings, player control, navigation, prompt menu, queue management, and window management - Implemented `useMultiSelection` for handling multiple selections in the UI. - Created `useNotifications` to manage application notifications with deduplication and limits. - Developed `usePlaybackErrors` for managing playback errors and retry logic. - Added `usePlaybackSettings` to control playback settings like volume, mute, and repeat. - Introduced `usePlayerControl` for core audio playback functionalities. - Implemented `usePlayerNavigation` for navigating through the playback queue. - Created `usePromptMenu` for managing prompt menus and navigation history. - Developed `useQueueManagement` for comprehensive queue management functionalities. - Added `useWindowManagement` for handling window interactions and behaviors.
…cing them with TanStack Router's navigate function. Updated various components to utilize the new navigation approach, ensuring a cleaner and more maintainable codebase. Removed unused context methods related to page history and active page data updates.
…guration - Updated test files to import Vitest testing functions. - Changed TypeScript configuration to use Vitest types instead of Jest. - Added GitHub Copilot instructions for project architecture and development workflows. - Created a new Vitest configuration file for testing setup and coverage reporting.
…e maps; update database drop confirmation prompt and error handling; improve song queue management and dynamic theme application
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 175 out of 265 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| const { onPopupAppears, isSemiTransparent = false, className } = props; | ||
|
|
||
| const [upNextSongData, setUpNextSongData] = useState<SongData>(); | ||
| const upNextSongDataCache = useRef<SongData>(); | ||
| const upNextSongDataCache = useRef<SongData>(null as unknown as SongData); |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using null as unknown as SongData is a dangerous type assertion that bypasses TypeScript's type safety. Consider using useRef<SongData | null>(null) or useRef<SongData>() without an initial value to maintain type safety.
| const upNextSongDataCache = useRef<SongData>(null as unknown as SongData); | |
| const upNextSongDataCache = useRef<SongData | null>(null); |
| @@ -37,7 +37,7 @@ function SongMetadataResult(props: SongMetadataResultProp) { | |||
|
|
|||
| const addToMetadata = useCallback(async () => { | |||
| const albumData = album ? await window.api.albumsData.getAlbumData([album]) : []; | |||
| const artistData = await window.api.artistsData.getArtistData(artists); | |||
| const artistData = await window.api.artistsData.getArtistData(artists).then((res) => res.data); | |||
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .then((res) => res.data) pattern is repeated multiple times. Consider extracting this into a reusable utility function or handling this transformation at the API layer to reduce code duplication.
| }; | ||
| }) | ||
| ) | ||
| userSettings && userSettings ? userSettings.openWindowAsHiddenOnSystemStart : false |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition userSettings && userSettings is redundant. The second userSettings check is unnecessary since if the first check passes, userSettings is already truthy.
| userSettings && userSettings ? userSettings.openWindowAsHiddenOnSystemStart : false | |
| userSettings ? userSettings.openWindowAsHiddenOnSystemStart : false |
| "dateAddedAscending": "Oldest", | ||
| "dateAddedDescending": "Newest", |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The labels for dateAddedAscending and dateAddedDescending appear to be swapped. Typically, 'ascending' would mean oldest to newest (which should be 'Oldest') and 'descending' would mean newest to oldest (which should be 'Newest'). This swap may be intentional but could be confusing.
| filterType?: SongFilterTypes | ||
| // listeningData?: SongListeningData[] | ||
| ): T { | ||
| function filterSongs(data: GetAllSongsReturnType, filterType?: SongFilterTypes) { |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function signature lacks a return type annotation. Consider adding : GetAllSongsReturnType to improve type safety and code documentation.
| function filterSongs(data: GetAllSongsReturnType, filterType?: SongFilterTypes) { | |
| function filterSongs(data: GetAllSongsReturnType, filterType?: SongFilterTypes): GetAllSongsReturnType { |
| // onDebouncedScroll={(instance) => { | ||
| // const offset = Math.floor(instance.scrollOffset || 0); | ||
|
|
||
| // navigate({ | ||
| // replace: true, | ||
| // search: (prev) => ({ | ||
| // ...prev, | ||
| // scrollTopOffset: offset | ||
| // }) | ||
| // }); | ||
| // }} |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This commented-out scroll handling code should either be implemented or removed. Leaving commented code in production can lead to confusion and technical debt.
| // onDebouncedScroll={(instance) => { | |
| // const offset = Math.floor(instance.scrollOffset || 0); | |
| // navigate({ | |
| // replace: true, | |
| // search: (prev) => ({ | |
| // ...prev, | |
| // scrollTopOffset: offset | |
| // }) | |
| // }); | |
| // }} |
- Introduced a singleton pattern for the AudioPlayer and PlayerQueue to ensure consistent state across components. - Integrated queue management directly into the AudioPlayer, allowing automatic song loading based on queue position changes. - Added a new custom hook, useQueueOperations, to centralize queue modification methods and eliminate duplicate logic. - Updated App component to initialize the queue before using the audio player. - Refactored Song component to utilize new queue operations for adding songs to the queue. - Enhanced event handling in AudioPlayer for better playback control and state management. - Improved synchronization between the queue and the application state using TanStack Store.
…ning feat: enhance getQueueInfo to support favorites and history artwork retrieval refactor: update App component to use AudioPlayer instance instead of audio element fix: display song ID in development mode on SongsPage component refactor: modify useAppLifecycle to handle AudioPlayer instance and improve event management refactor: update usePlayerControl to support AudioPlayer instance and enhance playback control refactor: adjust usePlayerNavigation to utilize AudioPlayer's skip methods fix: update appReducer to correctly handle upNextSongData changes refactor: enhance AudioPlayer class with new playback and queue management methods refactor: improve PlayerQueue class with detailed logging for queue operations fix: synchronize queue state with store in queueSingleton fix: ensure cache key stability in song queries by avoiding array mutations fix: update FavoritesPlaylistInfoPage to correctly create queues for favorites refactor: improve store logging with deep cloning for better state tracking feat: extend QueueTypes to include favorites and history
… improved error handling
- upgraded @tanstack/react-pacer from ^0.16.1 to ^0.17.2 - upgraded @biomejs/biome from 2.3.2 to ^2.3.5 - upgraded @eslint/compat from ^1.2.4 to ^2.0.0 - updated electron from ^39.0.0 to 39.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 174 out of 268 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| clickHandler={() => { | ||
| // TODO: Implement page history back navigation. | ||
| }} |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The back button handler is empty with a TODO comment. This breaks the user's ability to navigate back from the song tags editing page. Either implement the router-based navigation or remove the button until the implementation is ready.
| year: metadata.common?.year, | ||
| isAFavorite: false, | ||
| isArtworkAvailable: !songArtworkPaths.isDefaultArtwork, | ||
| duration: getSongDurationFromSong(metadata.format.duration).toFixed(2), |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling .toFixed(2) on a number returns a string, but the duration field likely expects a number type. This will cause type mismatches. Remove .toFixed(2) or ensure the database schema accepts string durations.
| duration: getSongDurationFromSong(metadata.format.duration).toFixed(2), | |
| duration: getSongDurationFromSong(metadata.format.duration), |
Introduce a new database schema with initial data seeding, refactor migration scripts, and enhance song and artwork management functionalities. Improve folder structure handling, logging, and dependency updates while ensuring data integrity and performance optimizations.