Fix missing DB tables: container, version_info, db_info, genre_map, extra DBs

- Add container table with Master (iPod) and Music containers
- Add version_info, db_info, genre_map, location_kind_map, category_map
- Create Dynamic.itdb, Extras.itdb, Genius.itdb with minimal schemas
- Insert default container entries with correct PIDs
- Nano 7G firmware requires all these for music display
This commit is contained in:
Maksim Totmin 2026-05-31 18:55:20 +07:00
parent 75ea0204ff
commit 29e4cc7398

View File

@ -32,6 +32,38 @@ logger = logging.getLogger(__name__)
MASTER_CONTAINER_PID = 203092939621887772 # "iPod" master playlist MASTER_CONTAINER_PID = 203092939621887772 # "iPod" master playlist
LIBRARY_SCHEMA_SQL = """ 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 ( CREATE TABLE IF NOT EXISTS item (
pid INTEGER PRIMARY KEY, media_kind INTEGER, is_song INTEGER, pid INTEGER PRIMARY KEY, media_kind INTEGER, is_song INTEGER,
date_modified INTEGER, year INTEGER, is_compilation 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 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 = """ LOCATIONS_SCHEMA_SQL = """
@ -135,21 +197,26 @@ class Nano7Database:
self._ensure_schema() self._ensure_schema()
def _ensure_schema(self) -> None: def _ensure_schema(self) -> None:
"""Create required tables in Library.itdb and Locations.itdb if missing.""" """Create required tables in all SQLite databases on the iPod."""
sqlite_path = self.sqlite_path itlp_dir = os.path.join(self.itunes_dir, "iTunes Library.itlp")
locations_path = self.locations_path 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), dbs = [
(locations_path, LOCATIONS_SCHEMA_SQL)]: (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: try:
os.makedirs(os.path.dirname(db_path), exist_ok=True)
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
conn.executescript(schema) conn.executescript(schema)
conn.commit() conn.commit()
conn.close() conn.close()
logger.info(f"Schema ensured in {os.path.basename(db_path)}")
except Exception as e: 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: def _detect_firewire_id(self) -> bytes:
"""Parse FirewireGuid from SysInfo file on the iPod.""" """Parse FirewireGuid from SysInfo file on the iPod."""