Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JustWatch integration #100

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
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
27 changes: 23 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- "develop"

jobs:
ci:
unit_tests:
runs-on: ubuntu-latest
permissions:
# Gives the action the necessary permissions for publishing new
Expand All @@ -36,16 +36,35 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest coverage
pip install pytest pytest-mock coverage
pip install -r requirements.txt
- name: Test with pytest
run: |
coverage run -m pytest
coverage run -m pytest -m "not integration"
coverage report
coverage xml
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

integration_tests:
needs: unit_tests
runs-on: ubuntu-latest
steps:
# Purges github badge cache
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python 3
uses: actions/setup-python@v3
with:
python-version: "3"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-mock
pip install -r requirements.txt
- name: Test with pytest
run: |
python -m pytest -m integration
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,7 @@ pyrightconfig.json
pyvenv.cfg
pip-selfcheck.json

# End of https://www.toptal.com/developers/gitignore/api/python,virtualenv
# End of https://www.toptal.com/developers/gitignore/api/python,virtualenv

# Exception for app/deleterr/scripts
!/app/scripts/
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ test:
coverage run -m pytest
coverage report
coverage xml

unit:
coverage run -m pytest -m "not integration"
coverage report
coverage xml

integration:
coverage run -m pytest -m integration
coverage report
coverage xml
20 changes: 19 additions & 1 deletion app/deleterr.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,29 @@ def main():
default="/config/settings.yaml",
help="Path to the config file",
)
args = parser.parse_args()
parser.add_argument(
"--jw-providers", action="store_true", help="Gather JustWatch providers"
)

args, unknown = parser.parse_known_args()

config = load_config(args.config)
config.validate()

# If providers flag is set, gather JustWatch providers and exit
if args.jw_providers:
from app.scripts.justwatch_providers import gather_providers

providers = gather_providers(
config.settings.get("trakt", {}).get("client_id"),
config.settings.get("trakt", {}).get("client_secret"),
)

print(providers)
logger.info("# of Trakt Providers: " + str(len(providers)))

return

Deleterr(config)


Expand Down
51 changes: 51 additions & 0 deletions app/modules/justwatch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from simplejustwatchapi.justwatch import search

from app import logger


class JustWatch:
def __init__(self, country, language):
self.country = country
self.language = language
logger.debug(
"JustWatch instance created with country: %s and language: %s",
country,
language,
)

"""
Search for a title on JustWatch API
Returns:
[MediaEntry(entry_id='ts8', object_id=8, object_type='SHOW', title='Better Call Saul', url='https://justwatch.com/pt/serie/better-call-saul', release_year=2015, release_date='2015-02-08', runtime_minutes=50, short_description='Six years before Saul Goodman meets Walter White. We meet him when the man who will become Saul Goodman is known as Jimmy McGill, a small-time lawyer searching for his destiny, and, more immediately, hustling to make ends meet. Working alongside, and, often, against Jimmy, is “fixer” Mike Ehrmantraut. The series tracks Jimmy’s transformation into Saul Goodman, the man who puts “criminal” in “criminal lawyer".', genres=['crm', 'drm'], imdb_id='tt3032476', poster='https://images.justwatch.com/poster/269897858/s718/better-call-saul.jpg', backdrops=['https://images.justwatch.com/backdrop/171468199/s1920/better-call-saul.jpg', 'https://images.justwatch.com/backdrop/269897860/s1920/better-call-saul.jpg', 'https://images.justwatch.com/backdrop/302946702/s1920/better-call-saul.jpg', 'https://images.justwatch.com/backdrop/304447863/s1920/better-call-saul.jpg', 'https://images.justwatch.com/backdrop/273394969/s1920/better-call-saul.jpg'], offers=[Offer(id='b2Z8dHM4OlBUOjg6ZmxhdHJhdGU6NGs=', monetization_type='FLATRATE', presentation_type='_4K', price_string=None, price_value=None, price_currency='EUR', last_change_retail_price_value=None, type='AGGREGATED', package=OfferPackage(id='cGF8OA==', package_id=8, name='Netflix', technical_name='netflix', icon='https://images.justwatch.com/icon/207360008/s100/netflix.png'), url='http://www.netflix.com/title/80021955', element_count=6, available_to=None, deeplink_roku='launch/12?contentID=80021955&MediaType=show', subtitle_languages=[], video_technology=[], audio_technology=[], audio_languages=[])])]
"""

def _search(self, title, max_results=5, detailed=False):
return search(title, self.country, self.language, max_results, detailed)

def search_by_title_and_year(self, title, year, media_type):
results = self._search(title)
for entry in results:
if entry.title == title and entry.release_year == year:
return entry
return None

def available_on(self, title, year, media_type, providers):
result = self.search_by_title_and_year(title, year, media_type)
if not result:
logger.debug("No results found for title: {title}")
return False

if "any" in providers and result.offers:
logger.debug("Title {title} available on any provider")
return True

for provider in providers:
for offer in result.offers:
if offer.package.technical_name == provider.lower():
logger.debug("Title {title} available on {provider}")
return True

return False

def is_not_available_on(self, title, year, media_type, providers):
return not self.available_on(title, year, media_type, providers)
3 changes: 0 additions & 3 deletions app/modules/tautulli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ def __init__(self, url, api_key):
def test_connection(self):
self.api.status()

def get_last_episode_activity(self, library_config, section):
return self.get_activity(library_config, section)

def refresh_library(self, section_id):
self.api.get_library_media_info(section_id=section_id, refresh=True)

Expand Down
Empty file added app/scripts/__init__.py
Empty file.
70 changes: 70 additions & 0 deletions app/scripts/justwatch_providers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from app.modules.justwatch import JustWatch
from app.modules.trakt import Trakt


def gather_providers(trakt_id, trakt_secret):
# Create a Trakt instance
trakt = Trakt(
trakt_id,
trakt_secret,
)

# Get the most popular shows
shows = trakt.get_all_items_for_url(
"show",
{
"max_items_per_list": 200,
"lists": [
"https://trakt.tv/shows/trending",
"https://trakt.tv/shows/popular",
"https://trakt.tv/shows/watched/yearly",
"https://trakt.tv/shows/collected/yearly",
],
},
)

# List of country codes to check providers for
countries = [
"US",
"BR",
"NG",
"IN",
"CN",
"RU",
"AU",
"PT",
"FR",
"DE",
"ES",
"IT",
"JP",
"KR",
"GB",
]

# Create a set to store the providers
providers = set()

# Iterate over the countries
for country in countries:
# Create a JustWatch instance for the current country
justwatch = JustWatch(country, "en")

# Iterate shows and collect all the different providers in a set
for show in shows:
try:
title = shows[show]["trakt"].title
year = shows[show]["trakt"].year
result = justwatch.search_by_title_and_year(title, year, "show")
if result:
for offer in result.offers:
providers.add(offer.package.technical_name)
except AttributeError:
# Skip if the show doesn't have a title or year
continue
except TypeError:
# There is a null error inside justwatch library, this is a workaround
continue

# Print the providers
return providers
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ PyYAML==6.0.1
requests==2.31.0
tautulli==3.7.0.2120
trakt.py==4.4.0
pytest-mock
simple-justwatch-python-api==0.14
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sonar.python.version=3

# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
sonar.sources=app/
sonar.tests.exclusions=tests/**
sonar.exclusions=/app/scripts/**
sonar.tests=tests/
sonar.language=python

Expand Down
Loading
Loading