Compare commits
4 Commits
81a1a0b32c
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ed28c03fb | |||
| c5f35efc9a | |||
| 32dc396e54 | |||
| 2c1cb54ff0 |
35
README.md
35
README.md
@@ -1,3 +1,36 @@
|
|||||||
# UniLoader
|
# UniLoader
|
||||||
|
|
||||||
A universal library loader for KiCad
|
A simple daemon that automatically imports any libraries into KiCad. It currently supports symbols and footprints from [SamacSys](https://componentsearchengine.com/), [UltraLibrarian](https://app.ultralibrarian.com/search), [SnapMagic](https://www.snapeda.com/home/)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
An automated install script is planned but currently the repo needs to be cloned, built and installed manually.
|
||||||
|
```
|
||||||
|
git clone https://git.h3cx.dev/h3cx/UniLoader.git
|
||||||
|
|
||||||
|
cd UniLoader
|
||||||
|
|
||||||
|
cargo build -r
|
||||||
|
```
|
||||||
|
You will then find the binary under `target/release` you can run it manually or create a systemd service to automatically run it at boot.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To get started you will need to set up your config, to do so create a config file. A full config file can be found in the config folder of this repo.
|
||||||
|
```
|
||||||
|
mkdir ~/.config/uniloader
|
||||||
|
|
||||||
|
touch ~/.config/uniloader/config.toml
|
||||||
|
```
|
||||||
|
Then use your favorite text editor to open this config and paste in the minimal config.
|
||||||
|
```
|
||||||
|
[libraries]
|
||||||
|
path = "~/KiCad/libraries/"
|
||||||
|
```
|
||||||
|
Now that you have set this up, the application should be ready to go, just head over to your favorite library website and download any component.
|
||||||
|
|
||||||
|
Upon downloading the first component from a given online library you will be required to add the library to KiCad. In KiCad open your symbol editor and head to `File->Add Library` then naviagate to the path you set in your config and add the library. Repeat this from the footprint editor for the footprints libraries.
|
||||||
|
|
||||||
|
## Warranty
|
||||||
|
|
||||||
|
Use this program at your own risk, no warranty is provided for anything that it could do to your libraries, KiCad or operating system. If you encounter any issues feel free to open an issue.
|
||||||
|
|||||||
8
config/config_full.toml
Normal file
8
config/config_full.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[libraries]
|
||||||
|
path = "/home/hector/KiCad/libraries/"
|
||||||
|
|
||||||
|
[options]
|
||||||
|
# Selectively enable and disable the importing of footprints and symbols
|
||||||
|
# symbols=true
|
||||||
|
# footprints=false
|
||||||
|
|
||||||
32
src/main.rs
32
src/main.rs
@@ -1,3 +1,5 @@
|
|||||||
|
// Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
@@ -14,6 +16,7 @@ use zip::ZipArchive;
|
|||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct Config {
|
struct Config {
|
||||||
libraries: LibConfig,
|
libraries: LibConfig,
|
||||||
|
options: Option<OptionsConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -32,6 +35,7 @@ impl Config {
|
|||||||
libraries: LibConfig {
|
libraries: LibConfig {
|
||||||
path: PathBuf::from("~/KiCad/libraries/"),
|
path: PathBuf::from("~/KiCad/libraries/"),
|
||||||
},
|
},
|
||||||
|
options: None,
|
||||||
};
|
};
|
||||||
let contents = toml::to_string_pretty(&config).map_err(|_| ConfigError::ConfigErr)?;
|
let contents = toml::to_string_pretty(&config).map_err(|_| ConfigError::ConfigErr)?;
|
||||||
fs::write(&config_path, contents).map_err(|_| ConfigError::ConfigErr)?;
|
fs::write(&config_path, contents).map_err(|_| ConfigError::ConfigErr)?;
|
||||||
@@ -45,6 +49,12 @@ struct LibConfig {
|
|||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
struct OptionsConfig {
|
||||||
|
symbols: Option<bool>,
|
||||||
|
footprints: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
enum SourceType {
|
enum SourceType {
|
||||||
SymacSys,
|
SymacSys,
|
||||||
UltraLibrarian,
|
UltraLibrarian,
|
||||||
@@ -309,7 +319,7 @@ fn import_symacsys(path: &Path, config: &Config) -> Result<(), ImportError> {
|
|||||||
let sym = root.join("SymacSys_Components.kicad_sym");
|
let sym = root.join("SymacSys_Components.kicad_sym");
|
||||||
let pretty = root.join("SymacSys_Footprints.pretty");
|
let pretty = root.join("SymacSys_Footprints.pretty");
|
||||||
|
|
||||||
install_library(sym_files, mod_files, &root, &sym, &pretty)?;
|
install_library(sym_files, mod_files, &root, &sym, &pretty, config)?;
|
||||||
|
|
||||||
fs::remove_dir_all(tmp_path).map_err(|_| ImportError::InvalidPath)?;
|
fs::remove_dir_all(tmp_path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -363,7 +373,7 @@ fn import_ultralibrarian(path: &Path, config: &Config) -> Result<(), ImportError
|
|||||||
let sym = root.join("UltraLibrarian_Components.kicad_sym");
|
let sym = root.join("UltraLibrarian_Components.kicad_sym");
|
||||||
let pretty = root.join("UltraLibrarian_Footprints.pretty");
|
let pretty = root.join("UltraLibrarian_Footprints.pretty");
|
||||||
|
|
||||||
install_library(sym_files, mod_files, &root, &sym, &pretty)?;
|
install_library(sym_files, mod_files, &root, &sym, &pretty, config)?;
|
||||||
|
|
||||||
fs::remove_dir_all(tmp_path).map_err(|_| ImportError::InvalidPath)?;
|
fs::remove_dir_all(tmp_path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -404,7 +414,7 @@ fn import_snapmagic(path: &Path, config: &Config) -> Result<(), ImportError> {
|
|||||||
let sym = root.join("SnapMagic_components.kicad_sym");
|
let sym = root.join("SnapMagic_components.kicad_sym");
|
||||||
let pretty = root.join("SnapMagic_Footprints.pretty");
|
let pretty = root.join("SnapMagic_Footprints.pretty");
|
||||||
|
|
||||||
install_library(sym_files, mod_files, &root, &sym, &pretty)?;
|
install_library(sym_files, mod_files, &root, &sym, &pretty, config)?;
|
||||||
|
|
||||||
fs::remove_dir_all(data_dir).map_err(|_| ImportError::InvalidPath)?;
|
fs::remove_dir_all(data_dir).map_err(|_| ImportError::InvalidPath)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -416,10 +426,23 @@ fn install_library(
|
|||||||
lib_root: &Path,
|
lib_root: &Path,
|
||||||
sym_file: &Path,
|
sym_file: &Path,
|
||||||
pretty_dir: &Path,
|
pretty_dir: &Path,
|
||||||
|
config: &Config,
|
||||||
) -> Result<(), ImportError> {
|
) -> Result<(), ImportError> {
|
||||||
|
let symbols = config
|
||||||
|
.options
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|o| o.symbols)
|
||||||
|
.unwrap_or(true);
|
||||||
|
let footprints = config
|
||||||
|
.options
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|o| o.footprints)
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
fs::create_dir_all(lib_root).map_err(|_| ImportError::InvalidPath)?;
|
fs::create_dir_all(lib_root).map_err(|_| ImportError::InvalidPath)?;
|
||||||
fs::create_dir_all(pretty_dir).map_err(|_| ImportError::InvalidPath)?;
|
fs::create_dir_all(pretty_dir).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
if symbols {
|
||||||
// Load or create symbol library
|
// Load or create symbol library
|
||||||
let mut current_lib = if sym_file.exists() {
|
let mut current_lib = if sym_file.exists() {
|
||||||
let contents = fs::read_to_string(sym_file).map_err(|_| ImportError::InvalidPath)?;
|
let contents = fs::read_to_string(sym_file).map_err(|_| ImportError::InvalidPath)?;
|
||||||
@@ -444,13 +467,16 @@ fn install_library(
|
|||||||
let output = current_lib.write();
|
let output = current_lib.write();
|
||||||
fs::write(sym_file, output).map_err(|_| ImportError::InvalidPath)?;
|
fs::write(sym_file, output).map_err(|_| ImportError::InvalidPath)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if footprints {
|
||||||
// Install footprints
|
// Install footprints
|
||||||
for mod_path in mod_files {
|
for mod_path in mod_files {
|
||||||
let filename = mod_path.file_name().ok_or(ImportError::InvalidPath)?;
|
let filename = mod_path.file_name().ok_or(ImportError::InvalidPath)?;
|
||||||
let target = pretty_dir.join(filename);
|
let target = pretty_dir.join(filename);
|
||||||
fs::copy(mod_path, target).map_err(|_| ImportError::InvalidPath)?;
|
fs::copy(mod_path, target).map_err(|_| ImportError::InvalidPath)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user