Skip to content

Commit

Permalink
fix: merged changes with db fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dreulavelle committed Jul 31, 2024
1 parent 6047b96 commit f3103b6
Show file tree
Hide file tree
Showing 21 changed files with 582 additions and 370 deletions.
File renamed without changes
File renamed without changes
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ clean:
@rm -rf data/alembic/
@rm -rf data/*.db

hard_reset:
hard_reset: clean
@poetry run python src/main.py --hard_reset_db

install:
Expand Down
70 changes: 69 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ sqlalchemy = "^2.0.31"
sqla-wrapper = "^6.0.0"
alembic = "^1.13.2"
psycopg2-binary = "^2.9.9"
apprise = "^1.8.1"

[tool.poetry.group.dev.dependencies]
pyright = "^1.1.352"
Expand Down
15 changes: 15 additions & 0 deletions src/controllers/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from fastapi import APIRouter, Request
from program.media.item import MediaItem
from program.symlink import Symlinker
from utils.logger import logger

router = APIRouter(
Expand All @@ -20,3 +21,17 @@ async def request(request: Request, imdb_id: str) -> Dict[str, Any]:
return {"success": False, "message": "Failed to create item from imdb_id"}

return {"success": True, "message": f"Added {imdb_id} to queue"}

@router.delete("/symlink/{_id}")
async def remove_symlink(request: Request, _id: int) -> Dict[str, Any]:
try:
symlinker: Symlinker = request.app.program.services[Symlinker]
if symlinker.delete_item_symlinks(_id):
logger.log("API", f"Removed symlink(s) for item with id: {_id}")
return {"success": True, "message": f"Removed symlink(s) for item with id: {_id}"}
else:
logger.error(f"Failed to remove symlink for item with id: {_id}")
return {"success": False, "message": "Failed to remove symlink"}
except Exception as e:
logger.error(f"Failed to remove symlink for item with id: {_id}, error: {e}")
return {"success": False, "message": "Failed to remove symlink", "error": str(e)}
107 changes: 24 additions & 83 deletions src/controllers/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,15 @@ async def get_items(
)

if type:
type_lower = type.lower()
if type_lower not in ["movie", "show", "season", "episode"]:
raise HTTPException(
status_code=400,
detail=f"Invalid type: {type}. Valid types are: ['movie', 'show', 'season', 'episode']",
)
query = query.where(MediaItem.type == type_lower)
if "," in type:
types = type.split(",")
for type in types:
if type not in ["movie", "show", "season", "episode"]:
raise HTTPException(
status_code=400,
detail=f"Invalid type: {type}. Valid types are: ['movie', 'show', 'season', 'episode']",
)
query = query.where(MediaItem.type.in_(types))

if sort and not search:
if sort.lower() == "asc":
Expand Down Expand Up @@ -117,26 +119,24 @@ async def get_items(
@router.get("/extended/{item_id}")
async def get_extended_item_info(_: Request, item_id: str):
with db.Session() as session:
item = DB._get_item_from_db(session, MediaItem({"imdb_id":str(item_id)}))
item = session.execute(select(MediaItem).where(MediaItem.imdb_id == item_id)).unique().scalar_one_or_none()
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return {"success": True, "item": item.to_extended_dict()}


@router.post("/add/imdb/{imdb_id}")
@router.post("/add/imdb/")
@router.post("/add")
async def add_items(
request: Request, imdb_id: Optional[str] = None, imdb_ids: Optional[IMDbIDs] = None
request: Request, imdb_ids: str = None
):
if imdb_id:
imdb_ids = IMDbIDs(imdb_ids=[imdb_id])
elif (
not imdb_ids or not imdb_ids.imdb_ids or any(not id for id in imdb_ids.imdb_ids)
):

if not imdb_ids:
raise HTTPException(status_code=400, detail="No IMDb ID(s) provided")

ids = imdb_ids.split(",")

valid_ids = []
for id in imdb_ids.imdb_ids:
for id in ids:
if not id.startswith("tt"):
logger.warning(f"Invalid IMDb ID {id}, skipping")
else:
Expand All @@ -152,54 +152,15 @@ async def add_items(
return {"success": True, "message": f"Added {len(valid_ids)} item(s) to the queue"}


@router.delete("/remove/")
@router.delete("/remove")
async def remove_item(
request: Request, item_id: Optional[str] = None, imdb_id: Optional[str] = None
_: Request, imdb_id: str
):
if item_id:
item = request.app.program.media_items.get(item_id)
id_type = "ID"
elif imdb_id:
item = next(
(i for i in request.app.program.media_items if i.imdb_id == imdb_id), None
)
id_type = "IMDb ID"
else:
raise HTTPException(status_code=400, detail="No item ID or IMDb ID provided")

if not item:
logger.error(f"Item with {id_type} {item_id or imdb_id} not found")
return {
"success": False,
"message": f"Item with {id_type} {item_id or imdb_id} not found. No action taken.",
}

try:
# Remove the item from the media items container
request.app.program.media_items.remove([item])
logger.log("API", f"Removed item with {id_type} {item_id or imdb_id}")

# Remove the symlinks associated with the item
symlinker = request.app.program.service[Symlinker]
symlinker.delete_item_symlinks(item)
logger.log(
"API", f"Removed symlink for item with {id_type} {item_id or imdb_id}"
)

# Save and reload the media items to ensure consistency
symlinker.save_and_reload_media_items(request.app.program.media_items)
logger.log(
"API",
f"Saved and reloaded media items after removing item with {id_type} {item_id or imdb_id}",
)

return {
"success": True,
"message": f"Successfully removed item with {id_type} {item_id or imdb_id}.",
}
except Exception as e:
logger.error(f"Failed to remove item with {id_type} {item_id or imdb_id}: {e}")
raise HTTPException from e(status_code=500, detail="Internal server error")
if not imdb_id:
raise HTTPException(status_code=400, detail="No IMDb ID provided")
if DB._remove_item_from_db(imdb_id):
return {"success": True, "message": f"Removed item with imdb_id {imdb_id}"}
return {"success": False, "message": f"No item with imdb_id ({imdb_id}) found"}


@router.get("/imdb/{imdb_id}")
Expand Down Expand Up @@ -238,23 +199,3 @@ async def get_imdb_info(
raise HTTPException(status_code=404, detail="Item not found")

return {"success": True, "item": item.to_extended_dict()}


@router.get("/incomplete")
async def get_incomplete_items(request: Request):
if not hasattr(request.app, "program"):
logger.error("Program not found in the request app")
raise HTTPException(status_code=500, detail="Internal server error")

with db.Session() as session:
incomplete_items = session.execute(
select(MediaItem).where(MediaItem.last_state != "Completed")
).unique().scalars().all()

if not incomplete_items:
return {"success": True, "incomplete_items": []}

return {
"success": True,
"incomplete_items": [item.to_dict() for item in incomplete_items],
}
31 changes: 0 additions & 31 deletions src/controllers/metrics.py

This file was deleted.

23 changes: 13 additions & 10 deletions src/program/content/plex_watchlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def _get_items_from_rss(self) -> Generator[MediaItem, None, None]:
guid_id = guid_text.split("//")[-1] if guid_text else ""
if not guid_id or guid_id in self.recurring_items:
continue
if guid_id.startswith("tt") and guid_id not in self.recurring_items:
if guid_id and guid_id.startswith("tt") and guid_id not in self.recurring_items:
yield guid_id
elif guid_id:
imdb_id: str = get_imdbid_from_tvdb(guid_id)
Expand All @@ -122,16 +122,19 @@ def _get_items_from_watchlist(self) -> Generator[MediaItem, None, None]:
# response = get(url)
items = self.account.watchlist()
for item in items:
if hasattr(item, "guids") and item.guids:
imdb_id = next((guid.id.split("//")[-1] for guid in item.guids if guid.id.startswith("imdb://")), None)
if imdb_id and imdb_id in self.recurring_items:
continue
elif imdb_id.startswith("tt"):
yield imdb_id
try:
if hasattr(item, "guids") and item.guids:
imdb_id: str = next((guid.id.split("//")[-1] for guid in item.guids if guid.id.startswith("imdb://")), "")
if not imdb_id or imdb_id in self.recurring_items:
continue
elif imdb_id.startswith("tt"):
yield imdb_id
else:
logger.log("NOT_FOUND", f"Unable to extract IMDb ID from {item.title} ({item.year}) with data id: {imdb_id}")
else:
logger.log("NOT_FOUND", f"Unable to extract IMDb ID from {item.title} ({item.year}) with data id: {imdb_id}")
else:
logger.log("NOT_FOUND", f"{item.title} ({item.year}) is missing guids attribute from Plex")
logger.log("NOT_FOUND", f"{item.title} ({item.year}) is missing guids attribute from Plex")
except Exception as e:
logger.error(f"An unexpected error occurred while fetching Plex watchlist item {item.log_string}: {e}")

@staticmethod
def _ratingkey_to_imdbid(ratingKey: str) -> str:
Expand Down
16 changes: 12 additions & 4 deletions src/program/db/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from program.settings.manager import settings_manager
from sqla_wrapper import Alembic, SQLAlchemy
from utils import data_dir_path
from utils.logger import logger

db = SQLAlchemy(settings_manager.settings.database.host)

Expand All @@ -20,17 +21,24 @@

# https://stackoverflow.com/questions/61374525/how-do-i-check-if-alembic-migrations-need-to-be-generated
def need_upgrade_check() -> bool:
diff = []
"""Check if there are any pending migrations."""
with db.engine.connect() as connection:
mc = MigrationContext.configure(connection)
diff = compare_metadata(mc, db.Model.metadata)
return diff != []
return bool(diff)


def run_migrations() -> None:
"""Run Alembic migrations if needed."""
try:
if need_upgrade_check():
logger.info("New migrations detected, creating revision...")
alembic.revision("auto-upg")
logger.info("Applying migrations...")
alembic.upgrade()
except Exception as _:
alembic.upgrade()
else:
logger.info("No new migrations detected.")
except Exception as e:
logger.error(f"Error during migration: {e}")
logger.info("Attempting to apply existing migrations...")
alembic.upgrade()
Loading

0 comments on commit f3103b6

Please sign in to comment.