X-Git-Url: http://git.code-monkey.de/?a=blobdiff_plain;f=resources%2Flib%2FNetflixSession.py;h=6f28de148264c7a2fbe0107e0ecd0886c6e9b038;hb=dfc7738380f5ce833063ce74e2c0ab9b82b84eae;hp=a96fae6f9b400b310e25120c27b222e3f827a9fd;hpb=4ec9b5afd40ef050c550ae2e6c97ca153314b6be;p=plugin.video.netflix.git diff --git a/resources/lib/NetflixSession.py b/resources/lib/NetflixSession.py index a96fae6..6f28de1 100644 --- a/resources/lib/NetflixSession.py +++ b/resources/lib/NetflixSession.py @@ -4,17 +4,17 @@ # Created on: 13.01.2017 import os -import base64 -import time -import urllib import json -import requests +from requests import session, cookies +from urllib import quote, unquote +from time import time +from base64 import urlsafe_b64encode +from bs4 import BeautifulSoup, SoupStrainer +from utils import noop try: import cPickle as pickle except: import pickle -from bs4 import BeautifulSoup, SoupStrainer -from utils import noop class NetflixSession: """Helps with login/session management of Netflix users & API data fetching""" @@ -24,15 +24,16 @@ class NetflixSession: urls = { 'login': '/login', - 'browse': '/browse', - 'video_list_ids': '/warmer', + 'browse': '/profiles/manage', + 'video_list_ids': '/preflight', 'shakti': '/pathEvaluator', - 'profiles': '/browse', + 'profiles': '/profiles/manage', 'switch_profiles': '/profiles/switch', 'adult_pin': '/pin/service', 'metadata': '/metadata', 'set_video_rating': '/setVideoRating', - 'update_my_list': '/playlistop' + 'update_my_list': '/playlistop', + 'kids': '/Kids' } """:obj:`dict` of :obj:`str` List of all static endpoints for HTML/JSON POST/GET requests""" @@ -100,7 +101,7 @@ class NetflixSession: self.log = log_fn # start session, fake chrome on the current platform (so that we get a proper widevine esn) & enable gzip - self.session = requests.session() + self.session = session() self.session.headers.update({ 'User-Agent': self._get_user_agent_for_current_platform(), 'Accept-Encoding': 'gzip' @@ -272,7 +273,7 @@ class NetflixSession: """ payload = { 'switchProfileGuid': profile_id, - '_': int(time.time()), + '_': int(time()), 'authURL': self.user_data['authURL'] } @@ -280,14 +281,9 @@ class NetflixSession: if response.status_code != 200: return False - # fetch the index page again, so that we can fetch the corresponding user data - browse_response = self._session_get(component='browse') - only_script_tags = SoupStrainer('script') - browse_soup = BeautifulSoup(browse_response.text, 'html.parser', parse_only=only_script_tags) account_hash = self._generate_account_hash(account=account) self.user_data['guid'] = profile_id; - self._save_data(filename=self.data_path + '_' + account_hash) - return True + return self._save_data(filename=self.data_path + '_' + account_hash) def send_adult_pin (self, pin): """Send the adult pin to Netflix in case an adult rated video requests it @@ -454,13 +450,13 @@ class NetflixSession: video_lists = response_data['lists'] for video_list_id in video_lists.keys(): video_list = video_lists[video_list_id] - if video_list['context'] == 'genre': - video_list_ids['genres'].update(self.parse_video_list_ids_entry(id=video_list_id, entry=video_list)) - elif video_list['context'] == 'similars' or video_list['context'] == 'becauseYouAdded': - video_list_ids['recommendations'].update(self.parse_video_list_ids_entry(id=video_list_id, entry=video_list)) - else: - video_list_ids['user'].update(self.parse_video_list_ids_entry(id=video_list_id, entry=video_list)) - + if video_list.get('context', False) != False: + if video_list['context'] == 'genre': + video_list_ids['genres'].update(self.parse_video_list_ids_entry(id=video_list_id, entry=video_list)) + elif video_list['context'] == 'similars' or video_list['context'] == 'becauseYouAdded': + video_list_ids['recommendations'].update(self.parse_video_list_ids_entry(id=video_list_id, entry=video_list)) + else: + video_list_ids['user'].update(self.parse_video_list_ids_entry(id=video_list_id, entry=video_list)) return video_list_ids def parse_video_list_ids_entry (self, id, entry): @@ -1295,9 +1291,10 @@ class NetflixSession: 'toRow': list_to, 'opaqueImageExtension': 'jpg', 'transparentImageExtension': 'png', - '_': int(time.time()), + '_': int(time()), 'authURL': self.user_data['authURL'] } + response = self._session_get(component='video_list_ids', params=payload, type='api') return self._process_response(response=response, component=self._get_api_url_for(component='video_list_ids')) @@ -1321,7 +1318,7 @@ class NetflixSession: Raw Netflix API call response or api call error """ # properly encode the search string - encoded_search_string = urllib.quote(search_str) + encoded_search_string = quote(search_str) paths = [ ['search', encoded_search_string, 'titles', {'from': list_from, 'to': list_to}, ['summary', 'title']], @@ -1334,6 +1331,48 @@ class NetflixSession: response = self._path_request(paths=paths) return self._process_response(response=response, component='Search results') + def get_lolomo_for_kids (self): + """Fetches the lolomo ID for Kids profiles + + Returns + ------- + :obj:`str` + Kids Lolomo ID + """ + response = self._session_get(component='kids') + for cookie in response.cookies: + if cookie.name.find('lhpuuidh-browse-' + self.user_data['guid']) != -1 and cookie.name.rfind('-T') == -1: + start = unquote(cookie.value).rfind(':') + return unquote(cookie.value)[start+1:] + return None + + def fetch_lists_for_kids (self, lolomo, list_from=0, list_to=50): + """Fetches the JSON which contains the contents of a the video list for kids users + + Parameters + ---------- + lolomo : :obj:`str` + Lolomo ID for the Kids profile + + list_from : :obj:`int` + Start entry for pagination + + list_to : :obj:`int` + Last entry for pagination + + Returns + ------- + :obj:`dict` of :obj:`dict` of :obj:`str` + Raw Netflix API call response or api call error + """ + paths = [ + ['lists', lolomo, {'from': list_from, 'to': list_to}, ['displayName', 'context', 'genreId', 'id', 'index', 'length']] + ] + + response = self._path_request(paths=paths) + res = self._process_response(response=response, component='Kids lists') + return self.parse_video_list_ids(response_data=res['value']) + def fetch_video_list (self, list_id, list_from=0, list_to=20): """Fetches the JSON which contains the contents of a given video list @@ -1424,7 +1463,7 @@ class NetflixSession: payload = { 'movieid': id, 'imageformat': 'jpg', - '_': int(time.time()) + '_': int(time()) } response = self._session_get(component='metadata', params=payload, type='api') return self._process_response(response=response, component=self._get_api_url_for(component='metadata')) @@ -1568,8 +1607,6 @@ class NetflixSession: }) params = { - 'withSize': True, - 'materialize': True, 'model': self.user_data['gpsModel'] } @@ -1813,10 +1850,10 @@ class NetflixSession: return False with open(filename) as f: - cookies = pickle.load(f) - if cookies: - jar = requests.cookies.RequestsCookieJar() - jar._cookies = cookies + _cookies = pickle.load(f) + if _cookies: + jar = cookies.RequestsCookieJar() + jar._cookies = _cookies self.session.cookies = jar else: return False @@ -1849,7 +1886,7 @@ class NetflixSession: :obj:`str` Account data hash """ - return base64.urlsafe_b64encode(account['email']) + return urlsafe_b64encode(account['email']) def _get_user_agent_for_current_platform (self): """Determines the user agent string for the current platform (to retrieve a valid ESN) @@ -1895,9 +1932,9 @@ class NetflixSession: Contents of the field to match """ url = self._get_document_url_for(component=component) if type == 'document' else self._get_api_url_for(component=component) - start = time.time() + start = time() response = self.session.post(url=url, data=data, params=params, headers=headers, verify=self.verify_ssl) - end = time.time() + end = time() self.log('[POST] Request for "' + url + '" took ' + str(end - start) + ' seconds') return response @@ -1921,9 +1958,9 @@ class NetflixSession: Contents of the field to match """ url = self._get_document_url_for(component=component) if type == 'document' else self._get_api_url_for(component=component) - start = time.time() + start = time() response = self.session.get(url=url, verify=self.verify_ssl, params=params) - end = time.time() + end = time() self.log('[GET] Request for "' + url + '" took ' + str(end - start) + ' seconds') return response @@ -2184,9 +2221,9 @@ class NetflixSession: 'profileName', 'isActive', 'isFirstUse', - 'isAccountOwner' + 'isAccountOwner', + 'isKids' ] - # values are accessible via dict (sloppy parsing successfull) if type(netflix_page_data) == dict: for profile_id in netflix_page_data.get('profiles'): @@ -2296,5 +2333,5 @@ class NetflixSession: self.esn = self._parse_esn_data(netflix_page_data=netflix_page_data) self.api_data = self._parse_api_base_data(netflix_page_data=netflix_page_data) self.profiles = self._parse_profile_data(netflix_page_data=netflix_page_data) - self.log('Found ESN "' + self.esn) + self.log('Found ESN "' + self.esn + '"') return netflix_page_data