def _process_data_album_dict(hm, lastfm, alb_name, a_name, do_whichone, mi=None, process_to_end=True): assert ( do_whichone in ('GRACENOTE', 'LASTFM', 'MUSICBRAINZ') ), "error, metadata service = %s should be one of GRACENOTE, LASTFM, or MUSICBRAINZ" % do_whichone if do_whichone == 'GRACENOTE': album_data_dict, status = hm.get_music_metadatas_album( a_name, alb_name) if status == 'SUCCESS': return album_data_dict, status if not_process_to_end: return return_error_raw(status) # ## now lastfm album_data_dict, status = lastfm.get_music_metadatas_album( a_name, alb_name) if status == 'SUCCESS': return album_data_dict, status # ## now musicbrainz if mi is not None: assert (mi.artist_name == a_name) else: mi = music.MusicInfo(a_name) album_data_dict, status = mi.get_music_metadatas_album(alb_name) if status == 'SUCCESS': return album_data_dict, status return return_error_raw(status) elif do_whichone == 'LASTFM': album_data_dict, status = lastfm.get_music_metadatas_album( a_name, alb_name) if status == 'SUCCESS': return album_data_dict, status if not process_to_end: return return_error_raw(status) # ## now musicbrainz if mi is not None: assert (mi.artist_name == a_name) else: mi = music.MusicInfo(a_name) album_data_dict, status = mi.get_music_metadatas_album(alb_name) if status == 'SUCCESS': return album_data_dict, status return return_error_raw(status) else: if mi is not None: assert (mi.artist_name == a_name) else: mi = music.MusicInfo(a_name) album_data_dict, status = mi.get_music_metadatas_album(alb_name) if status == 'SUCCESS': return album_data_dict, status return return_error_raw(status)
def get_default_tvlibrary(fullURL, token): try: all_libraries = core.get_libraries(token=token, fullURL=fullURL, do_full=True) except: return return_error_raw("Error, bad token or URL may have been given.") key_found = list( filter(lambda key: all_libraries[key][1] == 'show', all_libraries)) if key_found is None: return_error_raw("Error, could not find any TV libraries.") key_found = min(key_found) return all_libraries[key_found][0], 'SUCCESS'
def set_excluded_shows(tvdata, shows_to_exclude): shows_act = set(shows_to_exclude) & set(tvdata) if len(shows_act) == 0: return return_error_raw( "Error, the set of TV shows, %s, is not in any of %d TV shows in Plex library." % (sorted(shows_act), len(set(tvdata)))) # ## tv.push_shows_to_exclude(tvdata, shows_act)
def get_tvdata(tvlibraryname, fullURL, token, doCheck=True): if doCheck: try: all_libraries = core.get_libraries(token=token, fullURL=fullURL, do_full=True) except: return return_error_raw( "Error, bad token or URL may have been given.") key_found = list( filter(lambda key: all_libraries[key][0] == tvlibraryname, all_libraries)) if key_found is None: return return_error_raw("Error, %s library does not exist." % tvlibraryname) key_found = min(key_found) if all_libraries[key_found][1] != 'show': return return_error_raw("Error, %s library is not a TV library." % tvlibraryname) # ## now get data tvdata = core.get_library_data(tvlibraryname, token=token, fullURL=fullURL) return tvdata, 'SUCCESS'
def get_movie_torrent(name, verify=True): """ Returns a :py:class:`tuple` of candidate movie found using `Torrent files <torrent file_>`_ using the `YTS API`_ torrent service and the string ``"SUCCESS"``, if successful. :param str name: the movie string on which to search. :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``. :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of `Torrent files <torrent file_>`_ that match the searched movie, ordered from *earliest* to *latest* year of release. The second element is the string ``"SUCCESS"``. If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`. :rtype: tuple .. _`YTS API`: https://yts.ag/api .. _`torrent file`: https://en.wikipedia.org/wiki/Torrent_file """ mainURL = 'https://yts.ag/api/v2/list_movies.json' params = {'query_term': name, 'order_by': 'year'} response = requests.get(mainURL, params=params, verify=verify) if response.status_code != 200: return return_error_raw( 'Error, could not use the movie service. Exiting...') data = response.json()['data'] if 'movies' not in data or len(data['movies']) == 0: return return_error_raw("Could not find %s, exiting..." % name) movies = data['movies'] return movies, 'SUCCESS'
def get_book_torrent_jackett(name, maxnum=10, keywords=[], verify=True): """ Returns a :py:class:`tuple` of candidate book Magnet links found using the main Jackett_ torrent searching service and the string ``"SUCCESS"``, if successful. :param str name: the book to search for. :param int maxnum: optional argumeent, the maximum number of magnet links to return. Default is 10. Must be :math:`\ge 5`. :param list keywords: optional argument. If not empty, the title of the candidate element must have at least one of the keywords in ``keywords``. :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``. :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched episode, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are, * ``title`` is the name of the candidate book to download, and in parentheses the size of the candidate in MB or GB. * ``rawtitle`` also the name of the candidate episode to download. * ``seeders`` is the number of seeds for this Magnet link. * ``leechers`` is the number of leeches for this Magnet link. * ``link`` is the Magnet URI link. * ``torrent_size`` is the size of this torrent in Megabytes. If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`. :rtype: tuple .. _Jackett: https://github.com/Jackett/Jackett """ import validators assert (maxnum >= 5) data = core.get_jackett_credentials() if data is None: return return_error_raw( 'FAILURE, COULD NOT GET JACKETT SERVER CREDENTIALS') url, apikey = data if not url.endswith('/'): url = '%s/' % url endpoint = 'api/v2.0/indexers/all/results/torznab/api' name_split = name.split() last_tok = name_split[-1].lower() status = re.match('^s[0-9]{2}e[0-9]{2}', last_tok) def _return_params(name): params = {'apikey': apikey, 'q': name, 'cat': '7020'} return params logging.info('URL ENDPOINT: %s, PARAMS = %s.' % (urljoin(url, endpoint), { 'apikey': apikey, 'q': name, 'cat': '7020' })) response = requests.get(urljoin(url, endpoint), params={ 'apikey': apikey, 'q': name, 'cat': '7020' }, verify=verify) # tv shows if response.status_code != 200: return return_error_raw( 'FAILURE, PROBLEM WITH JACKETT SERVER ACCESSIBLE AT %s.' % url) html = BeautifulSoup(response.content, 'lxml') if len(html.find_all('item')) == 0: return return_error_raw( 'FAILURE, NO BOOKS SATISFYING CRITERIA FOR GETTING %s' % name) items = [] def _get_magnet_url(item): magnet_url = item.find('torznab:attr', {'name': 'magneturl'}) if magnet_url is not None and 'magnet' in magnet_url['value']: return magnet_url['value'] # ## not found it here, must go into URL url2 = item.find('guid') if url2 is None: return None url2 = url2.text if not validators.url(url2): return None resp2 = requests.get(url2, verify=verify) if resp2.status_code != 200: return None h2 = BeautifulSoup(resp2.content, 'lxml') valid_magnet_links = set( map( lambda elem: elem['href'], filter( lambda elem: 'href' in elem.attrs and 'magnet' in elem[ 'href'], h2.find_all('a')))) if len(valid_magnet_links) == 0: return None return max(valid_magnet_links) if status is None: last_tok = None for item in html('item'): title = item.find('title') if title is None: continue title = title.text # ## now check if the sXXeYY in name if last_tok is not None: if last_tok not in title.lower(): continue torrent_size = item.find('size') if torrent_size is not None: torrent_size = float(torrent_size.text) / 1024**2 seeders = item.find('torznab:attr', {'name': 'seeders'}) if seeders is None: seeders = -1 else: seeders = int(seeders['value']) leechers = item.find('torznab:attr', {'name': 'peers'}) if leechers is None: leechers = -1 else: leechers = int(leechers['value']) # ## now do one of two things to get the magnet URL magnet_url = _get_magnet_url(item) if magnet_url is None: continue myitem = { 'title': title, 'rawtitle': title, 'seeders': seeders, 'leechers': leechers, 'link': magnet_url } if torrent_size is not None: myitem['title'] = '%s (%0.1f MiB)' % (title, torrent_size) myitem['torrent_size'] = torrent_size pubdate_elem = item.find('pubdate') if pubdate_elem is not None: try: pubdate_s = pubdate_elem.get_text().split(',')[-1].strip() pubdate_s = ' '.join(pubdate_s.split()[:3]) pubdate = datetime.datetime.strptime(pubdate_s, '%d %B %Y').date() myitem['pubdate'] = pubdate except: pass items.append(myitem) items = sorted(items, key=lambda elem: elem['seeders'] + elem['leechers'])[::-1] if len(keywords) != 0: items = list( filter( lambda item: any( map(lambda tok: tok.lower() in item['rawtitle'].lower( ), keywords)) and not any( map( lambda tok: tok.lower() in item['rawtitle'].lower( ), keywords_exc)) and all( map( lambda tok: tok.lower() in item['rawtitle'] .lower(), must_have)), items)) if len(items) == 0: return return_error_raw( 'FAILURE, NO BOOKS SATISFYING CRITERIA FOR GETTING %s' % name) return items[:maxnum], 'SUCCESS'
def get_movie_torrent_zooqle(name, maxnum=10, verify=True): """ Returns a :py:class:`tuple` of candidate movie Magnet links found using the Zooqle_ torrent service and the string ``"SUCCESS"``, if successful. :param str name: the movie string on which to search. :param int maxnum: optional argument, the maximum number of magnet links to return. Default is 100. Must be :math:`\ge 5`. :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``. :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched movie, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are, * ``title`` is the name of the candidate movie to download, and in parentheses is the size of the download in MB or GB. * ``rawtitle`` is *only* the name of the candidate movie to download. * ``seeders`` is the number of seeds for this Magnet link. * ``leechers`` is the number of leeches for this Magnet link. * ``link`` is the Magnet URI link. * ``torrent_size`` is the size of this torrent in MB. If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`. :rtype: tuple .. _Zooqle: https://zooqle.com """ assert (maxnum >= 5) names_of_trackers = map( lambda tracker: tracker.replace(':', '%3A').replace('/', '%2F'), [ 'udp://tracker.opentrackr.org:1337/announce', 'udp://open.demonii.com:1337', 'udp://tracker.pomf.se:80/announce', 'udp://torrent.gresille.org:80/announce', 'udp://11.rarbg.com/announce', 'udp://11.rarbg.com:80/announce', 'udp://open.demonii.com:1337/announce', 'udp://tracker.openbittorrent.com:80', 'http://tracker.ex.ua:80/announce', 'http://tracker.ex.ua/announce', 'http://bt.careland.com.cn:6969/announce', 'udp://glotorrents.pw:6969/announce' ]) tracklist = ''.join( map(lambda tracker: '&tr=%s' % tracker, names_of_trackers)) def _get_magnet_link(info_hash, title): download_url = "magnet:?xt=urn:btih:" + info_hash + "&dn=" + '+'.join( title.split()) + tracklist return download_url candname = re.sub("'", '', name) url = 'https://zooqle.com/search' params = { 'q': '+'.join(candname.split() + [ 'category%3AMovie', ]), 'fmt': 'rss' } paramurl = '?' + '&'.join( map(lambda tok: '%s=%s' % (tok, params[tok]), params)) fullurl = urljoin(url, paramurl) response = requests.get(fullurl, verify=verify) if response.status_code != 200: return return_error_raw( 'ERROR, COULD NOT FIND ZOOQLE TORRENTS FOR %s' % candname) myxml = BeautifulSoup(response.content, 'lxml') def is_valid_elem(elem): names = set(map(lambda elm: elm.name, elem.find_all())) return len(names & set([ 'torrent:infohash', 'torrent:seeds', 'torrent:peers', 'torrent:contentlength' ])) == 4 cand_items = list( filter( lambda elem: len(elem.find_all('title')) >= 1 and is_valid_elem( elem) and get_maximum_matchval( max(elem.find_all('title')).get_text(), candname) >= 80, myxml.find_all('item'))) def get_num_forelem(elem, name): valid_elm = list( filter(lambda elm: elm.name == 'torrent:%s' % name, elem)) if len(valid_elm) == 0: return None valid_elm = valid_elm[0] return int(valid_elm.get_text()) def get_infohash(elem): valid_elm = list( filter(lambda elm: elm.name == 'torrent:infohash', elem)) if len(valid_elm) == 0: return None valid_elm = valid_elm[0] return valid_elm.get_text().lower() items_toshow = list( map( lambda elem: { 'title': '%s (%s)' % (max(elem.find_all('title')).get_text(), get_formatted_size(get_num_forelem(elem, 'contentlength'))), 'raw_title': max(elem.find_all('title')).get_text(), 'seeders': get_num_forelem(elem, 'seeds'), 'leechers': get_num_forelem(elem, 'peers'), 'link': _get_magnet_link(get_infohash(elem), max(elem.find_all('title')).get_text()), 'torrent_size': float(get_num_forelem(elem, 'contentlength') * 1.0 / 1024**2) }, cand_items)) if len(items_toshow) == 0: return return_error_raw( 'ERROR, COULD NOT FIND ZOOQLE TORRENTS FOR %s' % candname) return sorted(items_toshow, key=lambda item: -item['seeders'] - item['leechers'] )[:maxnum], 'SUCCESS'
def get_movie_torrent_eztv_io(name, maxnum=10, verify=True, tmdb_id=None): """ Returns a :py:class:`tuple` of candidate movie Magnet links found using the `EZTV.IO`_ torrent service and the string ``"SUCCESS"``, if successful. :param str name: the movie on which to search. :param int maxnum: optional argument, the maximum number of magnet links to return. Default is 10. Must be :math:`\ge 5`. :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``. :param str tmdb_id: optional argument. The TMDB_ ID of the movie. :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched movie, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are, * ``title`` is the name of the candidate movie to download, and in parentheses is the size of the download in MB or GB. * ``rawtitle`` is *only* the name of the candidate movie to download. * ``seeders`` is the number of seeds for this Magnet link. * ``leechers`` is the number of leeches for this Magnet link. * ``link`` is the Magnet URI link. * ``torrent_size`` is the size of this torrent in MB. If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`. :rtype: tuple .. warning:: As of |date|, cannot get it to work when giving it valid movie searches, such as ``"Star Trek Beyond"``. See :numref:`table_working_movietorrents`. .. _`EZTV.IO`: https://eztv.io .. _`Star Trek Beyond`: https://en.wikipedia.org/wiki/Star_Trek_Beyond """ assert (maxnum >= 5) if tmdb_id is None: tmdb_id = movie.get_movie_tmdbids(name, verify=verify) if tmdb_id is None: return return_error_raw('FAILURE, COULD NOT FIND IMDB ID FOR %s.' % name) # ## check that the name matches movie_name = movie.get_movie_info(tmdb_id, verify=verify)['title'].lower().strip() if movie_name != name.lower().strip(): return return_error_raw('FAILURE, COULD NOT FIND IMDB ID FOR %s.' % name) imdb_id = movie.get_imdbid_from_id(tmdb_id, verify=verify) if imdb_id is None: return return_error_raw('FAILURE, COULD NOT FIND IMDB ID FOR %s.' % name) response = requests.get('https://eztv.io/api/get-torrents', params={ 'imdb_id': int(imdb_id.replace('t', '')), 'limit': 100, 'page': 0 }, verify=verify) if response.status_code != 200: return return_error_raw( 'ERROR, COULD NOT FIND ANY TORRENTS FOR %s IN EZTV.IO' % name) alldat = response.json() if alldat['torrents_count'] == 0: return return_error_raw( 'ERROR, COULD NOT FIND ANY TORRENTS FOR %s IN EZTV.IO' % name) all_torrents = alldat['torrents'] for pageno in range(1, 101): if alldat['torrents_count'] < 100: break response = requests.get('https://eztv.io/api/get-torrents', params={ 'imdb_id': int(imdb_id.replace('t', '')), 'limit': 100, 'page': pageno }, verify=verify) if response.status_code != 200: break alldat = response.json() if alldat['torrents_count'] == 0: break all_torrents += alldat['torrents'] all_torrents_mine = all_torrents[:maxnum] if len(all_torrents_mine) == 0: return return_error_raw('ERROR, COULD NOT FIND %s IN EZTV.IO' % name) return list( map( lambda tor: { 'raw_title': tor['title'], 'title': '%s (%s)' % (tor['title'], get_formatted_size(tor['size_bytes'])), 'seeders': int(tor['seeds']), 'leechers': int(tor['peers']), 'link': tor['magnet_url'], 'torrent_size': float(tor['size_bytes']) / 1024**2 }, all_torrents_mine)), 'SUCCESS'
def get_movie_torrent_jackett(name, maxnum=10, verify=True, doRaw=False, tmdb_id=None): """ Returns a :py:class:`tuple` of candidate movie Magnet links found using the main Jackett_ torrent searching service and the string ``"SUCCESS"``, if successful. :param str name: the movie string on which to search. :param int maxnum: optional argumeent, the maximum number of magnet links to return. Default is 10. Must be :math:`\ge 5`. :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``. :param bool doRaw: optional argument. If ``True``, uses the IMDb_ information to search for the movie. Otherwise, uses the full string in ``name`` to search for the movie. :param int tmdb_id: optional argument. If defined, use this TMDB_ movie ID to search for magnet links. :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched movie, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are, * ``title`` is the name of the candidate movie to download, and in parentheses the size of the candidate in MB or GB. * ``rawtitle`` is *only* the name of the candidate movie to download. * ``seeders`` is the number of seeds for this Magnet link. * ``leechers`` is the number of leeches for this Magnet link. * ``link`` is the Magnet URI link. * ``torrent_size`` is the size of this torrent in GB. If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`. :rtype: tuple .. _Jackett: https://github.com/Jackett/Jackett """ time0 = time.time() data = core.get_jackett_credentials() if data is None: return return_error_raw( 'failure, could not get jackett server credentials') url, apikey = data endpoint = 'api/v2.0/indexers/all/results/torznab/api' popName = False if tmdb_id is not None: popName = True def _return_params(name, popName, tmdb_id): params = {'apikey': apikey, 'cat': 2000} if tmdb_id is not None: imdb_id = movie.get_imdbid_from_id(tmdb_id, verify=verify) params['imdbid'] = imdb_id return params elif doRaw: params['q'] = name return params tmdb_id = movie.get_movie_tmdbids(name, verify=verify) #if tmdb_id is None or doRaw and not popName: # params['q'] = name # return params # ## check that the name matches movie_name = movie.get_movie_info( tmdb_id, verify=verify)['title'].lower().strip() if movie_name != name.lower().strip(): params['q'] = name return params imdb_id = movie.get_imdbid_from_id(tmdb_id, verify=verify) if imdb_id is None: params['q'] = name return params params['imdbid'] = imdb_id return params params = _return_params(name, popName, tmdb_id) if popName and 'q' in params: params.pop('q') logging.info('params: %s, mainURL = %s' % (params, urljoin(url, endpoint))) response = requests.get(urljoin(url, endpoint), verify=verify, params=params) if response.status_code != 200: return return_error_raw(' '.join([ 'failure, problem with jackett server accessible at %s.' % url, 'Error code = %d. Error data = %s.' % (response.status_code, response.content) ])) logging.info('processed jackett torrents for %s in %0.3f seconds.' % (name, time.time() - time0)) html = BeautifulSoup(response.content, 'lxml') if len(html.find_all('item')) == 0: return return_error_raw( 'failure, could not find movie %s with jackett.' % name) items = [] def _get_magnet_url(item): magnet_url = item.find('torznab:attr', {'name': 'magneturl'}) if magnet_url is not None and 'magnet' in magnet_url['value']: return magnet_url['value'] # ## not found it here, must go into URL url2 = item.find('guid') if url2 is None: return None url2 = url2.text if not validators.url(url2): return None resp2 = requests.get(url2, verify=verify) if resp2.status_code != 200: return None h2 = BeautifulSoup(resp2.content, 'lxml') valid_magnet_links = set( map( lambda elem: elem['href'], filter( lambda elem: 'href' in elem.attrs and 'magnet' in elem[ 'href'], h2.find_all('a')))) if len(valid_magnet_links) == 0: return None return max(valid_magnet_links) for item in html.find_all('item'): title = item.find('title') if title is None: continue title = title.text torrent_size = item.find('size') if torrent_size is not None: torrent_size = float(torrent_size.text) / 1024**2 seeders = item.find('torznab:attr', {'name': 'seeders'}) if seeders is None: seeders = -1 else: seeders = int(seeders['value']) leechers = item.find('torznab:attr', {'name': 'peers'}) if leechers is None: leechers = -1 else: leechers = int(leechers['value']) # ## now do one of two things to get the magnet URL magnet_url = _get_magnet_url(item) if magnet_url is None: continue myitem = { 'raw_title': title, 'title': title, 'seeders': seeders, 'leechers': leechers, 'link': magnet_url } if torrent_size is not None: myitem['title'] = '%s (%s)' % ( title, get_formatted_size(torrent_size * 1024**2)) myitem['torrent_size'] = torrent_size items.append(myitem) if len(items) == 0: return return_error_raw('FAILURE, JACKETT CANNOT FIND %s' % name) return items[:maxnum], 'SUCCESS'