[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.

Use call_safe instead

Kasper (Sep 15, 2025, 1:54 AM +0200) 07d26575 6ca8412c

+57 -15
+3 -3
src-native/tracks/md.rs
··· 1 1 #![allow(non_snake_case)] 2 2 3 - use super::{generate_filename, Tag}; 3 + use super::{Tag, generate_filename}; 4 4 use crate::library_types::Track; 5 5 use crate::{get_now_timestamp, str_to_option}; 6 - use anyhow::{bail, Context, Result}; 6 + use anyhow::{Context, Result, bail}; 7 7 use serde::{Deserialize, Serialize}; 8 8 use std::fs; 9 9 use std::path::PathBuf; ··· 142 142 let new_comments = str_to_option(new_info.comments); 143 143 144 144 // save tag 145 - tag.write_to_path(&old_path)?; 145 + tag.write_to_path(&old_path).context("Failed to save tag")?; 146 146 147 147 // move file 148 148 if new_name != track.name || new_artist != track.artist {
+13 -10
src/lib/data.ts
··· 12 12 import { queue } from './queue' 13 13 import { current_playlist_id } from '@/components/TrackList.svelte' 14 14 import { navigate } from './router' 15 - import { call, error_popup, get_error_message, strict_call } from './error' 15 + import { call, call_safe, error_popup, get_error_message, strict_call } from './error' 16 16 17 17 export const is_dev = window.is_dev 18 18 export const local_data_path = window.local_data_path ··· 183 183 } 184 184 } 185 185 export function add_play(id: TrackID) { 186 - call((data) => data.add_play(id)) 187 - tracklist_updated.emit() 188 - save() 186 + return call_safe((data) => data.add_play(id)).on_success(() => { 187 + tracklist_updated.emit() 188 + save() 189 + }) 189 190 } 190 191 export function add_skip(id: TrackID) { 191 - call((data) => data.add_skip(id)) 192 - tracklist_updated.emit() 193 - save() 192 + return call_safe((data) => data.add_skip(id)).on_success(() => { 193 + tracklist_updated.emit() 194 + save() 195 + }) 194 196 } 195 197 export function add_play_time(id: TrackID, start_time: MsSinceUnixEpoch, duration_ms: number) { 196 - call((data) => data.add_play_time(id, start_time, duration_ms)) 197 - save() 198 + return call_safe((data) => data.add_play_time(id, start_time, duration_ms)).on_success(() => { 199 + save() 200 + }) 198 201 } 199 202 export function read_cover_async(id: TrackID, index: number) { 200 203 return inner_addon.read_cover_async(id, index).catch((error) => { ··· 203 206 }) 204 207 } 205 208 export function update_track_info(id: TrackID, md: TrackMd) { 206 - call((data) => data.update_track_info(id, md)) 209 + strict_call((data) => data.update_track_info(id, md)) 207 210 tracks_updated.emit() 208 211 save() 209 212 }
+41 -2
src/lib/error.ts
··· 62 62 63 63 // Handle async errors 64 64 if (result instanceof Promise) { 65 - result.catch((error) => { 65 + return result.catch((error) => { 66 66 error_popup(error, true) 67 67 throw error 68 - }) 68 + }) as T 69 69 } 70 70 71 71 // Handle synchronous result ··· 82 82 throw error 83 83 } 84 84 } 85 + 86 + type Result<T> = 87 + | { data: T; error: null; on_success: (cb: (data: T) => void) => Result<T> } 88 + | { data: null; error: Error; on_success: (cb: (data: T) => void) => Result<T> } 89 + 90 + type PromiseResult<T> = Promise<Result<T>> & { 91 + on_success: (cb: (data: T) => void) => Promise<Result<T>> 92 + } 93 + 94 + // Shows a popup message on error 95 + export function call_safe<T>(cb: (addon: typeof window.addon) => T): PromiseResult<T> { 96 + const promise = (async () => { 97 + try { 98 + const data = await cb(window.addon) 99 + 100 + const result = { data, error: null } 101 + return result 102 + } catch (raw_err) { 103 + let error: Error 104 + if (raw_err instanceof Error) { 105 + error = raw_err 106 + } else { 107 + error = new Error('Unexpected error: ' + String(raw_err)) 108 + } 109 + error_popup(error) 110 + 111 + const result = { data: null, error } 112 + return result 113 + } 114 + })() as PromiseResult<T> 115 + promise.on_success = async (cb) => { 116 + const result = await promise 117 + if (result.data) { 118 + cb(result.data) 119 + } 120 + return result 121 + } 122 + return promise 123 + }