Skip to content
This repository was archived by the owner on Jun 26, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
8423fb1
Update sonarr.py
ctalkington Apr 5, 2020
9f2b338
Update models.py
ctalkington Apr 5, 2020
0770b2c
Update models.py
ctalkington Apr 5, 2020
d674d3c
Update models.py
ctalkington Apr 5, 2020
8aee296
Update models.py
ctalkington Apr 5, 2020
9efe196
Update test_models.py
ctalkington Apr 5, 2020
a991eff
Create calendar.json
ctalkington Apr 5, 2020
b0a0fc9
Update test_models.py
ctalkington Apr 5, 2020
295139d
Update models.py
ctalkington Apr 5, 2020
b100e61
Update test_models.py
ctalkington Apr 5, 2020
352ba1b
Update test_models.py
ctalkington Apr 5, 2020
6c4c812
Update sonarr.py
ctalkington Apr 5, 2020
78a6fa7
Update models.py
ctalkington Apr 5, 2020
f3131da
Update test_models.py
ctalkington Apr 5, 2020
a1f242e
Update test_models.py
ctalkington Apr 5, 2020
5bd4328
Update sonarr.py
ctalkington Apr 5, 2020
aba8aae
Update models.py
ctalkington Apr 5, 2020
eaec9fc
Update models.py
ctalkington Apr 5, 2020
64bf2c2
Update models.py
ctalkington Apr 5, 2020
54df55a
Update sonarr.py
ctalkington Apr 5, 2020
4babc29
Update sonarr.py
ctalkington Apr 5, 2020
6a27e1d
Update sonarr.py
ctalkington Apr 5, 2020
9e3eee0
Update test_models.py
ctalkington Apr 5, 2020
2948beb
Update test_models.py
ctalkington Apr 5, 2020
a81e544
Update test_models.py
ctalkington Apr 5, 2020
6bce464
Update sonarr.py
ctalkington Apr 5, 2020
02dfcc7
Update test_models.py
ctalkington Apr 5, 2020
dbab772
Update test_models.py
ctalkington Apr 5, 2020
6763c0e
Update models.py
ctalkington Apr 5, 2020
07d6b3f
Update test_models.py
ctalkington Apr 5, 2020
8d50c9d
Update test_models.py
ctalkington Apr 5, 2020
dfa4afc
Update test_models.py
ctalkington Apr 5, 2020
e58f628
Update test_models.py
ctalkington Apr 5, 2020
8c9dd97
Update test_models.py
ctalkington Apr 5, 2020
db5d4e1
Update test_models.py
ctalkington Apr 5, 2020
f32bf1d
Update test_models.py
ctalkington Apr 5, 2020
f4a868d
Update test_models.py
ctalkington Apr 5, 2020
0b7c424
Update calendar.json
ctalkington Apr 5, 2020
96bb6d6
Update test_models.py
ctalkington Apr 5, 2020
91d4412
Update test_models.py
ctalkington Apr 5, 2020
460ca3e
Update test_models.py
ctalkington Apr 5, 2020
808db89
Update test_models.py
ctalkington Apr 5, 2020
38d1e80
Update test_models.py
ctalkington Apr 5, 2020
769fdcf
Update test_models.py
ctalkington Apr 5, 2020
412319e
Update test_models.py
ctalkington Apr 5, 2020
4a62bc5
Update test_models.py
ctalkington Apr 5, 2020
1a60532
Update test_models.py
ctalkington Apr 5, 2020
a0b3c5e
Update models.py
ctalkington Apr 5, 2020
67ee236
Update test_models.py
ctalkington Apr 5, 2020
d6143ae
Update test_models.py
ctalkington Apr 5, 2020
204c2a1
Update test_models.py
ctalkington Apr 5, 2020
7158e50
Update models.py
ctalkington Apr 5, 2020
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
81 changes: 79 additions & 2 deletions sonarr/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Models for DirecTV."""

from dataclasses import dataclass
from datetime import datetime
from typing import List

from .exceptions import SonarrError
Expand All @@ -26,17 +27,93 @@ def from_dict(data: dict):
)


@dataclass(frozen=True)
class Series:
"""Object holding series information from Sonarr."""

tvdb_id: int
series_id: int
series_type: str
slug: str
status: str
title: str
overview: str
network: str
runtime: int
timeslot: str
premieres: datetime
path: str
monitored: bool

@staticmethod
def from_dict(data: dict):
"""Return Series object from Sonarr API response."""
premieres = data.get("firstAired", None)
if premieres is not None:
premieres = datetime.strptime(premieres, "%Y-%m-%dT%H:%M:%S%z")

return Series(
tvdb_id=data.get("tvdbId", 0),
series_id=data.get("id", 0),
series_type=data.get("seriesType", "unknown"),
slug=data.get("titleSlug", ""),
status=data.get("status", "unknown"),
title=data.get("title", ""),
overview=data.get("overview", ""),
network=data.get("network", "Unknown"),
runtime=data.get("runtime", 0),
timeslot=data.get("airTime", ""),
premieres=premieres,
path=data.get("path", ""),
monitored=data.get("monitored", False),
)


@dataclass(frozen=True)
class Episode:
"""Object holding episode information from Sonarr."""

tvdb_id: int
episode_id: int
episode_number: int
season_number: int
title: str
overview: str
airs: datetime
downloading: bool
series: Series

@staticmethod
def from_dict(data: dict):
"""Return Episode object from Sonarr API response."""
airs = data.get("airDateUtc", None)
if airs is not None:
airs = datetime.strptime(airs, "%Y-%m-%dT%H:%M:%S%z")

return Episode(
tvdb_id=data.get("tvDbEpisodeId", 0),
episode_id=data.get("id", 0),
episode_number=data.get("episodeNumber", 0),
season_number=data.get("seasonNumber", 0),
title=data.get("title", ""),
overview=data.get("overview", ""),
airs=airs,
downloading=data.get("downloading", False),
series=Series.from_dict(data.get("series", {})),
)


@dataclass(frozen=True)
class Info:
"""Object holding information from Sonarr."""

brand: str
app_name: str
version: str

@staticmethod
def from_dict(data: dict):
"""Return Info object from Sonarr API response."""
return Info(brand="Sonarr", version=data.get("version", "Unknown"))
return Info(app_name="Sonarr", version=data.get("version", "Unknown"))


class Application:
Expand Down
22 changes: 20 additions & 2 deletions sonarr/sonarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
import asyncio
import json
from socket import gaierror as SocketGIAError
from typing import Any, Mapping, Optional
from typing import Any, List, Mapping, Optional

import aiohttp
import async_timeout
from yarl import URL

from .__version__ import __version__
from .exceptions import SonarrAccessRestricted, SonarrConnectionError, SonarrError
from .models import Application
from .models import Application, Episode


class Sonarr:
Expand Down Expand Up @@ -137,6 +137,24 @@ async def update(self, full_update: bool = False) -> Application:
self._application.update_from_dict({"diskspace": diskspace})
return self._application

async def calendar(self, start: int = None, end: int = None) -> List[Episode]:
"""Get upcoming episodes.

If start/end are not supplied, episodes airing
today and tomorrow will be returned.
"""
params = {}

if start is not None:
params["start"] = str(start)

if end is not None:
params["end"] = str(end)

results = await self._request("calendar", params=params)

return [Episode.from_dict(result) for result in results]

async def close(self) -> None:
"""Close open client session."""
if self._session and self._close_session:
Expand Down
107 changes: 107 additions & 0 deletions tests/fixtures/calendar.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
[
{
"seriesId": 3,
"episodeFileId": 0,
"seasonNumber": 4,
"episodeNumber": 11,
"title": "Easy Com-mercial, Easy Go-mercial",
"airDate": "2014-01-26",
"airDateUtc": "2014-01-27T01:30:00Z",
"overview": "To compete with fellow \"restaurateur,\" Jimmy Pesto, and his blowout Super Bowl event, Bob is determined to create a Bob's Burgers commercial to air during the \"big game.\" In an effort to outshine Pesto, the Belchers recruit Randy, a documentarian, to assist with the filmmaking and hire on former pro football star Connie Frye to be the celebrity endorser.",
"hasFile": false,
"monitored": true,
"sceneEpisodeNumber": 0,
"sceneSeasonNumber": 0,
"tvDbEpisodeId": 0,
"series": {
"tvdbId": 194031,
"tvRageId": 24607,
"imdbId": "tt1561755",
"title": "Bob's Burgers",
"cleanTitle": "bobsburgers",
"status": "continuing",
"overview": "Bob's Burgers follows a third-generation restaurateur, Bob, as he runs Bob's Burgers with the help of his wife and their three kids. Bob and his quirky family have big ideas about burgers, but fall short on service and sophistication. Despite the greasy counters, lousy location and a dearth of customers, Bob and his family are determined to make Bob's Burgers \"grand re-re-re-opening\" a success.",
"airTime": "5:30pm",
"monitored": true,
"qualityProfileId": 1,
"seasonFolder": true,
"lastInfoSync": "2014-01-26T19:25:55.4555946Z",
"runtime": 30,
"images": [
{
"coverType": "banner",
"url": "http://slurm.trakt.us/images/banners/1387.6.jpg"
},
{
"coverType": "poster",
"url": "http://slurm.trakt.us/images/posters/1387.6-300.jpg"
},
{
"coverType": "fanart",
"url": "http://slurm.trakt.us/images/fanart/1387.6.jpg"
}
],
"seriesType": "standard",
"network": "FOX",
"useSceneNumbering": false,
"titleSlug": "bobs-burgers",
"path": "T:\\Bob's Burgers",
"year": 0,
"firstAired": "2011-01-10T01:30:00Z",
"qualityProfile": {
"value": {
"name": "SD",
"allowed": [
{
"id": 1,
"name": "SDTV",
"weight": 1
},
{
"id": 8,
"name": "WEBDL-480p",
"weight": 2
},
{
"id": 2,
"name": "DVD",
"weight": 3
}
],
"cutoff": {
"id": 1,
"name": "SDTV",
"weight": 1
},
"id": 1
},
"isLoaded": true
},
"seasons": [
{
"seasonNumber": 4,
"monitored": true
},
{
"seasonNumber": 3,
"monitored": true
},
{
"seasonNumber": 2,
"monitored": true
},
{
"seasonNumber": 1,
"monitored": true
},
{
"seasonNumber": 0,
"monitored": false
}
],
"id": 66
},
"downloading": false,
"id": 14402
}
]
53 changes: 52 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests for Sonarr Models."""
import json
from datetime import datetime, timezone

import pytest
import sonarr.models as models
Expand All @@ -8,6 +9,7 @@
from . import load_fixture

INFO = json.loads(load_fixture("system-status.json"))
CALENDAR = json.loads(load_fixture("calendar.json"))
DISKSPACE = json.loads(load_fixture("diskspace.json"))
APPLICATION = {"info": INFO, "diskspace": DISKSPACE}

Expand Down Expand Up @@ -37,10 +39,32 @@ def test_info() -> None:
info = models.Info.from_dict(INFO)

assert info
assert info.brand == "Sonarr"
assert info.app_name == "Sonarr"
assert info.version == "2.0.0.1121"


def test_episode() -> None:
"""Test the Episode model."""
episode = models.Episode.from_dict(CALENDAR[0])

overview = """To compete with fellow \"restaurateur,\" Jimmy Pesto,
and his blowout Super Bowl event, Bob is determined to create a
Bob's Burgers commercial to air during the \"big game.\"
In an effort to outshine Pesto, the Belchers recruit Randy,
a documentarian, to assist with the filmmaking and hire on
former pro football star Connie Frye to be the celebrity endorser."""

assert episode
assert episode.tvdb_id == 0
assert episode.episode_id == 14402
assert episode.episode_number == 11
assert episode.season_number == 4
assert isinstance(episode.series, models.Series)
assert episode.title == "Easy Com-mercial, Easy Go-mercial"
assert episode.overview == overview.replace("\n", " ")
assert episode.airs == datetime(2014, 1, 27, 1, 30, tzinfo=timezone.utc)


def test_disk() -> None:
"""Test the Disk model."""
disk = models.Disk.from_dict(DISKSPACE[0])
Expand All @@ -50,3 +74,30 @@ def test_disk() -> None:
assert disk.label == ""
assert disk.free == 282500067328
assert disk.total == 499738734592


def test_series() -> None:
"""Test the Series model."""
series = models.Series.from_dict(CALENDAR[0]["series"])

overview = """Bob's Burgers follows a third-generation restaurateur,
Bob, as he runs Bob's Burgers with the help of his wife and their three
kids. Bob and his quirky family have big ideas about burgers, but fall
short on service and sophistication. Despite the greasy counters,
lousy location and a dearth of customers, Bob and his family are
determined to make Bob's Burgers \"grand re-re-re-opening\" a success."""

assert series
assert series.monitored
assert series.tvdb_id == 194031
assert series.series_id == 66
assert series.series_type == "standard"
assert series.status == "continuing"
assert series.slug == "bobs-burgers"
assert series.title == "Bob's Burgers"
assert series.overview == overview.replace("\n", " ")
assert series.network == "FOX"
assert series.runtime == 30
assert series.timeslot == "5:30pm"
assert series.premieres == datetime(2011, 1, 10, 1, 30, tzinfo=timezone.utc)
assert series.path == "T:\\Bob's Burgers"