diff --git a/.gitignore b/.gitignore index eda6bef..ac69a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ Backup-codes-series.notification.txt celerybeat-schedule databasepostgresql_env SeriesRobot.pem -certbot -imdb_api_access \ No newline at end of file +certbot \ No newline at end of file diff --git a/src/imdb_api_access/__init__.py b/src/imdb_api_access/__init__.py new file mode 100644 index 0000000..35d2270 --- /dev/null +++ b/src/imdb_api_access/__init__.py @@ -0,0 +1,2 @@ +from .series_counter import SeriesCounter +from .exceptions import * \ No newline at end of file diff --git a/src/imdb_api_access/_requests.py b/src/imdb_api_access/_requests.py new file mode 100644 index 0000000..e06f593 --- /dev/null +++ b/src/imdb_api_access/_requests.py @@ -0,0 +1,18 @@ +from requests import get +from typing import (Union, + Dict) +from .exceptions import * + + +def get_request(url: str) -> Union[StatusCodeError, APIError, MaximumUsageError, Dict]: + raw_data = get(url) + + if raw_data.status_code != 200: + raise StatusCodeError(raw_data.status_code) + data = raw_data.json() + + if data['errorMessage']: + if 'Maximum usage' in data['errorMessage']: + raise MaximumUsageError() + raise APIError(data['errorMessage']) + return data \ No newline at end of file diff --git a/src/imdb_api_access/exceptions.py b/src/imdb_api_access/exceptions.py new file mode 100644 index 0000000..16704b7 --- /dev/null +++ b/src/imdb_api_access/exceptions.py @@ -0,0 +1,23 @@ +from typing import Union + + +class StatusCodeError(Exception): + def __init__(self, status_code: Union[int, str] = 404) -> None: + self.status_code = status_code + + def __str__(self) -> str: + return f"request response status code: {self.status_code}" + +class APIError(Exception): + def __init__(self, message: str = 'Unknown Error') -> None: + self.message = message + + def __str__(self) -> str: + return f"IMDB API: {self.message}" + +class MaximumUsageError(Exception): + def __init__(self, message: str): + self.message = message + + def __str__(self) -> str: + return self.message \ No newline at end of file diff --git a/src/imdb_api_access/new_series_model.py b/src/imdb_api_access/new_series_model.py new file mode 100644 index 0000000..2ce725c --- /dev/null +++ b/src/imdb_api_access/new_series_model.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass +from series.models import SeriesModel +from typing import Optional + + +@dataclass +class NewSeries: + series: SeriesModel + new_episodes_count: Optional[int] = None + last_season: Optional[int] = None + last_episode: Optional[int] = None \ No newline at end of file diff --git a/src/imdb_api_access/series_counter.py b/src/imdb_api_access/series_counter.py new file mode 100644 index 0000000..1e92ceb --- /dev/null +++ b/src/imdb_api_access/series_counter.py @@ -0,0 +1,63 @@ +from ._requests import get_request +from datetime import datetime +from .exceptions import * +from .new_series_model import NewSeries +from series.models import SeriesModel +from typing import (List, + Tuple, + Dict, + Union) + + +class SeriesCounter: + def __init__(self, api_key: str) -> None: + self.api_key = api_key + self.new_series_list: List[NewSeries] = [] + self.error_series: List[SeriesModel] = [] + + def get_episode_count(self, series: SeriesModel, data: Dict) -> Union[StatusCodeError, APIError, MaximumUsageError, Tuple]: + # sourcery skip: aware-datetime-for-utc + now_date = datetime.strptime(datetime.strftime(datetime.utcnow(),'%d %b %Y'), '%d %b %Y') + new_episodes_count = 0 + + for n in data['tvSeriesInfo']['seasons'][data['tvSeriesInfo']['seasons'].index(str(series.last_season)):]: + data = get_request(f"https://imdb-api.com/en/API/SeasonEpisodes/{self.api_key}/{series.imdb_id}/{n}") + + episodes = data['episodes'] + for i in range(int(series.last_episode) if n == str(series.last_season) else 0, len(episodes)): + released_date = episodes[i]['released'].replace('.', '') + try: + episode_date = datetime.strptime(released_date, '%d %b %Y') + if (episode_date - now_date).days > 0: + raise ValueError + except ValueError: + try: + return new_episodes_count, int(last_n)+1, last_i+1 if new_episodes_count > 0 else 0, 0, 0 + except UnboundLocalError: + return new_episodes_count, int(n), last_i+1 if new_episodes_count > 0 else 0, 0, 0 + + last_i = i + new_episodes_count += 1 + last_n = n + + return new_episodes_count, n, i+1 if new_episodes_count > 0 else 0, 0, 0 + + def find_new_series(self, series: List[NewSeries]) -> Union[MaximumUsageError, None]: + for s in series: + try: + data = get_request(f"https://imdb-api.com/en/API/Title/{self.api_key}/{s.imdb_id}") + + except (StatusCodeError, APIError): + self.error_series.append(s) + + else: + data = self.get_episode_count(s, data) + if data[0]: + self.new_series_list.append( + NewSeries( + series=s, + new_episodes_count=data[0], + last_season=data[1], + last_episode=data[2] + ) + ) \ No newline at end of file diff --git a/src/series/templates/new_episodes.html b/src/series/templates/new_episodes.html index e9123b4..67600a7 100644 --- a/src/series/templates/new_episodes.html +++ b/src/series/templates/new_episodes.html @@ -17,31 +17,31 @@ - {% for s, d in data %} + {% for d in data %} - {{s.title}} + {{d.series.title}}
- + - + - - {{s.last_season}} - {{s.last_episode}} + + {{d.series.last_season}} - {{d.series.last_episode}} - + {{d.last_season}} - {{d.last_episode}} - {{d.count}} + {{d.new_episodes_count}} {% endfor %} diff --git a/src/series/views/new_episodes.py b/src/series/views/new_episodes.py index a763dae..d6274fa 100644 --- a/src/series/views/new_episodes.py +++ b/src/series/views/new_episodes.py @@ -3,68 +3,21 @@ from django.shortcuts import (render, from django.contrib.auth.decorators import login_required from django.contrib import messages from account.models import User -from requests import get -from datetime import datetime +from imdb_api_access import SeriesCounter +from imdb_api_access import MaximumUsageError -def get_request(request, link): - raw_data = get(link) - - if raw_data.status_code != 200: - messages.info(request, "Can't search for TV Series. Please try again.") - return redirect('homepage') - data = raw_data.json() - - if data['errorMessage']: - messages.info(request, f"IMDB API: {data['errorMessage']}") - return redirect('homepage') - return data - -def episode_counter(request, s, data): - now_date = datetime.strptime(datetime.strftime(datetime.utcnow(),'%d %b %Y'), '%d %b %Y') - new_episodes_count = 0 - - for n in data['tvSeriesInfo']['seasons'][data['tvSeriesInfo']['seasons'].index(str(s.last_season)):]: - data = get_request(request, f"https://imdb-api.com/en/API/SeasonEpisodes/{request.user.imdb_api_key}/{s.imdb_id}/{n}") - if type(data) is not dict: return data - - episodes = data['episodes'] - for i in range(int(s.last_episode) if n == str(s.last_season) else 0, len(episodes)): - released_date = episodes[i]['released'].replace('.', '') - try: - episode_date = datetime.strptime(released_date, '%d %b %Y') - if (episode_date - now_date).days > 0: - raise ValueError - except ValueError: - try: - return new_episodes_count, int(last_n)+1, last_i+1 if new_episodes_count > 0 else 0, 0, 0 - except UnboundLocalError: - return new_episodes_count, int(n), last_i+1 if new_episodes_count > 0 else 0, 0, 0 - - last_i = i - new_episodes_count += 1 - last_n = n - - return new_episodes_count, n, i+1 if new_episodes_count > 0 else 0, 0, 0 - @login_required(login_url='/account/login') def new_episodes_view(request): series = User.objects.get(id=request.user.id).series.filter(show=True).order_by('-id') - series_new_episodes = [] - for s in series: - data = get_request(request, f"https://imdb-api.com/en/API/Title/{request.user.imdb_api_key}/{s.imdb_id}") - if type(data) is not dict: return data - data = episode_counter(request, s, data) - if type(data) is not tuple: return data - if data[0]: - series_new_episodes.append([s, - {'count': data[0], - 'last_season': data[1], - 'last_episode': data[2]} - ]) + series_counter = SeriesCounter(request.user.imdb_api_key) + try: + series_counter.find_new_series(series) + except MaximumUsageError as e: ... + # messages.warning(request, f"{e} (some series could not be updated)") - if series_new_episodes: - return render(request, 'new_episodes.html', context={'data': series_new_episodes}) + if series_counter.new_series_list: + return render(request, 'new_episodes.html', context={'data': series_counter.new_series_list}) messages.warning(request, "There are no new episodes of any series :(") return redirect('homepage') \ No newline at end of file