Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion backend/program/content/mdblist.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Mdblist content module"""
import json
from utils.settings import settings_manager
from utils.logger import logger
from utils.request import RateLimitExceeded, RateLimiter, get, ping
Expand Down
2 changes: 0 additions & 2 deletions backend/program/content/overseerr.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ def _validate_settings(self):
return response.ok
except ConnectTimeout:
return False
# response = json.loads(response.content)
# return response['response']

def update_items(self, media_items: MediaItemContainer):
"""Fetch media from overseerr and add them to media_items attribute
Expand Down
53 changes: 53 additions & 0 deletions backend/program/content/plex_watchlist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Plex Watchlist Module"""
from requests import ConnectTimeout
from utils.request import get, ping
from utils.logger import logger
from utils.settings import settings_manager as settings
from program.media import MediaItemContainer
from program.updaters.trakt import Updater as Trakt
import json


class Content:
"""Class for managing Plex watchlist"""

def __init__(self):
self.initialized = False
self.watchlist_url = settings.get("plex")["watchlist"]
if not self.watchlist_url or not self._validate_settings():
logger.info("Plex watchlist RSS URL is not configured and will not be used.")
return
self.updater = Trakt()
self.initialized = True

def _validate_settings(self):
try:
response = ping(
self.watchlist_url,
timeout=5,
)
return response.ok
except ConnectTimeout:
return False

def update_items(self, media_items: MediaItemContainer):
"""Fetch media from Plex watchlist and add them to media_items attribute
if they are not already there"""
logger.info("Getting items...")
watchlist_items = self._get_items_from_plex_watchlist()
container = self.updater.create_items(watchlist_items)
added_items = media_items.extend(container)
if len(added_items) > 0:
logger.info("Added %s items", len(added_items))
logger.info("Done!")

def _get_items_from_plex_watchlist(self) -> list:
"""Fetch media from Plex watchlist"""
response_obj = get(self.watchlist_url, timeout=5)
watchlist_data = json.loads(response_obj.response.content)
items = watchlist_data.get('items', [])
ids = []
for item in items:
imdb_id = next((guid.split('//')[-1] for guid in item.get('guids') if "imdb://" in guid), None)
ids.append(imdb_id)
return ids
38 changes: 17 additions & 21 deletions backend/program/libraries/plex.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,25 @@
"""Plex library module"""
import copy
import os
import time
from typing import List, Optional
from plexapi import exceptions
from plexapi.server import PlexServer
import requests
from requests.exceptions import ReadTimeout, ConnectionError
from pydantic import BaseModel, HttpUrl
from typing import List
from utils.logger import logger
from utils.settings import settings_manager as settings
from requests.exceptions import ReadTimeout, ConnectionError
from program.media import MediaItemState, MediaItem, Movie, Show, Season, Episode
import copy
import os
import time
import requests


class PlexSettings(BaseModel):
user: str
token: str
url: HttpUrl
user_watchlist_rss: Optional[str] = None

class Library:
"""Plex library class"""

def __init__(self):
# Plex class library is a necessity
while True:
try:
temp_settings = settings.get("plex")
self.plex = PlexServer(temp_settings["url"], temp_settings["token"], timeout=15)
self.settings = PlexSettings(**temp_settings)
self.settings = settings.get("plex")
self.plex = PlexServer(self.settings["url"], self.settings["token"], timeout=15)
break
except exceptions.Unauthorized:
logger.error("Wrong plex token, retrying in 2...")
Expand All @@ -54,7 +45,7 @@ def update_items(self, media_items: List[MediaItem]):
items.append(media_item)
except requests.exceptions.ReadTimeout:
logger.error(f"Timeout occurred when accessing section: {section.title}")
continue # Skip to the next section
continue

processed_sections.add(section.key)

Expand Down Expand Up @@ -233,8 +224,13 @@ def _fix_match(self, library_item: MediaItem, item: MediaItem):
return True
return False



def item_exists(self, imdb_id):
"""Check if item exists in plex library"""
for section in self.plex.library.sections():
results = section.search(guid=f"imdb://{imdb_id}")
if results:
return True
return False

def _map_item_from_data(item, item_type):
"""Map Plex API data to MediaItemContainer."""
Expand Down Expand Up @@ -278,4 +274,4 @@ def _map_item_from_data(item, item_type):
media_item_data["season_number"] = season_number
return Episode(media_item_data)
else:
return None
return None
2 changes: 0 additions & 2 deletions backend/program/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ class MediaItem:

def __init__(self, item):
self._lock = threading.Lock()
# self._state = item.get("state", MediaItemState.UNKNOWN)
self.scraped_at = 0
self.active_stream = item.get("active_stream", None)
self.streams = {}
Expand All @@ -40,7 +39,6 @@ def __init__(self, item):
self.imdb_link = f"https://www.imdb.com/title/{self.imdb_id}/"
self.aired_at = item.get("aired_at", None)
self.genres = item.get("genres", [])
self.state = MediaItemState.UNKNOWN

# Plex related
self.key = item.get("key", None)
Expand Down
25 changes: 11 additions & 14 deletions backend/program/program.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
"""Program main module"""
import importlib
import inspect
import os
import sys
import requests
from typing import Optional
from pydantic import BaseModel, HttpUrl, Field
from utils.logger import logger
from utils.settings import settings_manager
from program.media import MediaItemContainer
from program.libraries.plex import Library as Plex
from program.debrid.realdebrid import Debrid as RealDebrid
from program.scrapers.torrentio import Scraper as Torrentio
import importlib
import inspect
import os
import sys


# Pydantic models for configuration
class PlexConfig(BaseModel):
user: str
token: str
address: HttpUrl
url: HttpUrl
watchlist: Optional[HttpUrl] = None

class MdblistConfig(BaseModel):
lists: list[str] = Field(default_factory=list)
Expand All @@ -38,7 +39,6 @@ class Settings(BaseModel):
debug: bool
service_mode: bool
log: bool
menu_on_startup: bool
plex: PlexConfig
mdblist: MdblistConfig
overseerr: OverseerrConfig
Expand All @@ -63,22 +63,19 @@ def run(self):
"""Run the program"""
if self._validate_modules():
return

self.media_items.load("data/media.pkl")

self.plex.update_sections(self.media_items)

# Update content lists
for content_service in self.content_services:
content_service.update_items(self.media_items)

self.plex.update_items(self.media_items)

for scraper in self.scraping_services:
scraper.scrape(self.media_items)

self.debrid.download(self.media_items)

self.media_items.save("data/media.pkl")

def _validate_modules(self):
Expand Down
6 changes: 3 additions & 3 deletions backend/utils/default_settings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"version": "0.2.0",
"version": "0.2.1",
"debug": true,
"service_mode": false,
"log": true,
"menu_on_startup": true,
"plex": {
"user": "",
"token" : "",
"url": "http://localhost:32400"
"url": "http://localhost:32400",
"watchlist": ""
},
"mdblist": {
"lists": [
Expand Down
1 change: 1 addition & 0 deletions backend/utils/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import shutil


class SettingsManager:
"""Class that handles settings"""

Expand Down