Created
June 12, 2018 10:58
-
-
Save afraca/a2ae4086bc6b1a11aca63ef86f313ed6 to your computer and use it in GitHub Desktop.
Python (typed) script to move your IMDb rating history to Netflix upvotes/downvotes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from selenium import webdriver | |
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile | |
from selenium.webdriver.remote.webelement import WebElement | |
from selenium.common.exceptions import * | |
from selenium.webdriver.firefox.options import Options | |
import getpass | |
from typing import Dict, Tuple, List | |
import os | |
import csv | |
import re | |
from difflib import SequenceMatcher | |
import time | |
import json | |
import click | |
# These type aliases makes the dictionary passing easier to understand | |
ImdbId = str | |
ImdbTitle = str | |
ImdbRating = int | |
# Netflix renders quickly enough, but finishes very slowly | |
DRIVER_TIMEOUT = 15 | |
NETFLIX_LOGIN_URL = 'https://www.netflix.com/nl-en/login' | |
# IMDB will set something (url?) required up in the "pre-login page" | |
IMDB_PRE_LOGIN_URL = 'https://www.imdb.com/registration/signin' | |
IMDB_LOGIN_URL = 'https://www.imdb.com/ap/signin?clientContext=133-8770098-9132729&openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Fwww.imdb.com%2Fap-signin-handler&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=imdb_us&openid.mode=checkid_setup&siteState=eyJvcGVuaWQuYXNzb2NfaGFuZGxlIjoiaW1kYl91cyIsInJlZGlyZWN0VG8iOiJodHRwczovL3d3dy5pbWRiLmNvbS8_cmVmXz1sb2dpbiJ9&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&&tag=imdbtag_reg-20 ' | |
IMDB_TEMP_FILE_NAME = 'ratings.csv' | |
RESULTS_FILE = 'results.json' | |
RESULTS = {} | |
# Poor man's ENUM | |
RESULT_FOUND_VOTED_UP = 0 | |
RESULT_FOUND_VOTED_DOWN = 1 | |
RESULT_NOT_FOUND = 2 | |
RESULTS_SHOULD_BE_IGNORED = False | |
def get_driver(headful: bool = False) -> webdriver.Firefox: | |
options = Options() | |
if not headful: | |
options.headless = True | |
profile = FirefoxProfile() | |
profile.set_preference("browser.download.folderList", 2) | |
profile.set_preference("browser.download.manager.showWhenStarting", False) | |
profile.set_preference("browser.download.dir", os.getcwd()) | |
profile.set_preference("browser.helperApps.neverAsk.saveToDisk", 'text/csv') | |
driver = webdriver.Firefox(firefox_options=options, firefox_profile=profile) | |
driver.set_page_load_timeout(DRIVER_TIMEOUT) | |
return driver | |
################################################################################################### | |
def should_update_ratings() -> bool: | |
answer = input('(Re-)download imdb ratings? (Y/N) (default: N):') | |
if answer == 'Y': | |
return True | |
if answer == 'N' or answer == '': | |
return False | |
# Repeat question on invalid input | |
return should_update_ratings() | |
def login_to_imdb(driver: webdriver.Firefox, username: str, password: str): | |
# As stated on global value, IMDB does something weird in login flow, so we need the 'pre-login' visit | |
driver.get(IMDB_PRE_LOGIN_URL) | |
login_button_elem = driver.find_element_by_partial_link_text('Sign in with IMDb') | |
login_button_elem.click() | |
user_elem = driver.find_element_by_id('ap_email') | |
user_elem.send_keys(username) | |
pass_elem = driver.find_element_by_id('ap_password') | |
pass_elem.send_keys(password) | |
submit = driver.find_element_by_id('signInSubmit') | |
submit.click() | |
def fetch_history(driver: webdriver.Firefox, download_csv: bool, user_id: str = None) -> \ | |
Dict[Tuple[ImdbId, ImdbTitle], ImdbRating]: | |
if download_csv: | |
if user_id is None: | |
raise ValueError('user_id for imdb fetching is required') | |
if os.path.exists(IMDB_TEMP_FILE_NAME): | |
remove_history_file() | |
history_url = 'https://imdb.com/user/{}/ratings/export'.format(user_id) | |
# Download finishes quick, but somehow we never register an 'end', | |
# so just set timeout and continue if file is there | |
driver.set_page_load_timeout(5) | |
try: | |
driver.get(history_url) | |
except TimeoutException: | |
if not os.path.exists(IMDB_TEMP_FILE_NAME): | |
raise | |
driver.set_page_load_timeout(DRIVER_TIMEOUT) | |
return read_history_file_to_dict() | |
def read_history_file_to_dict() -> Dict[Tuple[ImdbId, ImdbTitle], ImdbRating]: | |
ratings = {} | |
with open(IMDB_TEMP_FILE_NAME, newline='') as csvfile: | |
reader = csv.reader(csvfile, delimiter=',') | |
# skip header | |
next(reader, None) | |
for row in reader: | |
try: | |
imdb_id, title, rating = csv_row_to_dict_entry(row) | |
ratings[(imdb_id, title)] = rating | |
except ValueError as e: | |
print(e, row[0]) | |
print('Movie-To-Rating mapping created') | |
return ratings | |
def csv_row_to_dict_entry(row: List) -> Tuple[ImdbId, ImdbTitle, ImdbRating]: | |
if len(row) < 10: | |
raise ValueError('csv malformed') | |
if row[5] != 'movie': | |
raise ValueError('Not a movie') | |
return str(row[0]), str(row[3]), int(row[1]) | |
def remove_history_file(): | |
print('Removing ratings file') | |
os.remove(IMDB_TEMP_FILE_NAME) | |
################################################################################################### | |
def login_to_netflix(driver: webdriver.Firefox, username: str, password: str): | |
driver.get(NETFLIX_LOGIN_URL) | |
user_elem = driver.find_element_by_id('email') | |
user_elem.send_keys(username) | |
pass_elem = driver.find_element_by_id('password') | |
pass_elem.send_keys(password) | |
submit = driver.find_element_by_css_selector('.btn.login-button.btn-submit') | |
submit.click() | |
print('Logged in to Netflix') | |
def choose_netflix_profile(driver: webdriver.Firefox, profile_id: str): | |
print('Choosing netflix profile.') | |
driver.get('https://www.netflix.com/SwitchProfile?tkn={}'.format(profile_id)) | |
def vote_with_mapping(driver: webdriver.Firefox, mapping: Dict[Tuple[ImdbId, ImdbTitle], ImdbRating], | |
ignore_results: bool = False): | |
for (imdb_id, title), rating in mapping.items(): | |
if not should_try_vote(imdb_id, rating, ignore_results): | |
print('No netflix action required for {}'.format(title)) | |
continue | |
try: | |
driver.get('https://netflix.com/search?q={}'.format(title)) | |
except TimeoutException: | |
RESULTS[imdb_id] = RESULT_NOT_FOUND | |
print('Searched timed out for {}'.format(title)) | |
continue | |
links_for_query = driver.find_elements_by_xpath('//a[@href][@aria-label]') | |
# Last results are bad, exclude them, speeds it up | |
if len(links_for_query) > 25: | |
links_for_query = links_for_query[:25] | |
ids_with_similarity = ids_with_similarity_all_links(links_for_query, title) | |
if len(ids_with_similarity) == 0: | |
print('No results found for {}'.format(title)) | |
RESULTS[imdb_id] = RESULT_NOT_FOUND | |
continue | |
best_matching_id = max(ids_with_similarity, key=lambda key: ids_with_similarity[key]) | |
if ids_with_similarity[best_matching_id] < 0.9: | |
print('Could not find similar enough match for {}'.format(title)) | |
RESULTS[imdb_id] = RESULT_NOT_FOUND | |
if ids_with_similarity[best_matching_id] > 0.75: | |
print('(But it was really similar!...)') | |
continue | |
print('Closest id for {} was {}'.format(title, best_matching_id)) | |
vote_movie(driver, best_matching_id, imdb_id, rating) | |
def should_try_vote(imdb_id: str, rating: int, ignore_results: bool = False) -> bool: | |
if ignore_results: | |
return True | |
# Movie not yet processed | |
if imdb_id not in RESULTS: | |
return True | |
# It was not on Netflix in previous processing | |
# (So only way to accommodate Netflix changes is to rerun it ALL unfortunately) | |
if RESULTS[imdb_id] == RESULT_NOT_FOUND: | |
return False | |
vote_up = rating >= 7 | |
# Movie was voted incorrectly | |
if RESULTS[imdb_id] == RESULT_FOUND_VOTED_UP and not vote_up: | |
return True | |
# Movie was voted incorrectly | |
if RESULTS[imdb_id] == RESULT_FOUND_VOTED_DOWN and vote_up: | |
return True | |
# Movie was voted correctly | |
return False | |
def ids_with_similarity_all_links(links: List[WebElement], title: str) -> Dict[str, float]: | |
ids_with_similarity = {} | |
for elem in links: | |
label = elem.get_attribute('aria-label') | |
href = elem.get_attribute('href') | |
if label is None or href is None: | |
print('XPath returned illegal element') | |
continue | |
maybe_id = re.search(r"\d{5,10}", href) | |
if maybe_id is None: | |
continue | |
entry_id = maybe_id.group() | |
ids_with_similarity[entry_id] = SequenceMatcher(None, label, title).ratio() | |
return ids_with_similarity | |
def vote_movie(driver: webdriver.Firefox, netflix_id: str, imdb_id: str, rating: int): | |
# Netflix renders quick, completes slowly! Just try to get movie details couple of times | |
tries = 3 | |
for i in range(tries): | |
try: | |
driver.get('https://netflix.com/title/{}'.format(netflix_id)) | |
break | |
except TimeoutException: | |
if i == (tries - 1): | |
# Now we register it as really problematic | |
raise | |
try: | |
driver.find_element_by_css_selector('.thumbs-component.rated') | |
rated = True | |
except NoSuchElementException: | |
rated = False | |
if rated: | |
# remove old vote | |
# Even though manual inspection only shows 1, we get 2 elements (1 for up 1 for down??) | |
# Simply try them both for now... | |
unvote_elements = driver.find_elements_by_css_selector('a.nf-svg-button.simpleround') | |
def register_success(): | |
print('Removed old vote for {}'.format(id)) | |
try: | |
unvote_elements[0].click() | |
register_success() | |
except ElementClickInterceptedException: | |
unvote_elements[1].click() | |
register_success() | |
upvoting = rating >= 7 | |
data_rating_val = '"2"' if upvoting else '"1"' | |
print('Rating was {} so voting {} for {}'.format(rating, 'up' if upvoting else 'down', imdb_id)) | |
if upvoting: | |
RESULTS[imdb_id] = RESULT_FOUND_VOTED_UP | |
else: | |
RESULTS[imdb_id] = RESULT_FOUND_VOTED_DOWN | |
selector = 'a.nf-svg-button[data-rating={}]'.format(data_rating_val) | |
thumb = driver.find_element_by_css_selector(selector) | |
# After "unvoting" we need to wait before we can register new vote. This SHOULD work with the | |
# "expected conditions" of selenium (element_to_be_clickable), but did not work for me. Use stupid sleep... | |
time.sleep(0.5) | |
thumb.click() | |
################################################################################################### | |
def print_statistics(): | |
# Just to know where we are at start and end of script | |
num_not_found = sum(1 for result in RESULTS.items() if result[1] == RESULT_NOT_FOUND) | |
num_found_voted_up = sum(1 for result in RESULTS.items() if result[1] == RESULT_FOUND_VOTED_UP) | |
num_found_voted_down = sum(1 for result in RESULTS.items() if result[1] == RESULT_FOUND_VOTED_DOWN) | |
print('Not found:{}'.format(num_not_found)) | |
print('Found, voted up: {}'.format(num_found_voted_up)) | |
print('Found, voted down: {}'.format(num_found_voted_down)) | |
def set_results_from_file(): | |
global RESULTS | |
if os.path.exists(RESULTS_FILE): | |
with open(RESULTS_FILE) as f: | |
RESULTS = json.load(f) | |
print('Loaded results from json') | |
def set_file_from_results(): | |
with open(RESULTS_FILE, 'w') as f: | |
f.write(json.dumps(RESULTS)) | |
print('Wrote results to json') | |
@click.command() | |
@click.argument('netflix-username') | |
@click.argument('netflix-profile-id') | |
@click.option('--imdb-username') | |
@click.option('--imdb-id', default=None) | |
@click.option('--headful', default=False, is_flag=True) | |
@click.option('--ignore-results', default=False, is_flag=True) | |
def main(netflix_username: str, netflix_profile_id: str, imdb_username: str, imdb_id: str, headful: bool, | |
ignore_results: bool): | |
driver = None | |
set_results_from_file() | |
print_statistics() | |
def shutdown(): | |
nonlocal driver | |
set_file_from_results() | |
print_statistics() | |
if driver is not None: | |
driver.quit() | |
download_csv = should_update_ratings() | |
if download_csv: | |
if imdb_username is None: | |
raise ValueError('Missing --imdb-username value') | |
if imdb_id is None: | |
raise ValueError('Missing --imdb-id value') | |
imdb_pass = getpass.getpass('IMDB Password:') | |
else: | |
imdb_pass = None | |
netflix_pass = getpass.getpass('Netflix Password:') | |
try: | |
driver = get_driver(headful) | |
if download_csv: | |
login_to_imdb(driver, imdb_username, imdb_pass) | |
login_to_netflix(driver, netflix_username, netflix_pass) | |
mapping = fetch_history(driver, download_csv, imdb_id) | |
choose_netflix_profile(driver, netflix_profile_id) | |
vote_with_mapping(driver, mapping, ignore_results) | |
print('All done!') | |
shutdown() | |
remove_history_file() | |
except: | |
shutdown() | |
raise | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Statistics after running:
Not found:1141
Found, voted up: 68
Found, voted down: 98
Results (in case I lose it)
{"tt1000095": 2, "tt1001526": 1, "tt1007029": 0, "tt0100802": 2, "tt1010048": 2, "tt1013753": 2, "tt0101414": 2, "tt1014759": 2, "tt0101587": 2, "tt0101745": 2, "tt1019452": 2, "tt1022603": 2, "tt0102685": 1, "tt0102798": 2, "tt1028528": 2, "tt0102926": 2, "tt0103064": 2, "tt0103241": 2, "tt1032755": 2, "tt1032846": 2, "tt0103639": 2, "tt1037705": 2, "tt0103776": 2, "tt1038988": 2, "tt1043842": 2, "tt0104431": 2, "tt0104437": 2, "tt1045658": 2, "tt0104797": 2, "tt0104868": 2, "tt1049413": 2, "tt0105151": 2, "tt0105236": 2, "tt0105417": 2, "tt1054606": 2, "tt0105629": 2, "tt0105690": 1, "tt0105812": 2, "tt1059786": 1, "tt1060277": 0, "tt0106226": 2, "tt0106308": 1, "tt1065073": 2, "tt0106627": 2, "tt0106673": 2, "tt1068646": 2, "tt1068680": 2, "tt1069238": 2, "tt0106965": 2, "tt0107048": 2, "tt1071804": 2, "tt0107290": 2, "tt1073498": 2, "tt0107362": 2, "tt1074638": 2, "tt0107614": 2, "tt0107688": 2, "tt1077258": 2, "tt1077262": 2, "tt1077368": 2, "tt1078588": 2, "tt1080016": 2, "tt0108052": 2, "tt0108147": 2, "tt0108399": 2, "tt0109040": 2, "tt1091722": 2, "tt0109190": 2, "tt1092019": 2, "tt0109254": 2, "tt0109445": 2, "tt0109686": 1, "tt0109707": 2, "tt0109813": 2, "tt0109830": 0, "tt1099212": 1, "tt0109936": 2, "tt0110074": 2, "tt0110099": 2, "tt0110357": 2, "tt0110366": 1, "tt1104001": 2, "tt0110413": 2, "tt0110475": 2, "tt0110478": 2, "tt0110725": 2, "tt0110857": 2, "tt0110912": 0, "tt1109624": 2, "tt0110989": 2, "tt0111070": 2, "tt0111161": 2, "tt1111833": 1, "tt0111257": 2, "tt0111280": 2, "tt0111282": 2, "tt1119646": 2, "tt1121931": 2, "tt0112255": 2, "tt0112281": 2, "tt0112431": 2, "tt0112442": 2, "tt0112462": 2, "tt0112573": 0, "tt1125849": 2, "tt0112697": 1, "tt0112896": 2, "tt1130080": 2, "tt0113071": 2, "tt1130884": 2, "tt0113189": 2, "tt0113243": 2, "tt0113247": 2, "tt1132626": 2, "tt0113277": 2, "tt1133985": 2, "tt0113492": 2, "tt0113497": 2, "tt1135985": 1, "tt1136608": 2, "tt1139797": 2, "tt0114048": 2, "tt0114069": 2, "tt0114148": 1, "tt0114369": 2, "tt0114436": 2, "tt0114623": 2, "tt0114709": 2, "tt0114746": 2, "tt0114781": 1, "tt0114814": 2, "tt0114825": 2, "tt0114898": 2, "tt1149362": 2, "tt1152836": 0, "tt1155076": 2, "tt1156398": 0, "tt0115683": 2, "tt0115751": 2, "tt0115798": 2, "tt0116126": 1, "tt0116151": 2, "tt0116225": 2, "tt0116250": 2, "tt0116282": 0, "tt0116483": 1, "tt0116778": 2, "tt0116996": 2, "tt0117008": 2, "tt1170358": 2, "tt0117060": 0, "tt0117218": 2, "tt0117705": 2, "tt0117731": 2, "tt0117786": 2, "tt1178663": 2, "tt0117951": 2, "tt1179904": 2, "tt0117998": 2, "tt1182345": 2, "tt0118539": 2, "tt1185616": 2, "tt0118571": 2, "tt0118583": 2, "tt0118665": 2, "tt0118691": 2, "tt1187043": 2, "tt0118708": 2, "tt0118715": 2, "tt0118799": 2, "tt0118880": 2, "tt0118884": 2, "tt0118928": 2, "tt0118954": 2, "tt0118972": 2, "tt0118998": 2, "tt1191111": 2, "tt0119116": 2, "tt0119137": 2, "tt0119173": 2, "tt0119174": 2, "tt0119217": 0, "tt1193138": 2, "tt0119396": 2, "tt0119528": 2, "tt0119567": 2, "tt1196141": 2, "tt0119630": 2, "tt0119643": 2, "tt0119654": 2, "tt0119698": 2, "tt0119896": 2, "tt1201607": 2, "tt0120179": 2, "tt0120184": 2, "tt0120199": 2, "tt0120338": 2, "tt0120347": 2, "tt0120363": 2, "tt0120382": 2, "tt0120434": 2, "tt1205489": 2, "tt1205558": 2, "tt0120586": 2, "tt0120587": 1, "tt0120591": 2, "tt0120595": 2, "tt0120601": 2, "tt0120611": 2, "tt0120616": 2, "tt0120623": 2, "tt0120647": 2, "tt0120663": 2, "tt0120667": 2, "tt0120669": 2, "tt0120685": 2, "tt0120689": 2, "tt0120735": 2, "tt0120737": 2, "tt0120746": 2, "tt0120755": 1, "tt0120762": 2, "tt0120773": 2, "tt0120783": 2, "tt0120789": 2, "tt0120794": 0, "tt0120812": 2, "tt0120815": 0, "tt0120844": 2, "tt0120891": 2, "tt0120903": 2, "tt0120912": 2, "tt0120913": 2, "tt0120915": 2, "tt1210166": 2, "tt1211837": 0, "tt1211956": 1, "tt1213644": 2, "tt1213663": 2, "tt1216475": 2, "tt1217209": 2, "tt0121765": 2, "tt0121766": 2, "tt1219827": 2, "tt1220719": 2, "tt1224366": 2, "tt1228705": 2, "tt0122933": 2, "tt1229340": 2, "tt1231583": 2, "tt1231587": 2, "tt1232776": 2, "tt1232829": 2, "tt1233227": 2, "tt0012349": 2, "tt1245492": 2, "tt1250777": 0, "tt1253863": 2, "tt0125439": 1, "tt0125664": 2, "tt0126029": 0, "tt1270798": 2, "tt1282140": 2, "tt1285016": 2, "tt0128853": 2, "tt1289406": 2, "tt1291150": 1, "tt0129167": 2, "tt0129290": 2, "tt0129387": 2, "tt1298650": 2, "tt1300854": 2, "tt1302011": 1, "tt0130827": 2, "tt1313104": 2, "tt0131369": 2, "tt1320352": 2, "tt0132245": 2, "tt1329457": 2, "tt0133093": 2, "tt0133152": 2, "tt0133240": 2, "tt1333125": 2, "tt1335975": 2, "tt1337051": 2, "tt1340138": 2, "tt1343092": 2, "tt0013442": 2, "tt1345836": 2, "tt0134847": 2, "tt1355644": 2, "tt1371111": 2, "tt0137494": 2, "tt0137523": 2, "tt1375666": 2, "tt0138097": 2, "tt1385956": 2, "tt1386588": 2, "tt1386697": 2, "tt1386703": 2, "tt0138749": 1, "tt0139134": 2, "tt1392170": 1, "tt1392190": 2, "tt0139654": 2, "tt0139809": 2, "tt1403865": 0, "tt1409024": 2, "tt1411250": 1, "tt1411697": 2, "tt0141369": 1, "tt1418377": 1, "tt0142342": 2, "tt1426378": 2, "tt1431045": 2, "tt0143145": 2, "tt1436562": 2, "tt0144084": 0, "tt0144528": 2, "tt1446714": 2, "tt1454029": 2, "tt1454468": 2, "tt0145487": 2, "tt0145660": 2, "tt0146316": 2, "tt1464540": 2, "tt1465522": 2, "tt1467061": 1, "tt1468843": 2, "tt0147612": 2, "tt0147800": 2, "tt1483013": 2, "tt1486192": 2, "tt1490017": 2, "tt0149261": 2, "tt1499658": 2, "tt1504320": 2, "tt0150662": 2, "tt1512235": 2, "tt1515091": 2, "tt0151738": 2, "tt0151804": 2, "tt1524930": 2, "tt1527186": 2, "tt1528100": 2, "tt1534085": 2, "tt1535108": 2, "tt1542344": 2, "tt0154420": 2, "tt0155711": 2, "tt1560747": 2, "tt1568346": 2, "tt1571222": 2, "tt0157503": 2, "tt0015864": 2, "tt1587707": 2, "tt1588398": 2, "tt1596350": 2, "tt0160127": 2, "tt1601913": 1, "tt1608290": 1, "tt0160862": 2, "tt0161083": 2, "tt1611224": 2, "tt1612774": 2, "tt1617661": 2, "tt1619029": 1, "tt0162222": 1, "tt0163025": 2, "tt0163187": 2, "tt1634122": 1, "tt0163651": 2, "tt1636826": 2, "tt1637725": 2, "tt1638355": 2, "tt1639084": 1, "tt0164052": 2, "tt1645170": 1, "tt1646971": 1, "tt1646987": 2, "tt0164912": 2, "tt1650062": 2, "tt1650554": 2, "tt1655442": 2, "tt1659337": 2, "tt1663202": 2, "tt0166813": 1, "tt0166896": 2, "tt0166924": 2, "tt1670345": 0, "tt0167190": 2, "tt0167260": 2, "tt0167261": 2, "tt0167404": 2, "tt1674771": 2, "tt1675434": 2, "tt0168563": 2, "tt0168629": 2, "tt1686821": 2, "tt1687247": 2, "tt1690953": 2, "tt1690967": 1, "tt1692486": 2, "tt0169547": 0, "tt0170016": 2, "tt1706593": 1, "tt1706620": 2, "tt1707386": 2, "tt0017136": 2, "tt0172156": 2, "tt1723121": 2, "tt1723811": 2, "tt0172495": 0, "tt1727770": 2, "tt1731141": 1, "tt1735898": 2, "tt0173840": 2, "tt0175142": 1, "tt1753743": 2, "tt1754656": 2, "tt0175880": 2, "tt1767372": 2, "tt1772264": 2, "tt1772341": 2, "tt1776196": 2, "tt0177789": 2, "tt1790809": 2, "tt1790864": 2, "tt0017925": 2, "tt1798709": 2, "tt1800241": 2, "tt0180093": 2, "tt0181689": 1, "tt0181739": 2, "tt0181852": 2, "tt0181875": 2, "tt1823664": 2, "tt1823672": 2, "tt1825683": 2, "tt0182789": 2, "tt0183505": 2, "tt0183649": 2, "tt1840309": 1, "tt1843866": 2, "tt0184894": 2, "tt0185183": 2, "tt0018528": 2, "tt1853728": 2, "tt1854564": 2, "tt0185937": 2, "tt1859650": 2, "tt0186566": 2, "tt0187078": 2, "tt1872181": 2, "tt0187738": 2, "tt1877832": 2, "tt1895587": 0, "tt0189998": 2, "tt0190332": 2, "tt0190590": 2, "tt0190641": 2, "tt1912398": 2, "tt0191754": 2, "tt0192614": 2, "tt1929263": 0, "tt1935859": 2, "tt1937390": 2, "tt1951264": 0, "tt1951265": 2, "tt1951266": 1, "tt0195714": 2, "tt0196229": 1, "tt1964418": 2, "tt1971325": 2, "tt1979388": 2, "tt0198021": 2, "tt1981115": 2, "tt0198781": 2, "tt2004420": 2, "tt0200465": 2, "tt2005151": 2, "tt2015381": 0, "tt2024544": 0, "tt0203009": 2, "tt0205000": 2, "tt0020629": 2, "tt0206634": 2, "tt0207201": 2, "tt0208092": 2, "tt0208298": 2, "tt2084970": 2, "tt0209144": 2, "tt0209163": 2, "tt2094766": 2, "tt2096673": 2, "tt2101569": 2, "tt0210234": 2, "tt2103281": 2, "tt0211915": 2, "tt0211933": 2, "tt0212338": 2, "tt0212346": 2, "tt0212720": 2, "tt0212985": 0, "tt0213149": 2, "tt2132285": 2, "tt2140373": 2, "tt2170439": 2, "tt0021749": 2, "tt0217869": 2, "tt2194499": 2, "tt2207467": 2, "tt2209764": 2, "tt0022100": 2, "tt2229499": 2, "tt0223897": 2, "tt2245084": 2, "tt2250912": 2, "tt0227538": 2, "tt2277860": 2, "tt2278388": 2, "tt2279373": 2, "tt2292707": 2, "tt2294449": 2, "tt2294629": 2, "tt0230011": 1, "tt2310332": 2, "tt0231775": 2, "tt2322441": 2, "tt0232500": 2, "tt2333804": 2, "tt2338151": 2, "tt0234215": 2, "tt0235679": 2, "tt2357291": 2, "tt2361509": 2, "tt2381111": 2, "tt2381249": 2, "tt2382009": 2, "tt0238380": 2, "tt0238546": 2, "tt0239395": 2, "tt2395427": 2, "tt2397535": 2, "tt2404425": 0, "tt0240462": 2, "tt0240772": 2, "tt0241303": 2, "tt0241527": 2, "tt0242423": 2, "tt0242445": 2, "tt0242527": 2, "tt0242653": 2, "tt0243155": 2, "tt0243585": 2, "tt0244244": 2, "tt2452042": 2, "tt0245429": 2, "tt0245844": 2, "tt0246460": 2, "tt0246464": 2, "tt0246578": 2, "tt0247586": 2, "tt0247638": 2, "tt2488496": 2, "tt0250258": 2, "tt0250494": 2, "tt0250687": 2, "tt0252503": 1, "tt0252866": 2, "tt0253474": 2, "tt0253556": 2, "tt0253754": 2, "tt0253867": 2, "tt2544766": 0, "tt2557490": 2, "tt2562232": 2, "tt0257076": 2, "tt0257106": 2, "tt0258000": 2, "tt0258153": 2, "tt2582846": 2, "tt0258463": 0, "tt0258470": 2, "tt2591814": 2, "tt0259324": 2, "tt0259711": 2, "tt2608732": 2, "tt0261392": 2, "tt2637276": 2, "tt2637294": 2, "tt2639254": 1, "tt0264464": 0, "tt0265086": 1, "tt0265087": 2, "tt0265208": 2, "tt0265666": 2, "tt2660888": 1, "tt0266308": 2, "tt0266543": 0, "tt0266697": 2, "tt0266915": 2, "tt0267913": 2, "tt0268288": 2, "tt0268380": 2, "tt0268695": 2, "tt0268978": 2, "tt2691498": 1, "tt0270288": 2, "tt0270846": 2, "tt0270980": 2, "tt0271668": 1, "tt0272152": 2, "tt2726560": 2, "tt0273822": 2, "tt2739338": 2, "tt0274166": 2, "tt0276919": 2, "tt0277296": 2, "tt0277371": 2, "tt2788710": 2, "tt0027977": 2, "tt2802144": 2, "tt0280460": 2, "tt0280486": 2, "tt0280590": 2, "tt0280720": 2, "tt0282667": 2, "tt0283026": 2, "tt0283054": 2, "tt0283111": 2, "tt0284490": 2, "tt0286106": 2, "tt0286499": 2, "tt0286716": 2, "tt0286788": 2, "tt2870708": 2, "tt2872518": 2, "tt2872732": 2, "tt0287635": 2, "tt0287717": 2, "tt0287978": 2, "tt0288045": 2, "tt2884206": 2, "tt2885450": 2, "tt0289043": 2, "tt0289765": 0, "tt0289848": 2, "tt0289879": 2, "tt0290002": 2, "tt0290095": 2, "tt0290334": 2, "tt2910904": 2, "tt2911342": 2, "tt2911666": 1, "tt0293564": 2, "tt0295178": 2, "tt0295297": 2, "tt0295700": 2, "tt0295701": 2, "tt0029583": 2, "tt0296572": 2, "tt0296845": 2, "tt0297162": 2, "tt0297284": 2, "tt2975590": 2, "tt2980516": 1, "tt0298130": 2, "tt0298148": 1, "tt0298203": 2, "tt0298814": 2, "tt0299977": 2, "tt0300471": 2, "tt0301357": 2, "tt0302297": 2, "tt0302640": 2, "tt0303933": 2, "tt3040964": 1, "tt0304141": 2, "tt0304669": 2, "tt0305224": 2, "tt0305357": 2, "tt0306047": 1, "tt3062096": 2, "tt0306841": 2, "tt0306892": 2, "tt0307156": 2, "tt0307453": 1, "tt0307479": 2, "tt3079380": 2, "tt0308208": 2, "tt0308644": 2, "tt0309530": 1, "tt0310775": 2, "tt0310793": 2, "tt3110958": 1, "tt0311113": 2, "tt0311429": 2, "tt0313792": 2, "tt0313911": 2, "tt3149038": 2, "tt0315327": 2, "tt0316654": 2, "tt0317198": 2, "tt0317219": 2, "tt0317248": 2, "tt0317303": 2, "tt0317705": 2, "tt0317919": 2, "tt3183660": 2, "tt0318649": 1, "tt0319061": 2, "tt0319262": 0, "tt0319343": 2, "tt0320661": 2, "tt0320691": 1, "tt0032138": 2, "tt0322259": 2, "tt0322330": 2, "tt0322420": 2, "tt0322589": 1, "tt0323572": 2, "tt0032455": 2, "tt0032553": 2, "tt0325703": 2, "tt0325710": 2, "tt0325980": 2, "tt3263904": 2, "tt0326856": 2, "tt0326900": 2, "tt0327597": 2, "tt0327679": 2, "tt0330373": 2, "tt3312830": 2, "tt3315342": 2, "tt3316948": 1, "tt0332379": 2, "tt0332452": 2, "tt0333766": 2, "tt0333780": 2, "tt0033467": 2, "tt0335119": 2, "tt0335266": 2, "tt0335438": 2, "tt0337697": 2, "tt0338013": 0, "tt0338094": 2, "tt0338459": 1, "tt0338466": 2, "tt0338526": 1, "tt3385516": 2, "tt0338564": 2, "tt0338751": 2, "tt0339034": 2, "tt0339300": 2, "tt3410834": 2, "tt3416742": 2, "tt0342508": 2, "tt0342735": 2, "tt0343660": 2, "tt0343737": 2, "tt0343818": 1, "tt0034492": 2, "tt0034583": 2, "tt0345950": 0, "tt3464902": 2, "tt0347149": 2, "tt0347246": 2, "tt0347791": 2, "tt0348150": 2, "tt0348333": 2, "tt0349205": 2, "tt3498820": 2, "tt0349903": 2, "tt3501632": 2, "tt0351283": 1, "tt3521164": 2, "tt3531824": 2, "tt0354575": 2, "tt3553976": 2, "tt0356150": 2, "tt0356470": 2, "tt0356910": 2, "tt0357277": 2, "tt0357413": 0, "tt0358082": 2, "tt0358273": 2, "tt0358294": 2, "tt0359013": 2, "tt0359950": 2, "tt0360486": 2, "tt0360717": 2, "tt0361467": 2, "tt0361748": 0, "tt0361862": 2, "tt0362120": 1, "tt3622592": 2, "tt0362270": 2, "tt0363163": 2, "tt0363282": 2, "tt0363589": 2, "tt0363771": 2, "tt0364569": 2, "tt0365748": 0, "tt0365830": 2, "tt3659388": 2, "tt0365957": 1, "tt0366548": 2, "tt0367085": 2, "tt3672742": 2, "tt0367594": 2, "tt0367652": 2, "tt0036775": 2, "tt0367882": 2, "tt0368667": 2, "tt0368794": 2, "tt0368891": 2, "tt0368933": 2, "tt0369610": 2, "tt0370032": 1, "tt0370263": 2, "tt0370986": 2, "tt0371724": 2, "tt0371746": 2, "tt0372183": 0, "tt3722070": 2, "tt0372237": 1, "tt0372334": 2, "tt0372588": 2, "tt0372784": 2, "tt0372873": 2, "tt0373051": 2, "tt0373469": 2, "tt0373889": 2, "tt3748528": 2, "tt0374900": 2, "tt0375063": 2, "tt0375679": 2, "tt0376105": 2, "tt0376994": 2, "tt0377092": 1, "tt0377109": 2, "tt0378194": 2, "tt3783958": 2, "tt0378947": 2, "tt0379725": 0, "tt0379786": 2, "tt0381061": 2, "tt0381111": 2, "tt0381348": 2, "tt0381707": 2, "tt0382330": 2, "tt0382625": 2, "tt0382932": 2, "tt0383028": 2, "tt0383216": 2, "tt0038348": 2, "tt0383574": 2, "tt0385700": 2, "tt0385752": 2, "tt3859052": 2, "tt0386032": 2, "tt0386117": 2, "tt0038650": 2, "tt0386588": 2, "tt3874544": 2, "tt0387564": 2, "tt0387808": 1, "tt0387898": 2, "tt0388795": 2, "tt3896198": 2, "tt0389790": 1, "tt0389860": 2, "tt0390521": 2, "tt0391198": 2, "tt3913244": 2, "tt3922818": 2, "tt0392878": 2, "tt3949660": 2, "tt0395169": 2, "tt0395699": 2, "tt0396171": 2, "tt0396269": 2, "tt0396555": 2, "tt0396652": 2, "tt0397065": 2, "tt0397313": 2, "tt0397535": 2, "tt0398165": 2, "tt0398286": 0, "tt0398808": 2, "tt0399201": 2, "tt0399295": 0, "tt4005402": 2, "tt0401233": 2, "tt0401383": 2, "tt0401711": 2, "tt0401729": 2, "tt0401792": 2, "tt0401997": 2, "tt0402022": 2, "tt0403702": 2, "tt0403703": 2, "tt0404203": 2, "tt0404496": 2, "tt4046784": 2, "tt0405094": 2, "tt0405159": 2, "tt0405296": 2, "tt0407304": 2, "tt0407887": 2, "tt0408236": 2, "tt0408790": 2, "tt0409459": 2, "tt0410377": 2, "tt0410764": 0, "tt0411477": 1, "tt4116284": 2, "tt0412922": 2, "tt0413267": 1, "tt0413300": 2, "tt0414993": 2, "tt0415306": 2, "tt0416449": 2, "tt0416508": 2, "tt0417741": 2, "tt0418279": 2, "tt0041959": 2, "tt0420076": 2, "tt0421054": 2, "tt0421073": 2, "tt0421082": 2, "tt0421715": 2, "tt0422861": 2, "tt0423977": 2, "tt0424136": 2, "tt0424345": 2, "tt0425061": 2, "tt0425112": 2, "tt0425123": 2, "tt0425210": 2, "tt0425413": 2, "tt0426578": 2, "tt0426883": 2, "tt0426931": 2, "tt0427309": 2, "tt0427470": 2, "tt0427944": 2, "tt4287320": 2, "tt0043014": 2, "tt0430308": 2, "tt0432283": 2, "tt0432348": 2, "tt0433362": 2, "tt0433383": 2, "tt0433386": 2, "tt0434409": 2, "tt0435705": 2, "tt0435761": 2, "tt0436697": 2, "tt0438097": 2, "tt0438488": 2, "tt0044081": 2, "tt0440963": 0, "tt0441773": 1, "tt4425200": 2, "tt0442933": 2, "tt0443274": 2, "tt0443453": 2, "tt0443489": 2, "tt0443649": 2, "tt0443706": 2, "tt4438848": 2, "tt0445953": 2, "tt0446029": 0, "tt4468740": 2, "tt0448134": 2, "tt4481514": 2, "tt0448157": 2, "tt0449059": 2, "tt0449088": 2, "tt4500922": 2, "tt0450278": 2, "tt0450385": 2, "tt0451279": 2, "tt0045152": 2, "tt0452608": 2, "tt0453467": 2, "tt0453556": 2, "tt0454848": 0, "tt0454876": 2, "tt0454921": 2, "tt0454945": 1, "tt0455538": 2, "tt0455590": 2, "tt0455857": 2, "tt0456396": 2, "tt4572792": 2, "tt0457430": 2, "tt0457513": 2, "tt0458339": 2, "tt0458481": 1, "tt0458525": 2, "tt0460791": 0, "tt0462322": 2, "tt0462538": 0, "tt0462590": 1, "tt0463854": 2, "tt0464049": 2, "tt4649466": 2, "tt0465234": 2, "tt0465494": 2, "tt0465538": 2, "tt0467200": 2, "tt0467406": 2, "tt0468569": 2, "tt0468644": 1, "tt0469494": 2, "tt0470752": 2, "tt0473075": 2, "tt0047396": 2, "tt0475290": 2, "tt0477078": 2, "tt0477348": 0, "tt0478087": 2, "tt0478134": 2, "tt0478304": 2, "tt0478311": 2, "tt0478970": 2, "tt0479500": 2, "tt0479884": 2, "tt0479952": 1, "tt0480025": 2, "tt0480249": 2, "tt0482088": 2, "tt0482571": 2, "tt4846340": 2, "tt0485947": 2, "tt0048624": 2, "tt0486358": 2, "tt0486576": 2, "tt0486655": 2, "tt0486822": 2, "tt4877122": 2, "tt0489099": 2, "tt0489270": 2, "tt0493464": 1, "tt0049406": 0, "tt0494238": 2, "tt0496806": 2, "tt0497116": 2, "tt0049762": 2, "tt0049833": 2, "tt0498353": 2, "tt0499448": 2, "tt0499549": 2, "tt0499603": 2, "tt0050083": 2, "tt0050825": 2, "tt0050974": 2, "tt0052357": 0, "tt0053125": 2, "tt0053146": 2, "tt0053291": 2, "tt0053946": 2, "tt0054215": 2, "tt0054331": 0, "tt0054743": 2, "tt0055254": 2, "tt5541240": 0, "tt0055630": 2, "tt5580390": 2, "tt0056172": 2, "tt0056193": 2, "tt0056592": 0, "tt0056869": 2, "tt0057012": 2, "tt0057076": 2, "tt0058150": 2, "tt0058331": 2, "tt0059742": 2, "tt0060196": 2, "tt0061418": 2, "tt0061722": 2, "tt0061852": 1, "tt0062622": 2, "tt0063518": 2, "tt0064116": 2, "tt0066921": 2, "tt0066995": 2, "tt0067992": 2, "tt0068428": 2, "tt0068646": 0, "tt0070328": 2, "tt0070511": 2, "tt0070707": 2, "tt0070909": 2, "tt0071315": 2, "tt0071562": 0, "tt0071807": 2, "tt0071853": 0, "tt0071877": 2, "tt0072684": 2, "tt0073341": 2, "tt0073486": 2, "tt0074207": 2, "tt0074958": 0, "tt0075148": 2, "tt0075314": 2, "tt0075686": 0, "tt0758730": 2, "tt0758758": 2, "tt0760311": 2, "tt0762073": 2, "tt0765429": 2, "tt0765443": 2, "tt0076759": 2, "tt0770772": 2, "tt0770802": 2, "tt0770828": 2, "tt0077631": 1, "tt0780622": 2, "tt0783233": 2, "tt0078346": 2, "tt0787474": 0, "tt0078748": 2, "tt0787524": 2, "tt0078788": 2, "tt0792986": 2, "tt0079470": 2, "tt0079501": 2, "tt0079522": 2, "tt0795441": 2, "tt0079574": 2, "tt0796366": 0, "tt0079817": 2, "tt0799934": 2, "tt0799949": 2, "tt0800369": 2, "tt0080120": 2, "tt0803096": 2, "tt0080339": 2, "tt0080453": 2, "tt0080455": 2, "tt0805564": 2, "tt0080678": 2, "tt0080684": 2, "tt0808151": 1, "tt0808417": 2, "tt0808506": 2, "tt0811080": 2, "tt0811138": 2, "tt0813547": 2, "tt0081398": 2, "tt0814255": 2, "tt0814314": 2, "tt0081505": 2, "tt0815245": 2, "tt0081573": 2, "tt0816692": 2, "tt0817177": 2, "tt0820142": 2, "tt0822854": 1, "tt0824747": 2, "tt0825232": 2, "tt0826711": 2, "tt0082694": 2, "tt0082971": 2, "tt0830515": 2, "tt0831888": 2, "tt0083564": 2, "tt0083658": 2, "tt0083866": 2, "tt0083907": 2, "tt0083944": 2, "tt0083987": 2, "tt0084021": 2, "tt0840361": 2, "tt0842926": 2, "tt0084329": 2, "tt0844471": 1, "tt0084503": 2, "tt0084602": 2, "tt0846040": 2, "tt0084726": 2, "tt0848228": 2, "tt0084827": 2, "tt0851578": 2, "tt0857191": 2, "tt0085959": 2, "tt0086034": 2, "tt0086190": 2, "tt0086250": 2, "tt0862846": 2, "tt0086465": 2, "tt0865556": 1, "tt0086567": 2, "tt0086879": 2, "tt0086960": 2, "tt0086979": 2, "tt0869977": 2, "tt0870111": 2, "tt0870984": 2, "tt0087332": 2, "tt0087363": 2, "tt0875609": 2, "tt0876563": 2, "tt0087843": 2, "tt0087928": 2, "tt0880502": 2, "tt0880578": 2, "tt0088170": 2, "tt0088184": 2, "tt0088247": 2, "tt0088323": 2, "tt0884328": 1, "tt0088763": 2, "tt0887883": 2, "tt0088847": 2, "tt0890870": 2, "tt0089153": 2, "tt0892769": 0, "tt0089822": 2, "tt0089880": 2, "tt0090264": 2, "tt0903624": 2, "tt0090605": 2, "tt0090685": 2, "tt0090756": 2, "tt0091042": 0, "tt0910970": 2, "tt0091129": 2, "tt0914863": 2, "tt0091777": 2, "tt0918927": 2, "tt0091949": 2, "tt0092005": 2, "tt0092007": 2, "tt0092067": 2, "tt0926063": 1, "tt0926084": 2, "tt0092644": 2, "tt0926762": 2, "tt0092929": 2, "tt0929425": 1, "tt0929632": 2, "tt0092991": 2, "tt0093058": 2, "tt0093389": 2, "tt0093405": 2, "tt0936501": 0, "tt0093756": 2, "tt0093779": 2, "tt0093818": 2, "tt0093822": 2, "tt0093936": 2, "tt0094012": 2, "tt0942385": 2, "tt0945513": 1, "tt0094625": 0, "tt0094651": 2, "tt0947798": 2, "tt0948470": 2, "tt0094898": 1, "tt0949731": 2, "tt0951216": 2, "tt0952640": 1, "tt0095327": 2, "tt0953318": 2, "tt0095705": 2, "tt0095765": 2, "tt0095882": 2, "tt0095953": 0, "tt0095956": 2, "tt0960731": 2, "tt0960890": 2, "tt0096283": 2, "tt0096438": 2, "tt0964517": 2, "tt0964539": 2, "tt0970411": 1, "tt0970416": 1, "tt0970472": 2, "tt0097115": 2, "tt0097123": 2, "tt0097165": 2, "tt0097235": 2, "tt0974015": 2, "tt0097428": 2, "tt0974554": 2, "tt0097523": 1, "tt0097576": 2, "tt0976051": 2, "tt0097757": 2, "tt0978759": 2, "tt0978762": 2, "tt0978764": 2, "tt0980970": 0, "tt0098105": 2, "tt0983193": 2, "tt0986263": 2, "tt0988045": 2, "tt0993846": 0, "tt0099487": 2, "tt0099685": 2, "tt0099739": 2, "tt0099785": 2}