libgpod Integration
This document covers the libgpod C library and how podkit integrates with it through native Node.js bindings.
Overview
libgpod is a C library for reading and writing the iTunes database (iTunesDB) on iPod devices. It is the de facto standard library used by most Linux iPod management tools.
| Attribute | Value |
|---|---|
| Repository | https://github.com/fadingred/libgpod |
| Language | C (with GLib) |
| License | LGPL-2.1 |
| Current Version | 0.8.3 |
| Status | Maintenance mode (stable, infrequent updates) |
IpodDatabase Abstraction
For application code, use IpodDatabase from @podkit/core instead of @podkit/libgpod-node directly.
The IpodDatabase class provides a clean, high-level API that:
- Hides internal details like
TrackHandlereferences - Provides type-safe track and playlist operations
- Returns immutable snapshots for safe data access
- Handles error translation to structured
IpodErrortypes
When to Use Each Package
| Use Case | Package |
|---|---|
| CLI commands, application code | @podkit/core (IpodDatabase) |
| Sync engine, business logic | @podkit/core (IpodDatabase) |
| libgpod binding tests | @podkit/libgpod-node |
| Debugging low-level issues | @podkit/libgpod-node |
Quick Example
import { IpodDatabase } from '@podkit/core';
// Open iPodconst ipod = await IpodDatabase.open('/Volumes/IPOD');
// Display infoconst info = ipod.getInfo();console.log(`${info.device.modelName} (${info.device.capacity}GB)`);
// List tracksfor (const track of ipod.getTracks()) { console.log(`${track.artist} - ${track.title}`);}
// Add a trackconst track = ipod.addTrack({ title: 'New Song', artist: 'Artist', album: 'Album',});track.copyFile('/path/to/song.mp3');
// Save and closeawait ipod.save();ipod.close();libgpod Core API
Database Operations
#include <gpod/itdb.h>
// Parse iPod database from mount pointItdb_iTunesDB *itdb_parse(const char *mountpoint, GError **error);
// Write database back to iPodgboolean itdb_write(Itdb_iTunesDB *itdb, GError **error);
// Free database structurevoid itdb_free(Itdb_iTunesDB *itdb);Track Management
// Create new trackItdb_Track *itdb_track_new(void);
// Add track to databasevoid itdb_track_add(Itdb_iTunesDB *itdb, Itdb_Track *track, gint32 pos);
// Remove track from databasevoid itdb_track_remove(Itdb_Track *track);
// Copy file to iPodgboolean itdb_cp_track_to_ipod(Itdb_Track *track, const char *filename, GError **error);
// Set track artworkgboolean itdb_track_set_thumbnails(Itdb_Track *track, const char *filename);Playlist Management
// Create new playlistItdb_Playlist *itdb_playlist_new(const char *title, gboolean spl);
// Add playlist to databasevoid itdb_playlist_add(Itdb_iTunesDB *itdb, Itdb_Playlist *pl, gint32 pos);
// Add track to playlistvoid itdb_playlist_add_track(Itdb_Playlist *pl, Itdb_Track *track, gint32 pos);Track Identification
libgpod provides several identifiers, but only pointers (Itdb_Track*) are reliable references.
Why IDs Are Unreliable
The id field has limitations:
- Assigned during
itdb_write(), notitdb_track_add() - Reassigned on every export
- New tracks have
id = 0until database is saved
How libgpod-node Uses TrackHandle
The TrackHandle abstraction wraps raw Itdb_Track* pointers safely:
import { Database } from '@podkit/libgpod-node';
const db = Database.openSync('/Volumes/IPOD');
// addTrack returns a TrackHandleconst handle = db.addTrack({ title: 'Song', artist: 'Artist' });
// All operations accept TrackHandledb.updateTrack(handle, { rating: 80 });db.copyTrackToDevice(handle, '/path/to/song.mp3');
// After save, handle remains validdb.saveSync();const track = db.getTrack(handle); // Still worksHandles become invalid after removeTrack() or close(). Attempting to use an invalid handle throws a LibgpodError.
Behavioral Deviations
The libgpod-node bindings intentionally deviate from raw libgpod to prevent data corruption and assertion failures:
| Operation | libgpod Issue | Our Fix |
|---|---|---|
removeTrack() | Doesn’t remove from playlists | Remove from all playlists first |
create() | No master playlist | Create master playlist |
initializeIpod() | Requires mountpoint to exist | Creates directory with g_mkdir_with_parents() |
clearTrackChapters() | NULL chapterdata crashes | Create empty chapterdata |
See packages/libgpod-node/README.md for detailed rationale and code examples for each deviation.
GLib Type Handling
libgpod uses GLib extensively:
| GLib Type | Complexity | Notes |
|---|---|---|
gchar* | Low | Just C strings |
gint32, guint32 | Low | Standard integers |
gboolean | Low | 0/1 integer |
GList* | Medium | Linked list, needs iteration |
GError** | Medium | Output parameter for errors |
Thread Safety
libgpod is not thread-safe. All operations on a single database must be serialized. Multiple databases can be used in parallel if they’re for different devices.
Investigating Issues
When encountering libgpod CRITICAL assertions:
- Reproduce with a test - Create an integration test that triggers the issue
- Check libgpod source - Look at
tools/libgpod-macos/build/libgpod-0.8.3/src/ - Understand the expectation - What does libgpod expect vs. what we’re providing?
- Fix and document - Apply the fix and document the deviation
Native Code Structure
The C++ native code in packages/libgpod-node/native/ is organized by concern:
| File | Purpose |
|---|---|
gpod_binding.cc | N-API module entry, database open/create/init |
database_wrapper.cc | Database class wrapping Itdb_iTunesDB |
track_operations.cc | Track add/remove/update/chapters |
playlist_operations.cc | Playlist create/add/remove |
artwork_operations.cc | Artwork thumbnail management |
gpod_converters.cc | Type conversion between N-API and GLib |
gpod_helpers.cc | Utility functions |
photo_database_wrapper.cc | Photo database operations |
See Also
- Architecture - Overall system design
- iPod Internals - iTunesDB format details
packages/libgpod-node/README.md- Full binding documentation with API reference- libgpod API Documentation