simple-justwatch-python-api 0.15__tar.gz → 0.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (14) hide show
  1. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/PKG-INFO +1 -1
  2. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/pyproject.toml +1 -1
  3. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simple_justwatch_python_api.egg-info/PKG-INFO +1 -1
  4. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simplejustwatchapi/main.py +6 -5
  5. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simplejustwatchapi/query.py +180 -0
  6. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/LICENSE +0 -0
  7. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/README.md +0 -0
  8. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/setup.cfg +0 -0
  9. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simple_justwatch_python_api.egg-info/SOURCES.txt +0 -0
  10. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simple_justwatch_python_api.egg-info/dependency_links.txt +0 -0
  11. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simple_justwatch_python_api.egg-info/requires.txt +0 -0
  12. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simple_justwatch_python_api.egg-info/top_level.txt +0 -0
  13. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simplejustwatchapi/__init__.py +0 -0
  14. {simple_justwatch_python_api-0.15 → simple_justwatch_python_api-0.16}/src/simplejustwatchapi/justwatch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simple-justwatch-python-api
3
- Version: 0.15
3
+ Version: 0.16
4
4
  Summary: A simple JustWatch Python API
5
5
  Author-email: Electronic Mango <78230210+Electronic-Mango@users.noreply.github.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "simple-justwatch-python-api"
7
7
  authors = [{name = "Electronic Mango", email = "78230210+Electronic-Mango@users.noreply.github.com"}]
8
- version = "0.15"
8
+ version = "0.16"
9
9
  description="A simple JustWatch Python API"
10
10
  readme = "README.md"
11
11
  license = {file = "LICENSE"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simple-justwatch-python-api
3
- Version: 0.15
3
+ Version: 0.16
4
4
  Summary: A simple JustWatch Python API
5
5
  Author-email: Electronic Mango <78230210+Electronic-Mango@users.noreply.github.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,11 +1,9 @@
1
+ from dataclasses import dataclass
1
2
  from itertools import product
2
3
 
3
4
  from justwatch import details, offers_for_countries, search
4
5
 
5
6
 
6
- from dataclasses import dataclass
7
-
8
-
9
7
  @dataclass
10
8
  class Base:
11
9
  base_field_1: int
@@ -22,9 +20,12 @@ def main():
22
20
  # result = details("ts4") # Breaking Bad
23
21
  # result = details("tss25") # Breaking Bad S5
24
22
  # result = details("tse411") # Breaking Bad S5E1
25
- # result = details("tm10") # The Matrix
26
- result = details("ts20711") # The Simpsons
23
+ # result = details("tm10")
24
+ # print(result) # The Matrix
25
+ # result = details("ts20711") # The Simpsons
26
+ # result = details("tm118477") # Haker (2002)
27
27
  # result = search("The Simpsons")
28
+ result = search("The Matrix", "US", "en", 3, True)
28
29
  print(result)
29
30
  print()
30
31
 
@@ -100,6 +100,40 @@ fragment TitleDetails on MovieOrShow {
100
100
  backdropUrl
101
101
  __typename
102
102
  }
103
+ ageCertification
104
+ scoring {
105
+ imdbScore
106
+ imdbVotes
107
+ tmdbPopularity
108
+ tmdbScore
109
+ tomatoMeter
110
+ certifiedFresh
111
+ jwRating
112
+ __typename
113
+ }
114
+ interactions {
115
+ likelistAdditions
116
+ dislikelistAdditions
117
+ __typename
118
+ }
119
+ __typename
120
+ }
121
+ streamingCharts(country: $country) {
122
+ edges {
123
+ streamingChartInfo {
124
+ rank
125
+ trend
126
+ trendDifference
127
+ daysInTop3
128
+ daysInTop10
129
+ daysInTop100
130
+ daysInTop1000
131
+ topRank
132
+ updatedAt
133
+ __typename
134
+ }
135
+ __typename
136
+ }
103
137
  __typename
104
138
  }
105
139
  offers(country: $country, platform: WEB, filter: $filter) {
@@ -224,6 +258,72 @@ class Offer(NamedTuple):
224
258
  """List of 2-letter language codes of available audio tracks, e.g. ``["en", "pt", "de"]``."""
225
259
 
226
260
 
261
+ class Scoring(NamedTuple):
262
+ """Parsed data related to user scoring for a single entry."""
263
+
264
+ imdb_score: float | None
265
+ """IMDB score."""
266
+
267
+ imdb_votes: int | None
268
+ """Number of votes on IMDB."""
269
+
270
+ tmdb_popularity: float | None
271
+ """TMDB popularity score."""
272
+
273
+ tmdb_score: float | None
274
+ """TMDB score."""
275
+
276
+ tomatometer: int | None
277
+ """Tomatometer score on Rotten Tomatoes."""
278
+
279
+ certified_fresh: bool | None
280
+ """Flag whether entry has "Certified Fresh" seal on Rotten Tomatoes."""
281
+
282
+ jw_rating: float | None
283
+ """JustWatch rating."""
284
+
285
+
286
+ class Interactions(NamedTuple):
287
+ """Parsed data regarding number of likes and dislikes on JustWatch for a single entry."""
288
+
289
+ likes: int | None
290
+ """Number of likes on JustWatch."""
291
+
292
+ dislikes: int | None
293
+ """Number of dislikes on JustWatch."""
294
+
295
+
296
+ class StreamingCharts(NamedTuple):
297
+ """Parsed data related to JustWatch rank for a single entry."""
298
+
299
+ rank: int
300
+ """Rank on JustWatch."""
301
+
302
+ trend: str
303
+ """Trend in ranking on JustWatch, ``UP``, ``DOWN``, ``STABLE``."""
304
+
305
+ trend_difference: int
306
+ """Difference in rank; related to trend."""
307
+
308
+ top_rank: int
309
+ """Top rank ever reached."""
310
+
311
+ days_in_top_3: int
312
+ """Number of days in top 3 ranks."""
313
+
314
+ days_in_top_10: int
315
+ """Number of days in top 10 ranks."""
316
+
317
+ days_in_top_100: int
318
+ """Number of days in top 100 ranks."""
319
+
320
+ days_in_top_1000: int
321
+ """Number of days in top 1000 ranks."""
322
+
323
+ updated: str
324
+ """Date when rank data was last updated as a string, e.g.: ``2024-10-06T09:20:36.397Z``."""
325
+
326
+
227
327
  class MediaEntry(NamedTuple):
228
328
  """Parsed response from JustWatch GraphQL API for "GetSearchTitles" query for single entry."""
229
329
 
@@ -269,6 +369,18 @@ class MediaEntry(NamedTuple):
269
369
  backdrops: list[str]
270
370
  """List of URLs for backdrops (full screen images to use as background)."""
271
371
 
372
+ age_certification: str | None
373
+ """Age rating as a string, e.g.: "R", "TV-14"."""
374
+
375
+ scoring: Scoring | None
376
+ """Scoring data."""
377
+
378
+ interactions: Interactions | None
379
+ """Interactions (likes/dislikes) data."""
380
+
381
+ streaming_charts: StreamingCharts | None
382
+ """JustWatch charts/ranks data."""
383
+
272
384
  offers: list[Offer]
273
385
  """List of available offers for this entry, empty if there are no available offers."""
274
386
 
@@ -477,6 +589,10 @@ def _parse_entry(json: any) -> MediaEntry:
477
589
  poster_url_field = content.get("posterUrl")
478
590
  poster = _IMAGES_URL + poster_url_field if poster_url_field else None
479
591
  backdrops = [_IMAGES_URL + bd.get("backdropUrl") for bd in content.get("backdrops", []) if bd]
592
+ age_certification = content.get("ageCertification")
593
+ scoring = _parse_scores(content.get("scoring"))
594
+ interactions = _parse_interactions(content.get("interactions"))
595
+ streaming_charts = _parse_streaming_charts(json)
480
596
  offers = [_parse_offer(offer) for offer in json.get("offers", []) if offer]
481
597
  return MediaEntry(
482
598
  entry_id,
@@ -493,10 +609,74 @@ def _parse_entry(json: any) -> MediaEntry:
493
609
  tmdb_id,
494
610
  poster,
495
611
  backdrops,
612
+ age_certification,
613
+ scoring,
614
+ interactions,
615
+ streaming_charts,
496
616
  offers,
497
617
  )
498
618
 
499
619
 
620
+ def _parse_scores(json: any) -> Scoring | None:
621
+ if not json:
622
+ return None
623
+ imdb_score = json.get("imdbScore")
624
+ imdb_votes = json.get("imdbVotes")
625
+ tmdb_popularity = json.get("tmdbPopularity")
626
+ tmdb_score = json.get("tmdbScore")
627
+ tomatometer = json.get("tomatoMeter")
628
+ certified_fresh = json.get("certifiedFresh")
629
+ jw_rating = json.get("jwRating")
630
+ return Scoring(
631
+ imdb_score,
632
+ int(imdb_votes) if imdb_votes is not None else None,
633
+ tmdb_popularity,
634
+ tmdb_score,
635
+ int(tomatometer) if tomatometer is not None else None,
636
+ certified_fresh,
637
+ jw_rating,
638
+ )
639
+
640
+
641
+ def _parse_interactions(json: any) -> Interactions | None:
642
+ if not json:
643
+ return None
644
+ likes = json.get("likelistAdditions")
645
+ dislikes = json.get("dislikelistAdditions")
646
+ return Interactions(likes, dislikes)
647
+
648
+
649
+ def _parse_streaming_charts(json: any) -> StreamingCharts | None:
650
+ if (
651
+ not (streaming_chart_info := json.get("streamingCharts", {}).get("edges"))
652
+ or not (streaming_chart_info := streaming_chart_info[0].get("streamingChartInfo"))
653
+ # Getting final info is awkward, I think this in general can return a list when searching
654
+ # for ranks for multiple entries. In this case, to unify searching and displaying details,
655
+ # it's always getting single element in a list.
656
+ ):
657
+ return None
658
+ rank = streaming_chart_info.get("rank")
659
+ trend = streaming_chart_info.get("trend")
660
+ trend_difference = streaming_chart_info.get("trendDifference")
661
+ top_rank = streaming_chart_info.get("topRank")
662
+ days_in_top_3 = streaming_chart_info.get("daysInTop3")
663
+ days_in_top_10 = streaming_chart_info.get("daysInTop10")
664
+ days_in_top_100 = streaming_chart_info.get("daysInTop100")
665
+ days_in_top_1000 = streaming_chart_info.get("daysInTop1000")
666
+ updated = streaming_chart_info.get("updatedAt")
667
+ return StreamingCharts(
668
+ rank,
669
+ trend,
670
+ trend_difference,
671
+ top_rank,
672
+ days_in_top_3,
673
+ days_in_top_10,
674
+ days_in_top_100,
675
+ days_in_top_1000,
676
+ updated,
677
+ )
678
+
679
+
500
680
  def _parse_offer(json: any) -> Offer:
501
681
  id = json.get("id")
502
682
  monetization_type = json.get("monetizationType")