mirror of
https://github.com/NohamR/JustJustWatch.git
synced 2026-03-16 14:49:58 +00:00
192 lines
7.2 KiB
Python
192 lines
7.2 KiB
Python
import requests
|
|
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
|
|
from tqdm import tqdm
|
|
from tabulate import tabulate
|
|
from InquirerPy import inquirer
|
|
from InquirerPy.base.control import Choice
|
|
|
|
|
|
def country_code_to_flag(code):
|
|
return chr(127462 + ord(code[0]) - ord("A")) + chr(127462 + ord(code[1]) - ord("A"))
|
|
|
|
|
|
def format_countries(country_codes):
|
|
representative_flags = ["FR", "GB", "US", "DE"]
|
|
selected_countries = [
|
|
code for code in country_codes if code in representative_flags
|
|
]
|
|
|
|
if len(selected_countries) < len(representative_flags):
|
|
selected_countries += [
|
|
code for code in country_codes if code not in selected_countries
|
|
][: 4 - len(selected_countries)]
|
|
|
|
if len(country_codes) > len(selected_countries):
|
|
return (
|
|
", ".join(
|
|
[f"{country_code_to_flag(code)} {code}" for code in selected_countries]
|
|
)
|
|
+ f" + {len(country_codes) - len(selected_countries)} more"
|
|
)
|
|
else:
|
|
return ", ".join(
|
|
[f"{country_code_to_flag(code)} {code}" for code in selected_countries]
|
|
)
|
|
|
|
|
|
def update_url_query(url):
|
|
parsed_url = urlparse(url)
|
|
query_params = parse_qs(parsed_url.query)
|
|
keys_to_remove = [
|
|
"utm_source",
|
|
"utm_medium",
|
|
"utm_campaign",
|
|
"utm_term",
|
|
"utm_content",
|
|
]
|
|
for key in keys_to_remove:
|
|
query_params.pop(key, None)
|
|
new_query_string = urlencode(query_params, doseq=True)
|
|
return urlunparse(
|
|
(
|
|
parsed_url.scheme,
|
|
parsed_url.netloc,
|
|
parsed_url.path,
|
|
parsed_url.params,
|
|
new_query_string,
|
|
parsed_url.fragment,
|
|
)
|
|
)
|
|
|
|
|
|
def search(query):
|
|
headers = {
|
|
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
|
|
}
|
|
json_data = {
|
|
"operationName": "GetSuggestedTitles",
|
|
"variables": {
|
|
"country": "US",
|
|
"language": "en",
|
|
"first": 5,
|
|
"filter": {
|
|
"searchQuery": query,
|
|
"includeTitlesWithoutUrl": True,
|
|
},
|
|
},
|
|
"query": "query GetSuggestedTitles($country: Country!, $language: Language!, $first: Int!, $filter: TitleFilter) {\n popularTitles(country: $country, first: $first, filter: $filter) {\n edges {\n node {\n ...SuggestedTitle\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment SuggestedTitle on MovieOrShow {\n __typename\n id\n objectType\n objectId\n content(country: $country, language: $language) {\n fullPath\n title\n originalReleaseYear\n posterUrl\n __typename\n }\n watchNowOffer(country: $country, platform: WEB) {\n id\n standardWebURL\n package {\n id\n packageId\n __typename\n }\n __typename\n }\n offers(country: $country, platform: WEB) {\n monetizationType\n presentationType\n standardWebURL\n package {\n id\n packageId\n __typename\n }\n id\n __typename\n }\n}\n",
|
|
}
|
|
|
|
response = requests.post(
|
|
"https://apis.justwatch.com/graphql", headers=headers, json=json_data
|
|
).json()
|
|
choices = []
|
|
|
|
for edge in response["data"]["popularTitles"]["edges"]:
|
|
node = edge["node"]
|
|
content = node["content"]
|
|
slug = f"{content['title']} ({node['objectType']}) - {content['originalReleaseYear']}"
|
|
|
|
if content["fullPath"] == "":
|
|
slug += " - Unavailable 🚧"
|
|
|
|
choices.append(Choice(value=node, name=slug))
|
|
|
|
if not choices:
|
|
print("No results found.")
|
|
return
|
|
|
|
selected_node = inquirer.fuzzy(
|
|
message="Select a title:",
|
|
choices=choices,
|
|
match_exact=True,
|
|
).execute()
|
|
|
|
path = selected_node["content"]["fullPath"]
|
|
if path == "":
|
|
print("This title is unavailable")
|
|
else:
|
|
print(
|
|
f"Selected: {selected_node['content']['title']} - https://www.justwatch.com{path}"
|
|
)
|
|
fetchdata(path)
|
|
|
|
|
|
def fetchdata(path):
|
|
headers = {
|
|
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
|
|
}
|
|
countries = [
|
|
country["iso_3166_2"]
|
|
for country in requests.get(
|
|
"https://apis.justwatch.com/content/locales/state", headers=headers
|
|
).json()
|
|
]
|
|
|
|
print(f"Checking {len(countries)} countries...")
|
|
SERVICES = {}
|
|
IDS = []
|
|
|
|
query_str = 'query GetUrlTitleDetails($fullPath: String!, $country: Country!, $language: Language!, $episodeMaxLimit: Int, $platform: Platform! = WEB, $allowSponsoredRecommendations: SponsoredRecommendationsInput, $format: ImageFormat, $backdropProfile: BackdropProfile, $streamingChartsFilter: StreamingChartsFilter) {\n urlV2(fullPath: $fullPath) {\n node {\n ...TitleDetails\n __typename\n }\n __typename\n }\n}\n\nfragment TitleDetails on Node {\n ... on MovieOrShowOrSeason {\n plexPlayerOffers: offers(country: $country, platform: $platform, filter: {packages: ["pxp"]}) {\n id\n standardWebURL\n package { id packageId clearName technicalName shortName __typename }\n __typename\n }\n }\n}'
|
|
|
|
for country in tqdm(countries, desc="Fetching Availability"):
|
|
json_data = {
|
|
"operationName": "GetUrlTitleDetails",
|
|
"variables": {
|
|
"platform": "WEB",
|
|
"fullPath": path,
|
|
"language": "fr",
|
|
"country": country,
|
|
"episodeMaxLimit": 100,
|
|
},
|
|
"query": query_str,
|
|
}
|
|
|
|
try:
|
|
res = requests.post(
|
|
"https://apis.justwatch.com/graphql", headers=headers, json=json_data
|
|
).json()
|
|
offers = (
|
|
res.data["urlV2"]["node"].get("plexPlayerOffers", [])
|
|
if res.get("data")
|
|
else []
|
|
)
|
|
|
|
for offer in offers:
|
|
service = offer["package"]["clearName"]
|
|
url = update_url_query(offer["standardWebURL"])
|
|
pkg_id = offer["package"]["id"]
|
|
|
|
if pkg_id not in IDS:
|
|
IDS.append(pkg_id)
|
|
SERVICES[service] = {
|
|
"id": pkg_id,
|
|
"service": service,
|
|
"url": url,
|
|
"countries": [country],
|
|
}
|
|
else:
|
|
if country not in SERVICES[service]["countries"]:
|
|
SERVICES[service]["countries"].append(country)
|
|
except:
|
|
continue
|
|
|
|
if not SERVICES:
|
|
print("\nNo Plex/Free offers found globally.")
|
|
return
|
|
|
|
table_data = [
|
|
[details["service"], format_countries(details["countries"]), details["url"]]
|
|
for details in SERVICES.values()
|
|
]
|
|
print(
|
|
"\n"
|
|
+ tabulate(table_data, headers=["Service", "Countries", "URL"], tablefmt="grid")
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
user_query = inquirer.text(message="Enter the movie/show to search:").execute()
|
|
if user_query:
|
|
search(user_query)
|