diff --git a/src/ipod_nano7_db.py b/src/ipod_nano7_db.py index d9d7d10..3cf5253 100644 --- a/src/ipod_nano7_db.py +++ b/src/ipod_nano7_db.py @@ -32,6 +32,38 @@ logger = logging.getLogger(__name__) MASTER_CONTAINER_PID = 203092939621887772 # "iPod" master playlist LIBRARY_SCHEMA_SQL = """ +CREATE TABLE IF NOT EXISTS version_info ( + major INTEGER, minor INTEGER, device_update_level INTEGER, + platform INTEGER +); + +CREATE TABLE IF NOT EXISTS db_info ( + primary_container_pid INTEGER +); + +CREATE TABLE IF NOT EXISTS container ( + pid INTEGER PRIMARY KEY, distinguished_kind INTEGER, + is_hidden INTEGER, is_master INTEGER, media_kind INTEGER, + name TEXT, parent_pid INTEGER +); + +CREATE TABLE IF NOT EXISTS genre_map ( + pid INTEGER PRIMARY KEY, name TEXT, name_order INTEGER, + has_songs INTEGER, has_music_videos INTEGER, item_count INTEGER, + computed_has_songs INTEGER, computed_has_music_videos INTEGER, + computed_item_count INTEGER +); + +CREATE TABLE IF NOT EXISTS location_kind_map ( + pid INTEGER PRIMARY KEY, name TEXT +); + +CREATE TABLE IF NOT EXISTS category_map ( + pid INTEGER PRIMARY KEY, name TEXT, + is_protected INTEGER, is_subscribed INTEGER, + feed_url TEXT, user_pid INTEGER +); + CREATE TABLE IF NOT EXISTS item ( pid INTEGER PRIMARY KEY, media_kind INTEGER, is_song INTEGER, date_modified INTEGER, year INTEGER, is_compilation INTEGER, @@ -89,6 +121,36 @@ CREATE TABLE IF NOT EXISTS track_size_calc ( ); INSERT OR IGNORE INTO track_size_calc (kind, size) VALUES ('audio', 0); +INSERT OR IGNORE INTO version_info (major, minor, device_update_level, platform) VALUES (1, 111, 1104, 2); +INSERT OR IGNORE INTO db_info (primary_container_pid) VALUES (203092939621887772); +INSERT OR IGNORE INTO location_kind_map (pid, name) VALUES (1, 'MPEG audio file'); +INSERT OR IGNORE INTO location_kind_map (pid, name) VALUES (2, 'Purchased AAC audio file'); +INSERT OR IGNORE INTO location_kind_map (pid, name) VALUES (3, 'AAC audio file'); +INSERT OR IGNORE INTO container (pid, distinguished_kind, is_hidden, is_master, media_kind, name, parent_pid) + VALUES (203092939621887772, 0, 1, 1, 1, 'iPod', NULL); +INSERT OR IGNORE INTO container (pid, distinguished_kind, is_hidden, is_master, media_kind, name, parent_pid) + VALUES (4872745547565090077, 4, 1, 0, 1, 'Music', NULL); +""" + +DYNAMIC_SCHEMA_SQL = """ +CREATE TABLE IF NOT EXISTS item ( + item_pid INTEGER PRIMARY KEY, play_count INTEGER, + last_played_date INTEGER, skip_count INTEGER, + rating INTEGER, bookmark INTEGER +); +""" + +EXTRAS_SCHEMA_SQL = """ +CREATE TABLE IF NOT EXISTS item ( + item_pid INTEGER PRIMARY KEY, lyrics TEXT, + chapter_data BLOB, description TEXT +); +""" + +GENIUS_SCHEMA_SQL = """ +CREATE TABLE IF NOT EXISTS item ( + item_pid INTEGER PRIMARY KEY +); """ LOCATIONS_SCHEMA_SQL = """ @@ -135,21 +197,26 @@ class Nano7Database: self._ensure_schema() def _ensure_schema(self) -> None: - """Create required tables in Library.itdb and Locations.itdb if missing.""" - sqlite_path = self.sqlite_path - locations_path = self.locations_path + """Create required tables in all SQLite databases on the iPod.""" + itlp_dir = os.path.join(self.itunes_dir, "iTunes Library.itlp") + os.makedirs(itlp_dir, exist_ok=True) + os.makedirs(os.path.dirname(self.locations_path), exist_ok=True) - for db_path, schema in [(sqlite_path, LIBRARY_SCHEMA_SQL), - (locations_path, LOCATIONS_SCHEMA_SQL)]: + dbs = [ + (self.sqlite_path, LIBRARY_SCHEMA_SQL), + (self.locations_path, LOCATIONS_SCHEMA_SQL), + (os.path.join(itlp_dir, "Dynamic.itdb"), DYNAMIC_SCHEMA_SQL), + (os.path.join(itlp_dir, "Extras.itdb"), EXTRAS_SCHEMA_SQL), + (os.path.join(itlp_dir, "Genius.itdb"), GENIUS_SCHEMA_SQL), + ] + for db_path, schema in dbs: try: - os.makedirs(os.path.dirname(db_path), exist_ok=True) conn = sqlite3.connect(db_path) conn.executescript(schema) conn.commit() conn.close() - logger.info(f"Schema ensured in {os.path.basename(db_path)}") except Exception as e: - logger.warning(f"Failed to ensure schema in {db_path}: {e}") + logger.warning(f"Failed to ensure schema in {os.path.basename(db_path)}: {e}") def _detect_firewire_id(self) -> bytes: """Parse FirewireGuid from SysInfo file on the iPod."""