From 1df37a2c58cb6f75f39ddcd1840cb4be185e06b1 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 11:10:53 -0400 Subject: [PATCH 01/12] Create xb.py --- cps/xb.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 cps/xb.py diff --git a/cps/xb.py b/cps/xb.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/cps/xb.py @@ -0,0 +1 @@ + From f96e0bd451a3dd6ef30062be4dce098ef360eb04 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 11:26:17 -0400 Subject: [PATCH 02/12] Define core tables for xklb db --- cps/xb.py | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/cps/xb.py b/cps/xb.py index 8b13789179..705ba5781e 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -1 +1,199 @@ +import os +from sqlalchemy import Column, Integer, String, ForeignKey, Text, create_engine, exc +from sqlalchemy.orm import sessionmaker, scoped_session, relationship +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship +from . import logger +log = logger.create() + +session = None +xklb_DB_path = None +Base = declarative_base() + +class Media(Base): + __tablename__ = 'media' + + id = Column(Integer, primary_key=True) + playlists_id = Column(Integer) + size = Column(Integer) + duration = Column(Integer) + time_created = Column(Integer) + time_modified = Column(Integer) + time_deleted = Column(Integer) + time_downloaded = Column(Integer) + fps = Column(Integer) + view_count = Column(Integer) + path = Column(String) + webpath = Column(String) + extractor_id = Column(String) + title = Column(String) + uploader = Column(String) + live_status = Column(String) + error = Column(String) + time_uploaded = Column(Integer) + width = Column(Integer) + height = Column(Integer) + type = Column(String) + video_codecs = Column(String) + audio_codecs = Column(String) + subtitle_codecs = Column(String) + video_count = Column(Integer) + audio_count = Column(Integer) + language = Column(String) + subtitle_count = Column(Integer) + download_attempts = Column(Integer) + + captions = relationship("Caption", back_populates="media") + + def __repr__(self): + return f"" + +class Caption(Base): + __tablename__ = 'captions' + + id = Column(Integer, primary_key=True, autoincrement=True) + media_id = Column(Integer, ForeignKey('media.id')) + time = Column(Integer) + text = Column(Text) + + media = relationship("Media", back_populates="captions") + + def __repr__(self): + return f"" + +class BookMediaMapping(Base): + __tablename__ = 'book_media_map' + + book_id = Column(Integer, primary_key=True) + media_id = Column(Integer, ForeignKey('media.id')) + media = relationship("Media") + +class History(Base): + __tablename__ = 'history' + + id = Column(Integer, primary_key=True, autoincrement=True) + media_id = Column(Integer, ForeignKey('media.id')) + time_played = Column(Integer) + done = Column(Boolean) + + media = relationship("Media", back_populates="history") + +class Playlists(Base): + __tablename__ = 'playlists' + + id = Column(Integer, primary_key=True) + time_modified = Column(Integer) + time_deleted = Column(Integer) + time_created = Column(Integer) + hours_update_delay = Column(Integer) + path = Column(String) + extractor_key = Column(String) + profile = Column(String) + extractor_config = Column(String) + extractor_playlist_id = Column(String) + title = Column(String) + uploader = Column(String) + +# Define the captions_fts, media_fts, playlists_fts (full-text search tables) +class CaptionsFTS(Base): + __tablename__ = 'captions_fts' + + docid = Column(Integer, primary_key=True) + text = Column(Text) + +class MediaFTS(Base): + __tablename__ = 'media_fts' + + docid = Column(Integer, primary_key=True) + title = Column(Text) + +class PlaylistsFTS(Base): + __tablename__ = 'playlists_fts' + + docid = Column(Integer, primary_key=True) + title = Column(Text) + +# Define configuration tables for FTS (captions_fts_config, media_fts_config, playlists_fts_config) +class CaptionsFTSConfig(Base): + __tablename__ = 'captions_fts_config' + + id = Column(Integer, primary_key=True) + config_value = Column(String) + +class MediaFTSConfig(Base): + __tablename__ = 'media_fts_config' + + id = Column(Integer, primary_key=True) + config_value = Column(String) + +class PlaylistsFTSConfig(Base): + __tablename__ = 'playlists_fts_config' + + id = Column(Integer, primary_key=True) + config_value = Column(String) + +# Define data tables for FTS (captions_fts_data, media_fts_data, playlists_fts_data) +class CaptionsFTSData(Base): + __tablename__ = 'captions_fts_data' + + id = Column(Integer, primary_key=True) + data = Column(Text) + +class MediaFTSData(Base): + __tablename__ = 'media_fts_data' + + id = Column(Integer, primary_key=True) + data = Column(Text) + +class PlaylistsFTSData(Base): + __tablename__ = 'playlists_fts_data' + + id = Column(Integer, primary_key=True) + data = Column(Text) + +# Define document size tables for FTS (captions_fts_docsize, media_fts_docsize, playlists_fts_docsize) +class CaptionsFTSDocSize(Base): + __tablename__ = 'captions_fts_docsize' + + id = Column(Integer, primary_key=True) + docsize = Column(Integer) + +class MediaFTSDocSize(Base): + __tablename__ = 'media_fts_docsize' + + id = Column(Integer, primary_key=True) + docsize = Column(Integer) + +class PlaylistsFTSDocSize(Base): + __tablename__ = 'playlists_fts_docsize' + + id = Column(Integer, primary_key=True) + docsize = Column(Integer) + +# Define FTS indexes (captions_fts_idx, media_fts_idx, playlists_fts_idx) +class CaptionsFTSIndex(Base): + __tablename__ = 'captions_fts_idx' + + id = Column(Integer, primary_key=True) + idx_value = Column(String) + +class MediaFTSIndex(Base): + __tablename__ = 'media_fts_idx' + + id = Column(Integer, primary_key=True) + idx_value = Column(String) + +class PlaylistsFTSIndex(Base): + __tablename__ = 'playlists_fts_idx' + + id = Column(Integer, primary_key=True) + idx_value = Column(String) + +# SQLite statistics +class SQLiteStat1(Base): + __tablename__ = 'sqlite_stat1' + + tbl = Column(String, primary_key=True) + idx = Column(String) + stat = Column(String) From 2ca806ce43e0c6c95604ae91f7e07c48c94c55ed Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 11:29:52 -0400 Subject: [PATCH 03/12] Get book ID mapped to media ID Function to get a specific book (which maps to a media entry) for a given caption --- cps/xb.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cps/xb.py b/cps/xb.py index 705ba5781e..139949fea3 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -197,3 +197,20 @@ class SQLiteStat1(Base): tbl = Column(String, primary_key=True) idx = Column(String) stat = Column(String) + +def get_book_for_caption(caption): + try: + media_entry = session.query(Caption).filter(Caption.id == caption).first() + if not media_entry: + log.error(f"No media found for caption id: {caption}") + return None + + book_entry = session.query(BookMediaMapping).filter(BookMediaMapping.media_id == media_entry.media_id).first() + if not book_entry: + log.error(f"No book mapping found for media id: {media_entry.media_id}") + return None + + return book_entry.book_id + except Exception as e: + log.error(f"Error getting book for caption: {e}") + From d5571b03ae43b8237a6a7cce0353af076c344448 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 11:32:35 -0400 Subject: [PATCH 04/12] Create blank database --- cps/xb.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cps/xb.py b/cps/xb.py index 139949fea3..1cb628da39 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -214,3 +214,7 @@ def get_book_for_caption(caption): except Exception as e: log.error(f"Error getting book for caption: {e}") +def create_blank_database(engine): + """Creates the database with the required schema.""" + Base.metadata.create_all(engine) + log.info("New blank database created.") From 58c83419388bc1052157c2c2c913b0db830fc6c1 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 11:35:18 -0400 Subject: [PATCH 05/12] Add xklb db initialization --- cps/xb.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/cps/xb.py b/cps/xb.py index 1cb628da39..54354e4861 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -218,3 +218,46 @@ def create_blank_database(engine): """Creates the database with the required schema.""" Base.metadata.create_all(engine) log.info("New blank database created.") + +def init_db(xklb_db_path): + global session, xklb_DB_path + xklb_DB_path = xklb_db_path + + engine = create_engine(f'sqlite:///{xklb_db_path}', echo=False) + Session = scoped_session(sessionmaker(bind=engine)) + session = Session() + + if not os.path.exists(xklb_db_path): + log.info(f"Database file not found at {xklb_db_path}, creating a new blank database.") + create_blank_database(engine) + else: + try: + Base.metadata.create_all(engine) + log.info(f"Database file found at {xklb_db_path}.") + except exc.SQLAlchemyError as e: + log.error(f"Error ensuring tables exist in the database: {e}") + +def dispose(): + global session + old_session = session + session = None + if old_session: + try: + old_session.close() + except Exception: + pass + if old_session.bind: + try: + old_session.bind.dispose() + except Exception: + pass + +def session_commit(success=None): + try: + session.commit() + if success: + log.info(success) + except (exc.OperationalError, exc.InvalidRequestError) as e: + session.rollback() + log.error(f"Commit failed: {e}") + From 4e1fd5db63c9f523fc3f292ee1fb500dd4a2a579 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 12:42:34 -0400 Subject: [PATCH 06/12] Add missing Boolean import --- cps/xb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/xb.py b/cps/xb.py index 54354e4861..6a47fdea4c 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -1,5 +1,5 @@ import os -from sqlalchemy import Column, Integer, String, ForeignKey, Text, create_engine, exc +from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, Text, create_engine, exc from sqlalchemy.orm import sessionmaker, scoped_session, relationship from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship From 4dc4a3704d47ba49ed6ef80098203f093ebaaec9 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 13:20:16 -0400 Subject: [PATCH 07/12] Add add_book_media_mapping function --- cps/xb.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cps/xb.py b/cps/xb.py index 6a47fdea4c..740d2e3840 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -214,6 +214,14 @@ def get_book_for_caption(caption): except Exception as e: log.error(f"Error getting book for caption: {e}") +def add_book_media_mapping(book_id, media_id): + try: + book_media_mapping = BookMediaMapping(book_id=book_id, media_id=media_id) + session.add(book_media_mapping) + session_commit(f"Book-media mapping added for book id: {book_id}, media id: {media_id}") + except Exception as e: + log.error(f"Error adding book-media mapping: {e}") + def create_blank_database(engine): """Creates the database with the required schema.""" Base.metadata.create_all(engine) From e0623958b3f4291b4778545bdd9dedefd6592d6a Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 13:22:58 -0400 Subject: [PATCH 08/12] Comment stats table --- cps/xb.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cps/xb.py b/cps/xb.py index 740d2e3840..38302f6435 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -191,12 +191,12 @@ class PlaylistsFTSIndex(Base): idx_value = Column(String) # SQLite statistics -class SQLiteStat1(Base): - __tablename__ = 'sqlite_stat1' +# class SQLiteStat1(Base): +# __tablename__ = 'sqlite_stat1' - tbl = Column(String, primary_key=True) - idx = Column(String) - stat = Column(String) +# tbl = Column(String, primary_key=True) +# idx = Column(String) +# stat = Column(String) def get_book_for_caption(caption): try: From f2c76e382aa98ba34ebfd9d86cbab38e358ac66a Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Thu, 12 Sep 2024 22:07:04 -0400 Subject: [PATCH 09/12] Add missing relationship --- cps/xb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cps/xb.py b/cps/xb.py index 38302f6435..9bc7e0d0fb 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -45,6 +45,7 @@ class Media(Base): download_attempts = Column(Integer) captions = relationship("Caption", back_populates="media") + history = relationship("History", back_populates="media") def __repr__(self): return f"" From d3cebad161d5a1e54af33a39a559b714c9980fad Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Tue, 24 Sep 2024 12:32:32 -0400 Subject: [PATCH 10/12] Simplify xklb db modeling --- cps/xb.py | 288 +++++++++++++++--------------------------------------- 1 file changed, 81 insertions(+), 207 deletions(-) diff --git a/cps/xb.py b/cps/xb.py index 9bc7e0d0fb..eee2961b80 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -1,61 +1,78 @@ import os -from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, Text, create_engine, exc -from sqlalchemy.orm import sessionmaker, scoped_session, relationship +from sqlalchemy import ( + create_engine, Column, Integer, Text, ForeignKey, Index +) from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship -from . import logger +from sqlalchemy.orm import relationship, scoped_session, sessionmaker +from sqlalchemy import exc +from sqlalchemy.sql import text -log = logger.create() - -session = None -xklb_DB_path = None Base = declarative_base() class Media(Base): __tablename__ = 'media' + __table_args__ = ( + Index('idx_media_id', 'id'), + Index('idx_media_time_deleted', 'time_deleted'), + Index('idx_media_playlists_id', 'playlists_id'), + Index('idx_media_size', 'size'), + Index('idx_media_duration', 'duration'), + Index('idx_media_time_created', 'time_created'), + Index('idx_media_time_modified', 'time_modified'), + Index('idx_media_time_downloaded', 'time_downloaded'), + Index('idx_media_fps', 'fps'), + Index('idx_media_view_count', 'view_count'), + Index('idx_media_uploader', 'uploader'), + Index('idx_media_path', 'path', unique=True), + ) id = Column(Integer, primary_key=True) + time_deleted = Column(Integer) playlists_id = Column(Integer) size = Column(Integer) duration = Column(Integer) time_created = Column(Integer) time_modified = Column(Integer) - time_deleted = Column(Integer) time_downloaded = Column(Integer) fps = Column(Integer) view_count = Column(Integer) - path = Column(String) - webpath = Column(String) - extractor_id = Column(String) - title = Column(String) - uploader = Column(String) - live_status = Column(String) - error = Column(String) + path = Column(Text) + webpath = Column(Text) + extractor_id = Column(Text) + title = Column(Text) + uploader = Column(Text) time_uploaded = Column(Integer) width = Column(Integer) height = Column(Integer) - type = Column(String) - video_codecs = Column(String) - audio_codecs = Column(String) - subtitle_codecs = Column(String) + live_status = Column(Text) + type = Column(Text) + video_codecs = Column(Text) + audio_codecs = Column(Text) + subtitle_codecs = Column(Text) + other_codecs = Column(Text) video_count = Column(Integer) audio_count = Column(Integer) - language = Column(String) + chapter_count = Column(Integer) + other_count = Column(Integer) + language = Column(Text) subtitle_count = Column(Integer) download_attempts = Column(Integer) + error = Column(Text) captions = relationship("Caption", back_populates="media") - history = relationship("History", back_populates="media") def __repr__(self): return f"" class Caption(Base): __tablename__ = 'captions' + __table_args__ = ( + Index('idx_captions_media_id', 'media_id'), + Index('idx_captions_time', 'time'), + ) - id = Column(Integer, primary_key=True, autoincrement=True) - media_id = Column(Integer, ForeignKey('media.id')) - time = Column(Integer) + media_id = Column(Integer, ForeignKey('media.id'), primary_key=True) + time = Column(Integer, primary_key=True) text = Column(Text) media = relationship("Media", back_populates="captions") @@ -63,210 +80,67 @@ class Caption(Base): def __repr__(self): return f"" -class BookMediaMapping(Base): - __tablename__ = 'book_media_map' - - book_id = Column(Integer, primary_key=True) - media_id = Column(Integer, ForeignKey('media.id')) - media = relationship("Media") - -class History(Base): - __tablename__ = 'history' - - id = Column(Integer, primary_key=True, autoincrement=True) - media_id = Column(Integer, ForeignKey('media.id')) - time_played = Column(Integer) - done = Column(Boolean) - - media = relationship("Media", back_populates="history") - class Playlists(Base): __tablename__ = 'playlists' + __table_args__ = ( + Index('idx_playlists_id', 'id'), + Index('idx_playlists_time_modified', 'time_modified'), + Index('idx_playlists_time_deleted', 'time_deleted'), + Index('idx_playlists_time_created', 'time_created'), + Index('idx_playlists_hours_update_delay', 'hours_update_delay'), + Index('idx_playlists_uploader', 'uploader'), + Index('idx_playlists_extractor_config', 'extractor_config'), + Index('idx_playlists_profile', 'profile'), + Index('idx_playlists_path', 'path', unique=True), + Index('idx_playlists_extractor_key', 'extractor_key'), + ) id = Column(Integer, primary_key=True) time_modified = Column(Integer) time_deleted = Column(Integer) time_created = Column(Integer) hours_update_delay = Column(Integer) - path = Column(String) - extractor_key = Column(String) - profile = Column(String) - extractor_config = Column(String) - extractor_playlist_id = Column(String) - title = Column(String) - uploader = Column(String) - -# Define the captions_fts, media_fts, playlists_fts (full-text search tables) -class CaptionsFTS(Base): - __tablename__ = 'captions_fts' - - docid = Column(Integer, primary_key=True) - text = Column(Text) - -class MediaFTS(Base): - __tablename__ = 'media_fts' - - docid = Column(Integer, primary_key=True) - title = Column(Text) - -class PlaylistsFTS(Base): - __tablename__ = 'playlists_fts' - - docid = Column(Integer, primary_key=True) + path = Column(Text) + extractor_key = Column(Text) + profile = Column(Text) + extractor_config = Column(Text) + extractor_playlist_id = Column(Text) title = Column(Text) + uploader = Column(Text) -# Define configuration tables for FTS (captions_fts_config, media_fts_config, playlists_fts_config) -class CaptionsFTSConfig(Base): - __tablename__ = 'captions_fts_config' - - id = Column(Integer, primary_key=True) - config_value = Column(String) - -class MediaFTSConfig(Base): - __tablename__ = 'media_fts_config' - - id = Column(Integer, primary_key=True) - config_value = Column(String) - -class PlaylistsFTSConfig(Base): - __tablename__ = 'playlists_fts_config' - - id = Column(Integer, primary_key=True) - config_value = Column(String) - -# Define data tables for FTS (captions_fts_data, media_fts_data, playlists_fts_data) -class CaptionsFTSData(Base): - __tablename__ = 'captions_fts_data' - - id = Column(Integer, primary_key=True) - data = Column(Text) - -class MediaFTSData(Base): - __tablename__ = 'media_fts_data' - - id = Column(Integer, primary_key=True) - data = Column(Text) - -class PlaylistsFTSData(Base): - __tablename__ = 'playlists_fts_data' - - id = Column(Integer, primary_key=True) - data = Column(Text) - -# Define document size tables for FTS (captions_fts_docsize, media_fts_docsize, playlists_fts_docsize) -class CaptionsFTSDocSize(Base): - __tablename__ = 'captions_fts_docsize' - - id = Column(Integer, primary_key=True) - docsize = Column(Integer) - -class MediaFTSDocSize(Base): - __tablename__ = 'media_fts_docsize' - - id = Column(Integer, primary_key=True) - docsize = Column(Integer) - -class PlaylistsFTSDocSize(Base): - __tablename__ = 'playlists_fts_docsize' - - id = Column(Integer, primary_key=True) - docsize = Column(Integer) - -# Define FTS indexes (captions_fts_idx, media_fts_idx, playlists_fts_idx) -class CaptionsFTSIndex(Base): - __tablename__ = 'captions_fts_idx' - - id = Column(Integer, primary_key=True) - idx_value = Column(String) - -class MediaFTSIndex(Base): - __tablename__ = 'media_fts_idx' - - id = Column(Integer, primary_key=True) - idx_value = Column(String) - -class PlaylistsFTSIndex(Base): - __tablename__ = 'playlists_fts_idx' - - id = Column(Integer, primary_key=True) - idx_value = Column(String) - -# SQLite statistics -# class SQLiteStat1(Base): -# __tablename__ = 'sqlite_stat1' - -# tbl = Column(String, primary_key=True) -# idx = Column(String) -# stat = Column(String) - -def get_book_for_caption(caption): - try: - media_entry = session.query(Caption).filter(Caption.id == caption).first() - if not media_entry: - log.error(f"No media found for caption id: {caption}") - return None - - book_entry = session.query(BookMediaMapping).filter(BookMediaMapping.media_id == media_entry.media_id).first() - if not book_entry: - log.error(f"No book mapping found for media id: {media_entry.media_id}") - return None - - return book_entry.book_id - except Exception as e: - log.error(f"Error getting book for caption: {e}") - -def add_book_media_mapping(book_id, media_id): - try: - book_media_mapping = BookMediaMapping(book_id=book_id, media_id=media_id) - session.add(book_media_mapping) - session_commit(f"Book-media mapping added for book id: {book_id}, media id: {media_id}") - except Exception as e: - log.error(f"Error adding book-media mapping: {e}") + def __repr__(self): + return f"" -def create_blank_database(engine): - """Creates the database with the required schema.""" - Base.metadata.create_all(engine) - log.info("New blank database created.") +# Initialize the SQLAlchemy engine and session +engine = None +SessionFactory = None +Session = None def init_db(xklb_db_path): - global session, xklb_DB_path - xklb_DB_path = xklb_db_path - + engine, SessionFactory, Session engine = create_engine(f'sqlite:///{xklb_db_path}', echo=False) - Session = scoped_session(sessionmaker(bind=engine)) - session = Session() + SessionFactory = sessionmaker(bind=engine) + Session = scoped_session(SessionFactory) if not os.path.exists(xklb_db_path): - log.info(f"Database file not found at {xklb_db_path}, creating a new blank database.") - create_blank_database(engine) + print(f"Database file not found at {xklb_db_path}, importing a new blank database.") + pass + print("New blank database imported.") else: - try: - Base.metadata.create_all(engine) - log.info(f"Database file found at {xklb_db_path}.") - except exc.SQLAlchemyError as e: - log.error(f"Error ensuring tables exist in the database: {e}") + print(f"Database file found at {xklb_db_path}.") + # Ensure that tables, indexes, FTS tables, and triggers exist + Base.metadata.create_all(engine) def dispose(): - global session - old_session = session - session = None - if old_session: - try: - old_session.close() - except Exception: - pass - if old_session.bind: - try: - old_session.bind.dispose() - except Exception: - pass + global Session + if Session: + Session.remove() -def session_commit(success=None): +def session_commit(session, success=None): try: session.commit() if success: - log.info(success) + print(success) except (exc.OperationalError, exc.InvalidRequestError) as e: session.rollback() - log.error(f"Commit failed: {e}") - + print(f"Commit failed: {e}") From b3b2c23f0685016da2bb1b517340caf6c5992805 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Tue, 24 Sep 2024 12:34:56 -0400 Subject: [PATCH 11/12] Remove unused code --- cps/xb.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cps/xb.py b/cps/xb.py index eee2961b80..bb60c3969a 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -117,7 +117,6 @@ def __repr__(self): Session = None def init_db(xklb_db_path): - engine, SessionFactory, Session engine = create_engine(f'sqlite:///{xklb_db_path}', echo=False) SessionFactory = sessionmaker(bind=engine) Session = scoped_session(SessionFactory) @@ -131,11 +130,6 @@ def init_db(xklb_db_path): # Ensure that tables, indexes, FTS tables, and triggers exist Base.metadata.create_all(engine) -def dispose(): - global Session - if Session: - Session.remove() - def session_commit(session, success=None): try: session.commit() From 1976c21e5aab8e21c6b4d5c583c920e7cdc13489 Mon Sep 17 00:00:00 2001 From: Blondel MONDESIR Date: Tue, 24 Sep 2024 22:04:52 -0400 Subject: [PATCH 12/12] Enhance session --- cps/xb.py | 82 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/cps/xb.py b/cps/xb.py index bb60c3969a..f86724b4b7 100644 --- a/cps/xb.py +++ b/cps/xb.py @@ -1,11 +1,13 @@ -import os from sqlalchemy import ( - create_engine, Column, Integer, Text, ForeignKey, Index + create_engine, Column, Integer, Text, ForeignKey, Index, exc ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, scoped_session, sessionmaker -from sqlalchemy import exc -from sqlalchemy.sql import text +from sqlalchemy.pool import StaticPool +from . import logger +from .constants import XKLB_DB_FILE + +log = logger.create() Base = declarative_base() @@ -111,30 +113,48 @@ class Playlists(Base): def __repr__(self): return f"" -# Initialize the SQLAlchemy engine and session -engine = None -SessionFactory = None -Session = None - -def init_db(xklb_db_path): - engine = create_engine(f'sqlite:///{xklb_db_path}', echo=False) - SessionFactory = sessionmaker(bind=engine) - Session = scoped_session(SessionFactory) - - if not os.path.exists(xklb_db_path): - print(f"Database file not found at {xklb_db_path}, importing a new blank database.") - pass - print("New blank database imported.") - else: - print(f"Database file found at {xklb_db_path}.") - # Ensure that tables, indexes, FTS tables, and triggers exist - Base.metadata.create_all(engine) - -def session_commit(session, success=None): - try: - session.commit() - if success: - print(success) - except (exc.OperationalError, exc.InvalidRequestError) as e: - session.rollback() - print(f"Commit failed: {e}") +class XKLBDB: + _instance = None + + def __new__(cls, XKLB_DB_FILE=XKLB_DB_FILE): + if cls._instance is None: + cls._instance = super(XKLBDB, cls).__new__(cls) + cls._instance.XKLB_DB_FILE = XKLB_DB_FILE + cls._instance._init_engine() + cls._instance._init_session_factory() + cls._instance.session = cls._instance.SessionFactory() + log.info("XKLBDB instance created with database file: %s", cls._instance.XKLB_DB_FILE) + return cls._instance + + def _init_engine(self): + self.engine = create_engine( + f'sqlite:///{XKLB_DB_FILE}', + echo=True, + connect_args={'check_same_thread': False}, + poolclass=StaticPool + ) + + def _init_session_factory(self): + self.SessionFactory = scoped_session(sessionmaker(bind=self.engine, autocommit=False, autoflush=True)) + self.session = self.SessionFactory() + + def get_session(self): + return self.session + + def session_commit(self, success=None): + try: + self.session.commit() + if success: + log.info(success) + except (exc.OperationalError, exc.InvalidRequestError) as e: + self.session.rollback() + log.error(f"Commit failed: {e}") + + def dispose(self): + if self.session: + self.session.close() + self.SessionFactory.remove() + if self.engine: + self.engine.dispose() + XKLBDB._instance = None + log.info("XKLBDB instance disposed.")