|
|
|
|
@@ -13,6 +13,7 @@ from sqlalchemy import func
|
|
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
|
|
|
from sqlalchemy.future import delete
|
|
|
|
|
from sqlalchemy.future import select
|
|
|
|
|
from sqlalchemy.orm import registry
|
|
|
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
|
@@ -49,99 +50,95 @@ async def drop_db() -> None:
|
|
|
|
|
await logger.error(f"Failed to drop tables: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def insert_or_update_track(track_data: dict[str, Any]) -> None:
|
|
|
|
|
async def insert_or_update_track(
|
|
|
|
|
session: AsyncSession, track_data: dict[str, Any]
|
|
|
|
|
) -> None:
|
|
|
|
|
"""Insert a new track or update an existing one in the database asynchronously."""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
genre = await session.get(models.Genre, track_data.get("genre_id"))
|
|
|
|
|
if not genre:
|
|
|
|
|
genre = models.Genre(name=track_data["genre_name"])
|
|
|
|
|
session.add(genre)
|
|
|
|
|
await session.flush() # Ensures 'genre' is persisted and has an 'id'
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
genre = await session.get(models.Genre, track_data.get("genre_id"))
|
|
|
|
|
if not genre:
|
|
|
|
|
genre = models.Genre(name=track_data["genre_name"])
|
|
|
|
|
session.add(genre)
|
|
|
|
|
await session.flush() # Ensures 'genre' is persisted and has an 'id'
|
|
|
|
|
|
|
|
|
|
track = await session.get(models.Track, track_data.get("id"))
|
|
|
|
|
if track:
|
|
|
|
|
for key, value in track_data.items():
|
|
|
|
|
setattr(track, key, value)
|
|
|
|
|
await logger.info(f"Updated track: {track.title}")
|
|
|
|
|
else:
|
|
|
|
|
track = models.Track(**track_data)
|
|
|
|
|
session.add(track)
|
|
|
|
|
await logger.info(f"Inserted new track: {track.title}")
|
|
|
|
|
track = await session.get(models.Track, track_data.get("id"))
|
|
|
|
|
if track:
|
|
|
|
|
for key, value in track_data.items():
|
|
|
|
|
setattr(track, key, value)
|
|
|
|
|
await logger.info(f"Updated track: {track.title}")
|
|
|
|
|
else:
|
|
|
|
|
track = models.Track(**track_data)
|
|
|
|
|
session.add(track)
|
|
|
|
|
await logger.info(f"Inserted new track: {track.title}")
|
|
|
|
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
await logger.error(f"Database error in insert_or_update_track: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
await logger.error(f"Unexpected error in insert_or_update_track: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
await session.commit()
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
await logger.error(f"Database error in insert_or_update_track: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
await logger.error(f"Unexpected error in insert_or_update_track: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def cleanup_unused_genres() -> None:
|
|
|
|
|
async def cleanup_unused_genres(session: AsyncSession) -> None:
|
|
|
|
|
"""Remove genres that are no longer used by any tracks."""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
stmt = (
|
|
|
|
|
select(models.Genre)
|
|
|
|
|
.outerjoin(models.Track)
|
|
|
|
|
.filter(models.Track.id is None)
|
|
|
|
|
)
|
|
|
|
|
result = await session.execute(stmt)
|
|
|
|
|
unused_genres = result.scalars().all()
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
stmt = (
|
|
|
|
|
select(models.Genre)
|
|
|
|
|
.outerjoin(models.Track)
|
|
|
|
|
.filter(models.Track.id is None)
|
|
|
|
|
)
|
|
|
|
|
result = await session.execute(stmt)
|
|
|
|
|
unused_genres = result.scalars().all()
|
|
|
|
|
|
|
|
|
|
for genre in unused_genres:
|
|
|
|
|
await session.delete(genre)
|
|
|
|
|
for genre in unused_genres:
|
|
|
|
|
await session.delete(genre)
|
|
|
|
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
await logger.info(f"Cleaned up {len(unused_genres)} unused genres.")
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
await logger.error(f"Database error in cleanup_unused_genres: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
await logger.error(f"Unexpected error in cleanup_unused_genres: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
await session.commit()
|
|
|
|
|
await logger.info(f"Cleaned up {len(unused_genres)} unused genres.")
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
await logger.error(f"Database error in cleanup_unused_genres: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
await logger.error(f"Unexpected error in cleanup_unused_genres: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def update_statistics_timestamps(operation: str) -> None:
|
|
|
|
|
async def update_statistics_timestamps(session: AsyncSession, operation: str) -> None:
|
|
|
|
|
"""Update the start or end timestamps in the statistics model based on the specified operation.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The database session.
|
|
|
|
|
operation (str): A string to specify whether to update 'start' or 'end' timestamp.
|
|
|
|
|
"""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
stats = await session.get(models.Statistics, 1)
|
|
|
|
|
stats_obj = stats.scalars().first()
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
stats = await session.get(models.Statistics, 1)
|
|
|
|
|
stats_obj = stats.scalars().first()
|
|
|
|
|
|
|
|
|
|
if operation == "start":
|
|
|
|
|
stats_obj.track_update_start_timestamp = datetime.now()
|
|
|
|
|
stats_obj.track_update_end_timestamp = None
|
|
|
|
|
log_message = "Statistics start timestamp updated."
|
|
|
|
|
elif operation == "end":
|
|
|
|
|
stats_obj.track_update_end_timestamp = datetime.now()
|
|
|
|
|
log_message = "Statistics end timestamp updated."
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Invalid operation specified for statistics timestamp update."
|
|
|
|
|
)
|
|
|
|
|
if operation == "start":
|
|
|
|
|
stats_obj.track_update_start_timestamp = datetime.now()
|
|
|
|
|
stats_obj.track_update_end_timestamp = None
|
|
|
|
|
log_message = "Statistics start timestamp updated."
|
|
|
|
|
elif operation == "end":
|
|
|
|
|
stats_obj.track_update_end_timestamp = datetime.now()
|
|
|
|
|
log_message = "Statistics end timestamp updated."
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Invalid operation specified for statistics timestamp update."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
await logger.info(log_message)
|
|
|
|
|
await session.commit()
|
|
|
|
|
await logger.info(log_message)
|
|
|
|
|
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
await logger.error(
|
|
|
|
|
f"Database error during statistics timestamp update: {e}"
|
|
|
|
|
)
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
await logger.error(
|
|
|
|
|
f"Unexpected error during statistics timestamp update: {e}"
|
|
|
|
|
)
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
await logger.error(f"Database error during statistics timestamp update: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
await logger.error(f"Unexpected error during statistics timestamp update: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def add_new_holiday(
|
|
|
|
|
@@ -217,6 +214,7 @@ async def remove_holiday_by_id(session: AsyncSession, holiday_id: int) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def update_statistics(
|
|
|
|
|
session: AsyncSession,
|
|
|
|
|
new_average_duration: float,
|
|
|
|
|
new_podcast_length: float,
|
|
|
|
|
new_max_playtime: float,
|
|
|
|
|
@@ -237,6 +235,7 @@ async def update_statistics(
|
|
|
|
|
"""Update the statistics model.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The database session.
|
|
|
|
|
new_average_duration (float): The new average duration of a track by geometric mean
|
|
|
|
|
of all track durations as a float.
|
|
|
|
|
new_podcast_length (float): The new total podcast length of all current episodes as a float.
|
|
|
|
|
@@ -255,53 +254,52 @@ async def update_statistics(
|
|
|
|
|
new_max_holiday_favorite_base (int): The new max holiday favorite base unit as an int.
|
|
|
|
|
new_max_holiday_general_base (int): The new max holiday general base unit as an int.
|
|
|
|
|
"""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
# Assuming there's only one statistics record, or you might need to handle this differently
|
|
|
|
|
stats = await session.get(models.Statistics, 1)
|
|
|
|
|
if stats:
|
|
|
|
|
stats.average_track_length = new_average_duration
|
|
|
|
|
stats.total_podcast_length = new_podcast_length
|
|
|
|
|
stats.max_playtime = new_max_playtime
|
|
|
|
|
stats.category_count = new_category_count
|
|
|
|
|
stats.active_holiday_count = new_active_holidays
|
|
|
|
|
stats.max_tracks_per_day = new_max_tracks
|
|
|
|
|
stats.max_regular_tracks = new_max_regular
|
|
|
|
|
stats.max_holiday_tracks = new_max_holiday
|
|
|
|
|
stats.max_regular_favorite_tracks = new_max_regular_favorite
|
|
|
|
|
stats.max_regular_general_tracks = new_max_regular_general
|
|
|
|
|
stats.max_holiday_favorite_tracks = new_max_holiday_favorite
|
|
|
|
|
stats.max_holiday_general_tracks = new_max_holiday_general
|
|
|
|
|
stats.max_regular_favorite_base = new_max_regular_favorite_base
|
|
|
|
|
stats.max_regular_general_base = new_max_regular_general_base
|
|
|
|
|
stats.max_holiday_favorite_base = new_max_holiday_favorite_base
|
|
|
|
|
stats.max_holiday_general_base = new_max_holiday_general_base
|
|
|
|
|
await session.commit()
|
|
|
|
|
else:
|
|
|
|
|
# If no statistics entry exists, create one
|
|
|
|
|
new_stats = models.Statistics(
|
|
|
|
|
average_track_length=new_average_duration,
|
|
|
|
|
total_podcast_length=new_podcast_length,
|
|
|
|
|
max_playtime=new_max_playtime,
|
|
|
|
|
active_holiday_count=new_active_holidays,
|
|
|
|
|
max_tracks_per_day=new_max_tracks,
|
|
|
|
|
max_regular_tracks=new_max_regular,
|
|
|
|
|
max_holiday_tracks=new_max_holiday,
|
|
|
|
|
max_regular_favorite_tracks=new_max_regular_favorite,
|
|
|
|
|
max_regular_general_tracks=new_max_regular_general,
|
|
|
|
|
max_holiday_favorite_tracks=new_max_holiday_favorite,
|
|
|
|
|
max_holiday_general_tracks=new_max_holiday_general,
|
|
|
|
|
max_regular_favorite_base=new_max_regular_favorite_base,
|
|
|
|
|
max_regular_general_base=new_max_regular_general_base,
|
|
|
|
|
max_holiday_favorite_base=new_max_holiday_favorite_base,
|
|
|
|
|
max_holiday_general_base=new_max_holiday_general_base,
|
|
|
|
|
)
|
|
|
|
|
session.add(new_stats)
|
|
|
|
|
await session.commit()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to update the statistics model: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
try:
|
|
|
|
|
async with session.begin():
|
|
|
|
|
# Assuming there's only one statistics record, or you might need to handle this differently
|
|
|
|
|
stats = await session.get(models.Statistics, 1)
|
|
|
|
|
if stats:
|
|
|
|
|
stats.average_track_length = new_average_duration
|
|
|
|
|
stats.total_podcast_length = new_podcast_length
|
|
|
|
|
stats.max_playtime = new_max_playtime
|
|
|
|
|
stats.category_count = new_category_count
|
|
|
|
|
stats.active_holiday_count = new_active_holidays
|
|
|
|
|
stats.max_tracks_per_day = new_max_tracks
|
|
|
|
|
stats.max_regular_tracks = new_max_regular
|
|
|
|
|
stats.max_holiday_tracks = new_max_holiday
|
|
|
|
|
stats.max_regular_favorite_tracks = new_max_regular_favorite
|
|
|
|
|
stats.max_regular_general_tracks = new_max_regular_general
|
|
|
|
|
stats.max_holiday_favorite_tracks = new_max_holiday_favorite
|
|
|
|
|
stats.max_holiday_general_tracks = new_max_holiday_general
|
|
|
|
|
stats.max_regular_favorite_base = new_max_regular_favorite_base
|
|
|
|
|
stats.max_regular_general_base = new_max_regular_general_base
|
|
|
|
|
stats.max_holiday_favorite_base = new_max_holiday_favorite_base
|
|
|
|
|
stats.max_holiday_general_base = new_max_holiday_general_base
|
|
|
|
|
await session.commit()
|
|
|
|
|
else:
|
|
|
|
|
# If no statistics entry exists, create one
|
|
|
|
|
new_stats = models.Statistics(
|
|
|
|
|
average_track_length=new_average_duration,
|
|
|
|
|
total_podcast_length=new_podcast_length,
|
|
|
|
|
max_playtime=new_max_playtime,
|
|
|
|
|
active_holiday_count=new_active_holidays,
|
|
|
|
|
max_tracks_per_day=new_max_tracks,
|
|
|
|
|
max_regular_tracks=new_max_regular,
|
|
|
|
|
max_holiday_tracks=new_max_holiday,
|
|
|
|
|
max_regular_favorite_tracks=new_max_regular_favorite,
|
|
|
|
|
max_regular_general_tracks=new_max_regular_general,
|
|
|
|
|
max_holiday_favorite_tracks=new_max_holiday_favorite,
|
|
|
|
|
max_holiday_general_tracks=new_max_holiday_general,
|
|
|
|
|
max_regular_favorite_base=new_max_regular_favorite_base,
|
|
|
|
|
max_regular_general_base=new_max_regular_general_base,
|
|
|
|
|
max_holiday_favorite_base=new_max_holiday_favorite_base,
|
|
|
|
|
max_holiday_general_base=new_max_holiday_general_base,
|
|
|
|
|
)
|
|
|
|
|
session.add(new_stats)
|
|
|
|
|
await session.commit()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to update the statistics model: {e}")
|
|
|
|
|
await session.rollback()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def fetch_tracks_for_sublist(
|
|
|
|
|
@@ -413,62 +411,122 @@ async def insert_into_playlist( # noqa: C901
|
|
|
|
|
await session.rollback()
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
async def gen_all_category_ids() -> AsyncGenerator[int, None, None]:
|
|
|
|
|
"""Generate the current category ids from the database.
|
|
|
|
|
|
|
|
|
|
Yields:
|
|
|
|
|
int: The category id.
|
|
|
|
|
"""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
query = select(models.Category)
|
|
|
|
|
result = session.execute(query)
|
|
|
|
|
for row in result.scalars():
|
|
|
|
|
yield row.id
|
|
|
|
|
async def gen_all_category_ids(
|
|
|
|
|
session: AsyncSession,
|
|
|
|
|
) -> AsyncGenerator[int, None, None]:
|
|
|
|
|
"""Generate the current category ids from the database.
|
|
|
|
|
|
|
|
|
|
async def gen_active_holiday_ids() -> AsyncGenerator[int, None, None]:
|
|
|
|
|
"""Generate the current active holiday ids from the database.
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The database session.
|
|
|
|
|
|
|
|
|
|
Yields:
|
|
|
|
|
int: The holiday id.
|
|
|
|
|
"""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
query = select(models.Holiday).where(models.Holiday.is_active is True)
|
|
|
|
|
result = session.execute(query)
|
|
|
|
|
for row in result.scalars():
|
|
|
|
|
yield row.id
|
|
|
|
|
Yields:
|
|
|
|
|
int: The category id.
|
|
|
|
|
"""
|
|
|
|
|
query = select(models.Category)
|
|
|
|
|
result = session.execute(query)
|
|
|
|
|
for row in result.scalars():
|
|
|
|
|
yield row.id
|
|
|
|
|
|
|
|
|
|
async def get_existing_playlist_track_info() -> (
|
|
|
|
|
tuple[set[int], dict[tuple[bool, int, bool, enums.Sublist], int]]
|
|
|
|
|
):
|
|
|
|
|
"""Get the existing playlist track information.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple[set[int], dict[tuple[bool, int, bool, enums.Sublist], int]]: Two pieces:
|
|
|
|
|
First is a set of the ids of all of the tracks in the playlist. Second is a
|
|
|
|
|
dict with the key being the combination of is_category, type_id, is_favorite,
|
|
|
|
|
and sublist; the value is the count of that unique key combination.
|
|
|
|
|
"""
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
# TODO: Set up user-specific query here.
|
|
|
|
|
query = select(models.PlaylistTrack).where(
|
|
|
|
|
models.PlaylistTrack.episode_id is None
|
|
|
|
|
)
|
|
|
|
|
result = session.execute(query)
|
|
|
|
|
ids = set()
|
|
|
|
|
data = []
|
|
|
|
|
for row in result.scalars():
|
|
|
|
|
if row.episode_id is not None:
|
|
|
|
|
continue
|
|
|
|
|
ids.append(row.track_id)
|
|
|
|
|
is_category = row.category_id is not None
|
|
|
|
|
type_id = row.category_id if is_category else row.holiday_id
|
|
|
|
|
is_favorite = row.is_favorite
|
|
|
|
|
sublist = row.sublist
|
|
|
|
|
data.append((is_category, type_id, is_favorite, sublist))
|
|
|
|
|
data_counts = Counter(data)
|
|
|
|
|
return ids, data_counts
|
|
|
|
|
async def gen_active_holiday_ids(
|
|
|
|
|
session: AsyncSession,
|
|
|
|
|
) -> AsyncGenerator[int, None, None]:
|
|
|
|
|
"""Generate the current active holiday ids from the database.
|
|
|
|
|
|
|
|
|
|
async def get_statistics() -> models.Statistics:
|
|
|
|
|
stats = await session.get(models.Statistics, 1)
|
|
|
|
|
stats_obj = stats.scalars().first()
|
|
|
|
|
return stats_obj
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The database session.
|
|
|
|
|
|
|
|
|
|
Yields:
|
|
|
|
|
int: The holiday id.
|
|
|
|
|
"""
|
|
|
|
|
query = select(models.Holiday).where(models.Holiday.is_active is True)
|
|
|
|
|
result = session.execute(query)
|
|
|
|
|
for row in result.scalars():
|
|
|
|
|
yield row.id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_existing_playlist_track_info(
|
|
|
|
|
session: AsyncSession,
|
|
|
|
|
) -> tuple[set[int], dict[tuple[bool, int, bool, enums.Sublist], int]]:
|
|
|
|
|
"""Get the existing playlist track information.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The database session.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple[set[int], dict[tuple[bool, int, bool, enums.Sublist], int]]: Two pieces:
|
|
|
|
|
First is a set of the ids of all of the tracks in the playlist. Second is a
|
|
|
|
|
dict with the key being the combination of is_category, type_id, is_favorite,
|
|
|
|
|
and sublist; the value is the count of that unique key combination.
|
|
|
|
|
"""
|
|
|
|
|
# TODO: Set up user-specific query here.
|
|
|
|
|
query = select(models.PlaylistTrack).where(models.PlaylistTrack.episode_id is None)
|
|
|
|
|
result = session.execute(query)
|
|
|
|
|
ids = set()
|
|
|
|
|
data = []
|
|
|
|
|
for row in result.scalars():
|
|
|
|
|
if row.episode_id is not None:
|
|
|
|
|
continue
|
|
|
|
|
ids.append(row.track_id)
|
|
|
|
|
is_category = row.category_id is not None
|
|
|
|
|
type_id = row.category_id if is_category else row.holiday_id
|
|
|
|
|
is_favorite = row.is_favorite
|
|
|
|
|
sublist = row.sublist
|
|
|
|
|
data.append((is_category, type_id, is_favorite, sublist))
|
|
|
|
|
data_counts = Counter(data)
|
|
|
|
|
return ids, data_counts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_statistics(session: AsyncSession) -> models.Statistics:
|
|
|
|
|
"""Get the statistics object for the database.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The database session.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
models.Statistics: The statistics object used for playlist processing.
|
|
|
|
|
"""
|
|
|
|
|
stats = await session.get(models.Statistics, 1)
|
|
|
|
|
stats_obj = stats.scalars().first()
|
|
|
|
|
return stats_obj
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def remove_track_from_playlist(session: AsyncSession, server_id: str):
|
|
|
|
|
"""Directly removes all entries from PlaylistTrack that are associated with a given server_id.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
session (AsyncSession): The current database session.
|
|
|
|
|
server_id (str): The unique server ID of the track to remove from the playlist.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int: Number of rows affected by the delete operation.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# Delete directly using a join on Track where server_id matches
|
|
|
|
|
delete_query = delete(models.PlaylistTrack).where(
|
|
|
|
|
models.PlaylistTrack.track_id
|
|
|
|
|
== select(models.Track.id)
|
|
|
|
|
.where(models.Track.server_id == server_id)
|
|
|
|
|
.scalar_subquery()
|
|
|
|
|
)
|
|
|
|
|
result = await session.execute(delete_query)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
affected_rows = result.rowcount
|
|
|
|
|
logger.info(
|
|
|
|
|
f"Removed {affected_rows} entries from PlaylistTrack for server_id {server_id}."
|
|
|
|
|
)
|
|
|
|
|
return affected_rows
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
logger.error(
|
|
|
|
|
f"SQLAlchemy error occurred while removing tracks from playlist: {e}"
|
|
|
|
|
)
|
|
|
|
|
await session.rollback()
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(
|
|
|
|
|
f"Unexpected error occurred while removing tracks from playlist: {e}"
|
|
|
|
|
)
|
|
|
|
|
await session.rollback()
|
|
|
|
|
raise
|
|
|
|
|
|