diff --git a/Contents/DefaultPrefs.json b/Contents/DefaultPrefs.json index 491ec69..2be601e 100644 --- a/Contents/DefaultPrefs.json +++ b/Contents/DefaultPrefs.json @@ -114,6 +114,12 @@ "label": "Включить отображение коллекций", "type": "bool", "default": "true" + }, + { + "id": "api_key", + "label": "Ключ https://kinopoiskapiunofficial.tech/", + "type":"text", + "default":"" }, { "id": "captcha_key", diff --git a/Contents/Libraries/Shared/kinoplex/const.py b/Contents/Libraries/Shared/kinoplex/const.py index 35e1082..ddf57b2 100644 --- a/Contents/Libraries/Shared/kinoplex/const.py +++ b/Contents/Libraries/Shared/kinoplex/const.py @@ -22,11 +22,13 @@ def __setattr__(self, attr, val): # sources section # kinopoisk api config['kinopoisk']['api']['base'] = 'https://ext.kinopoisk.ru/ios/5.0.0/%s' -config['kinopoisk']['api']['search'] = config.kinopoisk.api.base % 'getKPLiveSearch?keyword=%s' -config['kinopoisk']['api']['film_details'] = config.kinopoisk.api.base % 'getKPFilmDetailView?filmID=%s&still_limit=50&sr=1' +config['kinopoisk']['api']['search'] = 'https://kinopoiskapiunofficial.tech/api/v2.1/films/search-by-keyword?keyword=%s&page=%s' +config['kinopoisk']['api']['film_details'] = 'https://kinopoiskapiunofficial.tech/api/v2.2/films/%s' +config['kinopoisk']['api']['distributions'] = 'https://kinopoiskapiunofficial.tech/api/v2.2/films/%s/distributions' config['kinopoisk']['api']['list_films'] = config.kinopoisk.api.base % 'getKPFilmsList?filmID=%s&type=%s' -config['kinopoisk']['api']['staff'] = config.kinopoisk.api.base % 'getStaffList?filmID=%s&type=all' -config['kinopoisk']['api']['film_reviews'] = config.kinopoisk.api.base % 'getKPReviews?filmID=%s&type=0&sortType=0' +config['kinopoisk']['api']['staff'] = 'https://kinopoiskapiunofficial.tech/api/v1/staff?filmId=%s' +config['kinopoisk']['api']['similars'] = 'https://kinopoiskapiunofficial.tech/api/v2.2/films/%s/similars' +config['kinopoisk']['api']['film_reviews'] = 'https://kinopoiskapiunofficial.tech/api/v1/reviews?filmId=%s&page=1' config['kinopoisk']['api']['gallery'] = config.kinopoisk.api.base % 'getGallery?filmID=%s' config['kinopoisk']['api']['series'] = config.kinopoisk.api.base % 'getKPSeriesList?serialID=%s&season=%s&page=%s' config['kinopoisk']['api']['hash'] = 'IDATevHDS7' @@ -39,10 +41,11 @@ def __setattr__(self, attr, val): 'Accept': 'application/json', 'device': 'android', 'Android-Api-Version': '22', + 'X-API-KEY': '93fbd7a8-47d8-4c0d-a822-8615816c9536', 'User-Agent': 'Android client (4.4 / api22),ru.kinopoisk/4.2.1 (52)' } -config['kinopoisk']['main']['search'] = 'https://www.kinopoisk.ru/search/suggest/?q=%s&topsuggest=true&ajax=1' +config['kinopoisk']['main']['search'] = 'https://kinopoiskapiunofficial.tech/api/v2.1/films/search-by-keyword?keyword=%s&page=%s' config['kinopoisk']['main']['headers'] = lambda: { 'Referer': 'https://www.kinopoisk.ru', 'Accept': 'text/html, application/xhtml+xml, image/jxr, */*', @@ -50,6 +53,7 @@ def __setattr__(self, attr, val): 'Accept-Language': 'en-US,en;q=0.8,ru;q=0.7,uk;q=0.5,de-DE;q=0.3,de;q=0.2', 'User-agent': generate_user_agent(), 'X-Compress': 'null', + 'X-API-KEY': '93fbd7a8-47d8-4c0d-a822-8615816c9536', 'Upgrade-Insecure-Requests': '1' } @@ -58,8 +62,8 @@ def __setattr__(self, attr, val): config['kinopoisk']['images'] = '%s' config['kinopoisk']['imagesactor'] = 'https://st.kp.yandex.net/images/%s' config['kinopoisk']['actor'] = config.kinopoisk.imagesactor % 'actor_iphone/iphone360_%s.jpg' -config['kinopoisk']['thumb'] = config.kinopoisk.images % 'film_iphone/iphone360_%s.jpg' -config['kinopoisk']['poster'] = config.kinopoisk.images % 'film_big/%s.jpg' +config['kinopoisk']['thumb'] = 'https://kinopoiskapiunofficial.tech/images/posters/kp_small/%s.jpg' +config['kinopoisk']['poster'] = 'https://kinopoiskapiunofficial.tech/images/posters/kp/%s.jpg' config['kptrailers']['extras']['base'] = 'https://www.kinopoisk.ru/film/%s/video/' config['kptrailers']['extras']['re'] = "//table[ancestor::table[2]]//div/a[@class='all' and contains(@href,'/film/')]" diff --git a/Contents/Libraries/Shared/kinoplex/meta.py b/Contents/Libraries/Shared/kinoplex/meta.py index 3ecba3c..92d26e0 100644 --- a/Contents/Libraries/Shared/kinoplex/meta.py +++ b/Contents/Libraries/Shared/kinoplex/meta.py @@ -261,6 +261,8 @@ def parse_meta(metadata_dict, metadata, api): continue dict_value = metadata_dict[attr_name] + if dict_value is None: + continue try: if isinstance(dict_value, list): @@ -333,11 +335,11 @@ def prepare_meta(metadata_dict, metadata, app): meta_staff.photo = staff.get('photo', '') meta_staff.role = staff.get('role', '') - metadata.reviews.clear() - for review in metadata_dict.get('reviews', []): - r = metadata.reviews.new() - r.author = review.get('author') - r.source = review.get('source') - r.image = review.get('image') - r.link = review.get('link') - r.text = review.get('text') + if not (metadata_dict.get('reviews') is None): + for review in metadata_dict.get('reviews', []): + r = metadata.reviews.new() + r.author = review.get('author') + r.source = review.get('source') + r.image = review.get('image') + r.link = review.get('link') + r.text = review.get('text') diff --git a/Contents/Libraries/Shared/kinoplex/sources/base.pyc b/Contents/Libraries/Shared/kinoplex/sources/base.pyc new file mode 100644 index 0000000..bce9bb8 Binary files /dev/null and b/Contents/Libraries/Shared/kinoplex/sources/base.pyc differ diff --git a/Contents/Libraries/Shared/kinoplex/sources/fanart.py b/Contents/Libraries/Shared/kinoplex/sources/fanart.py index 9885968..8367e28 100644 --- a/Contents/Libraries/Shared/kinoplex/sources/fanart.py +++ b/Contents/Libraries/Shared/kinoplex/sources/fanart.py @@ -13,7 +13,7 @@ def update(self, metadata, media, lang, force=False, periodic=False): try: json = self.api.JSON.ObjectFromURL(self.conf.movie % metadata['meta_ids'].get('tmdb'), headers=self.conf.headers('')) except: - self.l.Debug('No data from FanArt.tv') + self.l.Debug('No data from FanArt.tv') if json: for key, value in json.items(): diff --git a/Contents/Libraries/Shared/kinoplex/sources/itunes.py b/Contents/Libraries/Shared/kinoplex/sources/itunes.py index 9ecf668..d3788c7 100644 --- a/Contents/Libraries/Shared/kinoplex/sources/itunes.py +++ b/Contents/Libraries/Shared/kinoplex/sources/itunes.py @@ -44,7 +44,9 @@ def _search(self, metadata, media): self.d('search for itunes on rottentomatoes') omdb = self.api.JSON.ObjectFromURL(self.conf.omdb % imdb_id) if 'tomatoURL' in omdb and omdb['tomatoURL'] != 'N/A': - page = self.api.HTML.ElementFromURL(omdb['tomatoURL'].replace('http://', 'https://'), headers=self.c.headers.all, cacheTime=0) + tomatoURL = omdb['tomatoURL'].replace('http://', 'https://') + tomatoURL = tomatoURL.replace('https://rottentomatoes.com', 'https://www.rottentomatoes.com') + page = self.api.HTML.ElementFromURL(tomatoURL, headers=self.c.headers.all, cacheTime=0) lnk = page.xpath(self.c.itunes.rt_re) if len(lnk) > 0: return re.search('(?<=id)[0-9]+', lnk[0]).group(0) diff --git a/Contents/Libraries/Shared/kinoplex/sources/kinopoisk.py b/Contents/Libraries/Shared/kinoplex/sources/kinopoisk.py index 5097abb..2fe8285 100644 --- a/Contents/Libraries/Shared/kinoplex/sources/kinopoisk.py +++ b/Contents/Libraries/Shared/kinoplex/sources/kinopoisk.py @@ -25,8 +25,9 @@ def get_name(self, media): return self.api.String.Quote(_name, False) def _suggest_search(self, matches, media_name): + page = 1 json = self._fetch_json( - self.conf.main.yasearch % media_name, + self.conf.main.yasearch % media_name % page, headers=self.conf.main.headers() ) json = json[2] if 2 <= len(json) else [] @@ -45,27 +46,52 @@ def _suggest_search(self, matches, media_name): return cnt def _main_search(self, matches, media_name): + page = 1 json = self._fetch_json( - self.conf.main.search % media_name, + self.conf.main.search % (media_name , page), headers=self.conf.main.headers() ) cnt = 0 + pagesCount = 0 + if json: - for i, movie in enumerate(json): - _year = movie['year'][:4] + for i, movie in enumerate(json['films']): + if movie['year'].isdigit(): + _year = movie['year'][:4] if _year: _year = int(_year) - if movie['link'].startswith('/film/') \ - and ( - (movie['type'] in ['film', 'first']) + if ((movie['type'] in ['FILM', 'FIRST', 'VIDEO'] and (movie.get('nameRu'))) or ('is_serial' in movie and movie['is_serial'] in ('serial', 'mini', 'TV'))) \ and _year <= self.api.Datetime.Now().year: - matches[str(movie['id'])] = [movie['rus'], movie['name'], movie['year'], i, - 5 if movie['type'] == 'first' else 0] + matches[str(movie['filmId'])] = [movie['nameRu'] if 'nameRu' in movie else movie['nameEn'], movie['nameEn'] if 'nameEn' in movie else movie['nameRu'], movie['year'], i, + 5 if movie['type'] == 'first' else 0] cnt = cnt + 1 + page = page + 1 + + if json.get('pagesCount'): + pagesCount = int(json.get('pagesCount')) + while page <= pagesCount and pagesCount < 21: + json = self._fetch_json( + self.conf.main.search % (media_name , page), + headers=self.conf.main.headers() + ) + for i, movie in enumerate(json['films']): + if movie['year'].isdigit(): + _year = movie['year'][:4] + if _year: + _year = int(_year) + if ((movie['type'] in ['FILM', 'FIRST', 'VIDEO'] and (movie.get('nameRu'))) + or ('is_serial' in movie and movie['is_serial'] in ('serial', 'mini', 'TV'))) \ + and _year <= self.api.Datetime.Now().year: + matches[str(movie['filmId'])] = [movie['nameRu'] if 'nameRu' in movie else movie['nameEn'], movie['nameEn'] if 'nameEn' in movie else movie['nameRu'], movie['year'], i, + 5 if movie['type'] == 'first' else 0] + cnt = cnt + 1 + page = page + 1 return cnt def _api_search(self, matches, media_name): + return self._main_search(matches, media_name) + json = self._fetch_json( self.conf.api.search % media_name, headers=self.conf.api.headers @@ -201,100 +227,102 @@ def search(self, results, media, lang, manual=False, primary=True): if manual: for result in results: result.thumb = self.conf.thumb % result.id - self.l(result.thumb) + #self.l(result) results.Sort('score', descending=True) def make_request(self, link, *args): data = {} try: data = self.api.JSON.ObjectFromURL( - link % args, headers=self.conf.api.headers) + link % args, headers=self.conf.api.headers) except: self.l.Error('Something goes wrong with request', exc_info=True) finally: - data = data.get('data', {}) + if not (isinstance(data, list)): + if data.get('data'): + data = data.get('data', {}) return data def update(self, metadata, media, lang, force=False, periodic=False): self.l('update KinopoiskSource') self.load_meta(metadata) + self.load_distribution(metadata) self.load_staff(metadata) self.load_reviews(metadata) - self.load_gallery(metadata) - - if metadata['has_similar']: - self.load_similar(metadata) - #if metadata['has_pre_sequel']: - # self.load_sequel(metadata) + self.load_similar(metadata) if self.app.agent_type == 'tv': self.load_series(metadata, media) + #self.load_gallery(metadata) + def load_meta(self, metadata): movie_data = self.make_request(self.conf.api.film_details, metadata['id']) + if not movie_data: return - + repls = (u' (видео)', u' (ТВ)', u' (мини-сериал)', u' (сериал)') - metadata['title'] = reduce(lambda a, kv: a.replace(kv, ''), repls, movie_data['nameRU']) + metadata['title'] = reduce(lambda a, kv: a.replace(kv, ''), repls, movie_data['nameRu']) - if 'nameEN' in movie_data and movie_data['nameEN'] != movie_data['nameRU']: - metadata['original_title'] = movie_data['nameEN'] + if 'nameOriginal' in movie_data and movie_data['nameOriginal'] != movie_data['nameRu']: + metadata['original_title'] = movie_data['nameOriginal'] metadata['tagline'] = movie_data.get('slogan', '') - metadata['content_rating_age'] = int(movie_data.get('ratingAgeLimits') or 0) + metadata['content_rating_age'] = int(movie_data.get('ratingAgeLimits').replace('age', '') or 0) try: - metadata['year'] = int(movie_data.get('year', '').split('-', 1)[0] or 0) + metadata['year'] = int(movie_data.get('year', '')) except: self.l('error converting year (%s) to int', movie_data.get('year')) metadata['countries'] = [] - if 'country' in movie_data: - for country in movie_data.get('country', '').split(', '): - metadata['countries'].append(country) + if 'countries' in movie_data: + for country in movie_data.get('countries', []): + metadata['countries'].append(country['country']) metadata['genres'] = [] - for genre in movie_data.get('genre', '').split(', '): - metadata['genres'].append(genre.strip().title()) + for genre in movie_data.get('genres', []): + metadata['genres'].append(genre['genre']) if self.api.Prefs['content_rating'] == "MPAA": - metadata['content_rating'] = movie_data.get('ratingMPAA', '') + metadata['content_rating'] = movie_data.get('ratingMpaa', '') elif self.api.Prefs['content_rating'] == "Возраст" and movie_data.get('ratingAgeLimits'): metadata['content_rating'] = '%s+' % movie_data.get('ratingAgeLimits') - metadata['originally_available_at'] = self.api.Datetime.ParseDate( - ( - movie_data['rentData'].get('premiereWorld') or movie_data['rentData'].get('premiereRU') - ).replace('00.', '01.'), '%d.%m.%Y' - ).date() if (('rentData' in movie_data) and - [i for i in {'premiereWorld', 'premiereRU'} if - i in movie_data['rentData'] and len(movie_data['rentData'][i]) == 10] - ) else None - metadata['ratings']['kp'] = float(movie_data.get('ratingData', {}).get('rating', 0)) - metadata['ratings']['imdb'] = float(movie_data.get('ratingData', {}).get('ratingIMDb', 0)) + if movie_data.get('premiereWorld'): + metadata['originally_available_at'] = self.api.Datetime.ParseDate( + ( + movie_data['rentData'].get('premiereWorld') or movie_data['rentData'].get('premiereRU') + ).replace('00.', '01.'), '%d.%m.%Y' + ).date() if (('rentData' in movie_data) and + [i for i in {'premiereWorld', 'premiereRU'} if + i in movie_data['rentData'] and len(movie_data['rentData'][i]) == 10] + ) else None + + metadata['ratings']['kp'] = float(movie_data.get('ratingKinopoisk', 0)) + metadata['ratings']['imdb'] = float(movie_data.get('ratingImdb', 0)) summary_add = '' if self.api.Prefs['desc_show_slogan'] and movie_data.get('slogan'): summary_add += '%s\n' % movie_data.get('slogan') - if self.api.Prefs['desc_rating_kp'] and movie_data.get('ratingData', {}).get('rating'): - summary_add += u'КиноПоиск: %s' % movie_data['ratingData']['rating'] - if self.api.Prefs['desc_rating_vote_count'] and movie_data['ratingData'].get('ratingVoteCount'): - summary_add += ' (%s)' % movie_data['ratingData']['ratingVoteCount'] + if movie_data.get('ratingKinopoisk'): + summary_add += u'КиноПоиск: %s' % movie_data.get('ratingKinopoisk') + if movie_data.get('ratingKinopoiskVoteCount'): + summary_add += ' (%s)' % movie_data.get('ratingKinopoiskVoteCount') summary_add += '\n' if self.api.Prefs['desc_rating_newline'] else '. ' - if self.api.Prefs['desc_rating_imdb'] and movie_data.get('ratingData', {}).get('ratingIMDb'): - summary_add += u'IMDb: %s' % movie_data['ratingData']['ratingIMDb'] - if self.api.Prefs['desc_rating_vote_count'] and movie_data['ratingData'].get('ratingIMDbVoteCount'): - summary_add += ' (%s)' % movie_data['ratingData']['ratingIMDbVoteCount'] + if movie_data.get('ratingImdb'): + summary_add += u'IMDB: %s' % movie_data.get('ratingImdb') + if movie_data.get('ratingImdbVoteCount'): + summary_add += ' (%s)' % movie_data.get('ratingImdbVoteCount') summary_add += '\n' if self.api.Prefs['desc_rating_newline'] else '. ' metadata['summary'] = summary_add + movie_data.get('description', '') - - metadata['main_trailer'] = movie_data.get('videoURL', {}).get('hd', '') + metadata['main_poster'] = { - 'full': self.conf.poster % metadata['id'], - 'thumb': self.conf.thumb % metadata['id'] + 'full': movie_data.get('posterUrl'), + 'thumb': movie_data.get('posterUrlPreview') } if 'itunes' in movie_data: @@ -304,6 +332,20 @@ def load_meta(self, metadata): metadata['has_similar'] = movie_data.get('hasSimilarFilms', 0) metadata['seriesInfo'] = movie_data.get('seriesInfo', {}) + def load_distribution(self, metadata): + self.l('load distribution from kinopoisk') + distribution_data = self.make_request(self.conf.api.distributions, metadata['id']) + + if not distribution_data: + return + + if not distribution_data.get('items'): + return + + for distr in distribution_data.get('items', []): + if distr.get('type') == 'WORLD_PREMIER': + metadata['originally_available_at'] = self.api.Datetime.ParseDate((distr.get('date')).replace('00.', '01.'), '%Y-%m-%d').date() + def load_staff(self, metadata): self.l('load staff from kinopoisk') staff_data = self.make_request(self.conf.api.staff, metadata['id']) @@ -319,15 +361,14 @@ def load_staff(self, metadata): type_map = {'actor': 'roles', 'director': 'directors', 'writer': 'writers', 'producer': 'producers'} - for staff_type in staff_data.get('creators', []): - for staff in staff_type: - if type_map.get(staff.get('professionKey'), {}): - people[type_map[staff.get('professionKey')]].append(dict( - nameRU=staff.get('nameRU'), # staff name - nameEN=staff.get('nameEN'), # staff name - photo=self.conf.actor % staff['id'] if 'posterURL' in staff else None, - role=" ".join(re.sub(r'\([^)]*\)', '', staff.get('description', '')).split()) - )) + for staff in staff_data: + if type_map.get(staff.get('professionKey').lower(), {}): + people[type_map[staff.get('professionKey').lower()]].append(dict( + nameRU=staff.get('nameRu'), # staff name + nameEN=staff.get('nameEn'), # staff name + photo=staff.get('posterUrl') if 'posterUrl' in staff else None, + role=staff.get('description', '') + )) del people def load_reviews(self, metadata): @@ -337,23 +378,30 @@ def load_reviews(self, metadata): reviews = metadata['reviews']['kp'] = [] for review in reviews_dict.get('reviews', []): + image = '' + if review.get('reviewType') == 'NEGATIVE': + image = 'rottentomatoes://image.review.rotten' + if review.get('reviewType') == 'POSITIVE': + image = 'rottentomatoes://image.review.fresh' reviews.append({ 'author': review.get('reviewAutor'), 'source': 'Kinopoisk', + 'image': image, 'text': re.sub(r'[\x00-\x08\x0b\x0c\x0e]', '', review.get('reviewDescription')) }) del reviews def load_similar(self, metadata): self.l('load similar from kinopoisk') - similar_data = self.make_request(self.conf.api.list_films, metadata['id'], 'kp_similar_films') + similar_data = self.make_request(self.conf.api.similars, metadata['id']) if not similar_data: return metadata['similar'] = [] for similar in similar_data.get('items', []): - metadata['similar'].append(similar['nameRU']) + if similar.get('nameRu'): + metadata['similar'].append(similar['nameRu']) def load_sequel(self, metadata): self.l('load sequel from kinopoisk') diff --git a/Contents/Libraries/Shared/kinoplex/sources/tmdb.py b/Contents/Libraries/Shared/kinoplex/sources/tmdb.py index 77a98ee..5fb1138 100644 --- a/Contents/Libraries/Shared/kinoplex/sources/tmdb.py +++ b/Contents/Libraries/Shared/kinoplex/sources/tmdb.py @@ -89,7 +89,7 @@ def score_hash(self, metadata, matches): # Store the final score in the result vector. matches[key][5] = int(INITIAL_SCORE - dist - score_penalty) - def get_hash_results(self, meta, matches, search_type='hash', plex_hash='', lang='en'): + def get_hash_results(self, meta, matches, search_type='hash', plex_hash='', lang='en'): if search_type is 'hash' and plex_hash is not None: url = '%s/movie/hash/%s/%s.xml' % (self.conf.hash_base, plex_hash[0:2], plex_hash) else: diff --git a/Contents/VERSION b/Contents/VERSION new file mode 100644 index 0000000..2e7d938 --- /dev/null +++ b/Contents/VERSION @@ -0,0 +1 @@ +663f5d8 \ No newline at end of file diff --git a/README.md b/README.md index 53cfce8..164f20f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ПЛАГИН ВРЕМЕННО НЕ РАБОТАЕТ ИЗ-ЗА ИЗМЕНЕНИЙ API Кинопоиска! ПРОСЬБА НЕ СОЗДАВАТЬ ОДИНАКОВЫЕ СООБЩЕНИЯ +# ПЛАГИН ПЕРЕДЕЛАН НА РАБОТУ С НЕОФИЦИАЛЬНЫМ API kinopoiskapiunofficial.tech # Kinopoisk.bundle