Fbrowser/dbman.py
Stan44 98ee9ce50a feat: Implement database integration
This commit introduces database integration using FireflyDB for storing and retrieving audio file metadata.

- Integrated FireflyDB for persistent storage of metadata.
- Added methods to check for existing metadata in the database and retrieve it.
- Modified the Organizer class to use FireflyDB for processing metadata.
- Added auto-scanning and metadata extraction upon directory opening in Fbrowser.
- Created archiver.py and metaextract.py to house the ArchiveExtractor and MetadataExtractor classes respectively.
- Added .gitignore entries for Firefly related files.
- Added Mock MIT License, Contributor License Agreement, and Pro Edition License Agreement files.
2025-04-11 22:30:39 -05:00

242 lines
8.9 KiB
Python

from datetime import datetime
from ifireflylib import IFireflyClient as FireflyDatabase
class FireflyDB:
def __init__(self):
# Initialize with default values
self.use_db = False
self.db = None
self.artists = set()
self.albums = set()
self.genres = set()
self.years = set()
def connect_to(
self, use_db=False, db_host="localhost", db_port=6379, db_password=None
):
# Set the use_db flag
self.use_db = use_db
# Metadata organization
self.artists = set()
self.albums = set()
self.genres = set()
self.years = set()
if use_db:
try:
print(f"Connecting to FireflyDB at {db_host}:{db_port}")
self.db = FireflyDatabase(
host=db_host, port=db_port, password=db_password
)
# Test connection
if not self.db.ping():
print(
"Warning: Could not connect to FireflyDB. Continuing without database."
)
self.use_db = False
self.db = None
else:
print("Successfully connected to FireflyDB")
# Store connection timestamp
timestamp = str(datetime.now())
print(f"Setting last_connection timestamp: {timestamp}")
self.db.string_ops.string_set("last_connection", timestamp)
print("Connection timestamp stored successfully")
except Exception as e:
print(f"Error connecting to FireflyDB: {e}")
import traceback
traceback.print_exc()
self.use_db = False
self.db = None
def close(self):
"""Close database connection when done"""
if self.db:
self.db.close()
self.db = None
def has_metadata_in_db(self, file_path):
"""Check if metadata for a file already exists in the database"""
if not self.use_db or not self.db:
return False
try:
key = f"audio:{file_path}"
# Use hash_exists to check if the key exists in the database
exists = self.db.hash_ops.hash_exists(key, "title")
return exists
except Exception as e:
print(f"Error checking metadata in database: {e}")
return False
def store_metadata(self, metadata):
"""Store audio file metadata in the database"""
if not self.use_db or not self.db:
print("Database usage is disabled, not storing metadata")
return False
try:
# Use the file path as a unique key
file_path = metadata["file_path"]
key = f"audio:{file_path}"
# Log the storage attempt
print(f"Storing metadata for {file_path} in FireflyDB")
print(f"Metadata fields: {list(metadata.keys())}")
# Store as a hash with all metadata fields
success = True
for field, value in metadata.items():
if field != "file_path": # Skip using file_path as a field
print(f" Setting field {field}={value}")
# Use the direct hash_set method instead of hash_ops
result = self.db.hash_ops.hash_set(key, field, value)
if not result:
print(
f" Failed to store field {field} for {file_path}"
)
success = False
else:
print(f" Successfully stored field {field}")
# Add to index lists for quick lookup
if "artist" in metadata and metadata["artist"]:
artist_key = f"index:artist:{metadata['artist']}"
print(f" Adding to artist index: {artist_key}")
self.db.list_ops.list_right_push(artist_key, file_path)
if "album" in metadata and metadata["album"]:
album_key = f"index:album:{metadata['album']}"
print(f" Adding to album index: {album_key}")
self.db.list_ops.list_right_push(album_key, file_path)
if "genre" in metadata and metadata["genre"]:
genre_key = f"index:genre:{metadata['genre']}"
print(f" Adding to genre index: {genre_key}")
self.db.list_ops.list_right_push(genre_key, file_path)
# Add to a master list of all audio files for easy retrieval
print(" Adding to master audio files list")
self.db.list_ops.list_right_push("all_audio_files", file_path)
# Store timestamp of when metadata was added
self.db.hash_ops.hash_set(key, "timestamp", str(datetime.now()))
# Verify storage by retrieving one field
if "title" in metadata:
retrieved_title = self.db.hash_ops.hash_get(key, "title")
print(f" Verification - Retrieved title: {retrieved_title}")
if retrieved_title != metadata["title"]:
print(
f" Verification failed: expected '{metadata['title']}', got '{retrieved_title}'"
)
success = False
print(
f"Metadata storage {'successful' if success else 'partially failed'} for {file_path}"
)
return success
except Exception as e:
print(f"Error storing metadata in database: {e}")
import traceback
traceback.print_exc()
return False
def get_metadata_from_db(self, file_path):
"""Retrieve metadata for a file from the database"""
if not self.use_db or not self.db:
return None
try:
key = f"audio:{file_path}"
metadata = self.db.hash_ops.hash_get_all(key)
if metadata:
# Add the file path to the metadata
metadata["file_path"] = file_path
return metadata
return None
except Exception as e:
print(f"Error retrieving metadata from database: {e}")
return None
def search_by_artist(self, artist):
"""Search for files by artist"""
if not self.use_db or not self.db:
return []
try:
key = f"index:artist:{artist}"
return self.db.list_range(key, 0, -1)
except Exception as e:
print(f"Error searching by artist: {e}")
return []
# Similar methods for album and genre searches
def process_metadata(self, metadata):
"""Process extracted metadata"""
if "artist" in metadata and metadata["artist"]:
self.artists.add(metadata["artist"])
if "album" in metadata and metadata["album"]:
self.albums.add(metadata["album"])
if "genre" in metadata and metadata["genre"]:
self.genres.add(metadata["genre"])
if "year" in metadata and metadata["year"]:
self.years.add(metadata["year"])
# Store in database if enabled
if self.use_db and self.db:
db_success = self.store_metadata(metadata)
if db_success:
print(
f"Successfully stored metadata for {metadata.get('file_path', 'unknown file')} in database"
)
else:
print(
f"Failed to store metadata for {metadata.get('file_path', 'unknown file')} in database"
)
def verify_database_connection(self):
"""Verify that the database connection is working properly"""
if not self.use_db or not self.db:
print("Database usage is disabled")
return False
try:
# Test ping
ping_result = self.db.ping()
if not ping_result:
print("Database ping failed")
return False
# Test basic operations
test_key = "test:connection"
test_value = "connection_test"
# Test string operations
string_set_result = self.db.string_set(test_key, test_value)
if not string_set_result:
print("Failed to set test string in database")
return False
string_get_result = self.db.string_get(test_key)
if string_get_result != test_value:
print(
f"String get test failed. Expected '{test_value}', got '{string_get_result}'"
)
return False
# Clean up
self.db.delete(test_key)
print("Database connection verified successfully")
return True
except Exception as e:
print(f"Database verification failed with error: {e}")
return False