[READ-ONLY] Mirror of https://github.com/probablykasper/ferrum. Music library app for Mac, Linux and Windows ferrum.kasper.space
electron linux macos music music-library music-player napi windows
0

Configure Feed

Select the types of activity you want to include in your feed.

Remove unused code

Kasper (Sep 26, 2024, 3:20 AM +0200) 4d30b99c df20bb21

+12 -220
+1 -16
ferrum-addon/addon.d.ts
··· 19 19 tracksCount: number 20 20 playlistsCount: number 21 21 } 22 - export declare function copyFile(from: string, to: string): void 23 - export declare function atomicFileSave(filePath: string, content: string): void 24 22 export declare function get_default_sort_desc(field: string): boolean 25 23 export interface Track { 26 24 size: number ··· 122 120 itemIds: Array<ItemId> 123 121 } 124 122 export declare function get_tracks_page(options: TracksPageOptions): TracksPage 125 - export const enum ViewAs { 126 - Songs = 0, 127 - Artists = 1 128 - } 129 - export interface PageInfo { 130 - id: string 131 - viewAs: ViewAs 132 - tracklist: TrackList 133 - sortKey: string 134 - sortDesc: boolean 135 - length: number 136 - } 137 - export declare function move_tracks(playlistId: string, itemIds: Array<ItemId>, toIndex: number): void 138 123 export interface TrackListDetails { 139 124 id: string 140 125 name: string ··· 153 138 export declare function new_playlist(name: string, description: string, isFolder: boolean, parentId: string): void 154 139 export declare function update_playlist(id: string, name: string, description: string): void 155 140 export declare function move_playlist(id: string, fromId: string, toId: string, toIndex: number): void 141 + export declare function move_tracks(playlistId: string, itemIds: Array<ItemId>, toIndex: number): void 156 142 /** Returns `None` if the file does not have an image */ 157 143 export declare function read_small_cover_async(path: string, index: number, cacheDbPath: string): Promise<Buffer | null> 158 144 export interface TrackMd { ··· 183 169 export declare function add_skip(trackId: string): void 184 170 export declare function add_play_time(id: TrackID, start: MsSinceUnixEpoch, durMs: number): void 185 171 export declare function read_cover_async(trackId: string, index: number): Promise<ArrayBuffer> 186 - export declare function read_cover_async_path(path: string, index: number): Promise<ArrayBuffer> 187 172 export declare function import_file(path: string, now: MsSinceUnixEpoch): void 188 173 export declare function load_tags(trackId: string): void 189 174 export interface JsImage {
-116
package-lock.json
··· 12 12 "@sveltejs/vite-plugin-svelte": "^3.1.2", 13 13 "@tailwindcss/vite": "4.0.0-alpha.23", 14 14 "@tsconfig/svelte": "^5.0.4", 15 - "concurrently": "^8.2.2", 16 15 "electron": "^28.3.3", 17 16 "electron-builder": "^25.0.5", 18 17 "eslint": "^9.10.0", ··· 44 43 }, 45 44 "engines": { 46 45 "node": ">=6.0.0" 47 - } 48 - }, 49 - "node_modules/@babel/runtime": { 50 - "version": "7.25.6", 51 - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", 52 - "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", 53 - "dev": true, 54 - "dependencies": { 55 - "regenerator-runtime": "^0.14.0" 56 - }, 57 - "engines": { 58 - "node": ">=6.9.0" 59 46 } 60 47 }, 61 48 "node_modules/@develar/schema-utils": { ··· 3063 3050 "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 3064 3051 "dev": true 3065 3052 }, 3066 - "node_modules/concurrently": { 3067 - "version": "8.2.2", 3068 - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", 3069 - "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", 3070 - "dev": true, 3071 - "dependencies": { 3072 - "chalk": "^4.1.2", 3073 - "date-fns": "^2.30.0", 3074 - "lodash": "^4.17.21", 3075 - "rxjs": "^7.8.1", 3076 - "shell-quote": "^1.8.1", 3077 - "spawn-command": "0.0.2", 3078 - "supports-color": "^8.1.1", 3079 - "tree-kill": "^1.2.2", 3080 - "yargs": "^17.7.2" 3081 - }, 3082 - "bin": { 3083 - "conc": "dist/bin/concurrently.js", 3084 - "concurrently": "dist/bin/concurrently.js" 3085 - }, 3086 - "engines": { 3087 - "node": "^14.13.0 || >=16.0.0" 3088 - }, 3089 - "funding": { 3090 - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" 3091 - } 3092 - }, 3093 3053 "node_modules/config-file-ts": { 3094 3054 "version": "0.2.8-rc1", 3095 3055 "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", ··· 3230 3190 }, 3231 3191 "engines": { 3232 3192 "node": ">=4" 3233 - } 3234 - }, 3235 - "node_modules/date-fns": { 3236 - "version": "2.30.0", 3237 - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", 3238 - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", 3239 - "dev": true, 3240 - "dependencies": { 3241 - "@babel/runtime": "^7.21.0" 3242 - }, 3243 - "engines": { 3244 - "node": ">=0.11" 3245 - }, 3246 - "funding": { 3247 - "type": "opencollective", 3248 - "url": "https://opencollective.com/date-fns" 3249 3193 } 3250 3194 }, 3251 3195 "node_modules/debug": { ··· 6792 6736 "node": ">=8.10.0" 6793 6737 } 6794 6738 }, 6795 - "node_modules/regenerator-runtime": { 6796 - "version": "0.14.1", 6797 - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", 6798 - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", 6799 - "dev": true 6800 - }, 6801 6739 "node_modules/require-directory": { 6802 6740 "version": "2.1.1", 6803 6741 "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", ··· 6992 6930 "queue-microtask": "^1.2.2" 6993 6931 } 6994 6932 }, 6995 - "node_modules/rxjs": { 6996 - "version": "7.8.1", 6997 - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", 6998 - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", 6999 - "dev": true, 7000 - "dependencies": { 7001 - "tslib": "^2.1.0" 7002 - } 7003 - }, 7004 6933 "node_modules/sade": { 7005 6934 "version": "1.8.1", 7006 6935 "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", ··· 7130 7059 "node": ">=8" 7131 7060 } 7132 7061 }, 7133 - "node_modules/shell-quote": { 7134 - "version": "1.8.1", 7135 - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", 7136 - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", 7137 - "dev": true, 7138 - "funding": { 7139 - "url": "https://github.com/sponsors/ljharb" 7140 - } 7141 - }, 7142 7062 "node_modules/signal-exit": { 7143 7063 "version": "3.0.7", 7144 7064 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", ··· 7250 7170 "source-map": "^0.6.0" 7251 7171 } 7252 7172 }, 7253 - "node_modules/spawn-command": { 7254 - "version": "0.0.2", 7255 - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", 7256 - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", 7257 - "dev": true 7258 - }, 7259 7173 "node_modules/sprintf-js": { 7260 7174 "version": "1.1.3", 7261 7175 "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", ··· 7370 7284 "node": ">= 8.0" 7371 7285 } 7372 7286 }, 7373 - "node_modules/supports-color": { 7374 - "version": "8.1.1", 7375 - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 7376 - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 7377 - "dev": true, 7378 - "dependencies": { 7379 - "has-flag": "^4.0.0" 7380 - }, 7381 - "engines": { 7382 - "node": ">=10" 7383 - }, 7384 - "funding": { 7385 - "url": "https://github.com/chalk/supports-color?sponsor=1" 7386 - } 7387 - }, 7388 7287 "node_modules/supports-preserve-symlinks-flag": { 7389 7288 "version": "1.0.0", 7390 7289 "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", ··· 7742 7641 "node": ">=8.0" 7743 7642 } 7744 7643 }, 7745 - "node_modules/tree-kill": { 7746 - "version": "1.2.2", 7747 - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", 7748 - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", 7749 - "dev": true, 7750 - "bin": { 7751 - "tree-kill": "cli.js" 7752 - } 7753 - }, 7754 7644 "node_modules/truncate-utf8-bytes": { 7755 7645 "version": "1.0.2", 7756 7646 "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", ··· 7771 7661 "peerDependencies": { 7772 7662 "typescript": ">=4.2.0" 7773 7663 } 7774 - }, 7775 - "node_modules/tslib": { 7776 - "version": "2.7.0", 7777 - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", 7778 - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", 7779 - "dev": true 7780 7664 }, 7781 7665 "node_modules/type-check": { 7782 7666 "version": "0.4.0",
-1
package.json
··· 67 67 "@sveltejs/vite-plugin-svelte": "^3.1.2", 68 68 "@tailwindcss/vite": "4.0.0-alpha.23", 69 69 "@tsconfig/svelte": "^5.0.4", 70 - "concurrently": "^8.2.2", 71 70 "electron": "^28.3.3", 72 71 "electron-builder": "^25.0.5", 73 72 "eslint": "^9.10.0",
-25
src-native/js.rs
··· 1 - use atomicwrites::{AllowOverwrite, AtomicFile}; 2 - use napi::Error as NapiError; 3 - use std::fs::copy; 4 - use std::io::Write; 5 - 6 - // Create NapiError from string 7 - pub fn nerr(msg: &str) -> NapiError { 8 - return NapiError::from_reason(msg.to_owned()); 9 - } 10 - 11 - #[napi] 12 - #[allow(dead_code)] 13 - fn copy_file(from: String, to: String) -> napi::Result<()> { 14 - match copy(from, to) { 15 - Ok(_) => Ok(()), 16 - Err(err) => Err(err.into()), 17 - } 18 - } 19 - 20 - #[napi] 21 - #[allow(dead_code)] 22 - fn atomic_file_save(file_path: String, content: String) { 23 - let af = AtomicFile::new(file_path, AllowOverwrite); 24 - af.write(|f| f.write_all(content.as_bytes())).unwrap(); 25 - }
-1
src-native/lib.rs
··· 25 25 mod data_js; 26 26 mod filter; 27 27 mod itunes_import; 28 - mod js; 29 28 mod library; 30 29 mod library_types; 31 30 mod page;
+1 -23
src-native/page.rs
··· 3 3 use crate::filter::filter; 4 4 use crate::library_types::{ItemId, TrackList}; 5 5 use crate::sort::sort; 6 - use napi::{Env, JsUnknown, Result}; 6 + use napi::{Env, Result}; 7 7 8 8 #[napi(object)] 9 9 #[derive(Clone)] ··· 57 57 }; 58 58 Ok(track_page) 59 59 } 60 - 61 - #[napi] 62 - pub enum ViewAs { 63 - Songs, 64 - Artists, 65 - } 66 - impl Default for ViewAs { 67 - fn default() -> Self { 68 - Self::Songs 69 - } 70 - } 71 - 72 - #[napi(object)] 73 - pub struct PageInfo { 74 - pub id: String, 75 - pub view_as: ViewAs, 76 - #[napi(ts_type = "TrackList")] 77 - pub tracklist: JsUnknown, 78 - pub sort_key: String, 79 - pub sort_desc: bool, 80 - pub length: i64, 81 - }
+1 -1
src-native/playlists.rs
··· 121 121 /// Returns the deleted track lists, including folder children 122 122 #[napi(js_name = "delete_track_list")] 123 123 #[allow(dead_code)] 124 - pub fn delete_track_list_js(id: String, env: Env) -> Result<()> { 124 + pub fn delete_track_list(id: String, env: Env) -> Result<()> { 125 125 let data: &mut Data = get_data(&env)?; 126 126 let parent_id = data 127 127 .library
+1 -11
src-native/tracks/mod.rs
··· 1 1 use crate::data::Data; 2 2 use crate::data_js::get_data; 3 3 use crate::get_now_timestamp; 4 - use crate::js::nerr; 5 4 use crate::library_types::{ItemId, MsSinceUnixEpoch, Track, TrackID, TRACK_ID_MAP}; 6 5 use napi::{Env, JsArrayBuffer, JsBuffer, JsObject, Result, Task}; 7 6 use std::fs; ··· 103 102 pub fn add_play_time(id: TrackID, start: MsSinceUnixEpoch, dur_ms: i64, env: Env) -> Result<()> { 104 103 let data: &mut Data = get_data(&env)?; 105 104 let tracks = data.library.get_tracks(); 106 - tracks.get(&id).ok_or(nerr("Track ID not found"))?; 105 + tracks.get(&id).ok_or(nerr!("Track ID not found"))?; 107 106 data.library.playTime.push((id, start, dur_ms)); 108 107 Ok(()) 109 108 } ··· 140 139 let tracks_dir = &data.paths.tracks_dir; 141 140 let file_path = tracks_dir.join(&track.file); 142 141 let task = ReadCover(file_path, index.into()); 143 - env.spawn(task).map(|t| t.promise_object()) 144 - } 145 - #[napi( 146 - js_name = "read_cover_async_path", 147 - ts_return_type = "Promise<ArrayBuffer>" 148 - )] 149 - #[allow(dead_code)] 150 - pub fn read_cover_async_path(path: String, index: u16, env: Env) -> Result<JsObject> { 151 - let task = ReadCover(path.into(), index.into()); 152 142 env.spawn(task).map(|t| t.promise_object()) 153 143 } 154 144
+2 -7
src/components/Queue.svelte
··· 1 1 <script lang="ts"> 2 2 import { 3 - append_to_user_queue, 4 3 clear_user_queue, 5 4 get_by_queue_index, 6 5 get_queue_length, 7 6 insert_ids, 8 7 move_indexes, 9 - prepend_to_user_queue, 10 8 queue, 11 9 type QueueItem, 12 10 } from '../lib/queue' 13 - import { add_tracks_to_playlist, paths } from '../lib/data' 14 11 import { onDestroy } from 'svelte' 15 12 import QueueItemComponent from './QueueItem.svelte' 16 13 import { new_selection } from '@/lib/selection' ··· 18 15 import { methods } from '@/lib/data' 19 16 import * as dragGhost from './DragGhost.svelte' 20 17 import { ipc_listen, ipc_renderer } from '@/lib/window' 21 - import { assert_unreachable, check_shortcut } from '@/lib/helpers' 22 - import { fly, scale } from 'svelte/transition' 18 + import { check_shortcut } from '@/lib/helpers' 19 + import { fly } from 'svelte/transition' 23 20 import VirtualListBlock, { scroll_container_keydown } from './VirtualListBlock.svelte' 24 - import { open_track_info } from './TrackInfo.svelte' 25 - import Button from './Button.svelte' 26 21 import type { SelectedTracksAction } from '@/electron/typed_ipc' 27 22 import { get_flattened_tracklists, handle_selected_tracks_action } from '@/lib/menus' 28 23
-2
src/components/TrackList.svelte
··· 19 19 <script lang="ts"> 20 20 import { 21 21 filter, 22 - paths, 23 22 methods, 24 - track_lists_details_map, 25 23 move_tracks, 26 24 remove_from_playlist, 27 25 tracklist_updated,
+1 -1
src/electron/ipc.ts
··· 1 - import { dialog, Menu, shell, BrowserWindow, type MenuItemConstructorOptions } from 'electron' 1 + import { dialog, Menu, shell, BrowserWindow } from 'electron' 2 2 import { ipc_main } from './typed_ipc' 3 3 import path from 'path' 4 4 import is from './is'
+1 -1
src/electron/typed_ipc.ts
··· 9 9 MenuItemConstructorOptions, 10 10 } from 'electron' 11 11 import { ipcMain as electronIpcMain } from 'electron' 12 - import type { Track, TrackID } from '../../ferrum-addon' 12 + import type { TrackID } from '../../ferrum-addon' 13 13 14 14 type OptionalPromise<T> = T | Promise<T> 15 15 type InputMap = {
-4
src/lib/data.ts
··· 8 8 ItemId, 9 9 TrackMd, 10 10 TracksPageOptions, 11 - ViewAs, 12 11 ViewOptions, 13 12 } from '../../ferrum-addon' 14 13 import { queue } from './queue' ··· 25 24 export const ItunesImport = inner_addon.ItunesImport 26 25 27 26 call((addon) => addon.load_data(is_dev, local_data_path, library_path)) 28 - 29 - export const view_as_songs: ViewAs.Songs = 0 30 - export const view_as_artists: ViewAs.Artists = 1 31 27 32 28 function get_error_message(err: unknown): string { 33 29 if (typeof err === 'object' && err !== null) {
+3 -10
src/lib/menus.ts
··· 1 - import { 2 - add_tracks_to_playlist, 3 - methods, 4 - paths, 5 - remove_from_playlist, 6 - track_lists_details_map, 7 - } from '@/lib/data' 1 + import { add_tracks_to_playlist, methods, paths, track_lists_details_map } from '@/lib/data' 8 2 import { flatten_child_lists } from '@/lib/helpers' 9 3 import { ipc_renderer } from '@/lib/window' 10 - import type { ItemId, TrackID } from '../../ferrum-addon' 4 + import type { TrackID } from '../../ferrum-addon' 11 5 import { get } from 'svelte/store' 12 6 import { append_to_user_queue, prepend_to_user_queue } from './queue' 13 - import type { SelectedTracksAction, ShowTrackMenuOptions } from '@/electron/typed_ipc' 14 - import { current_playlist_id } from '@/components/TrackList.svelte' 7 + import type { SelectedTracksAction } from '@/electron/typed_ipc' 15 8 import { open_track_info } from '@/components/TrackInfo.svelte' 16 9 17 10 export function get_flattened_tracklists() {
+1 -1
src/lib/selection-new.ts
··· 1 - import { writable, type Updater, type Writable } from 'svelte/store' 1 + import { writable, type Writable } from 'svelte/store' 2 2 import { check_mouse_shortcut, check_shortcut } from './helpers' 3 3 4 4 type SelectionOptions<T> = {