Beispiel #1
0
 def get_items(self, method, data, language, status_message=True):
     pretty = util.pretty_names[
         method] if method in util.pretty_names else method
     movie_ids = []
     if status_message:
         logger.info(f"Processing {pretty}: {data}")
     items = self.parse_list(data, language)
     total_items = len(items)
     if total_items == 0:
         raise Failed(f"Letterboxd Error: No List Items found in {data}")
     length = 0
     for i, item in enumerate(items, 1):
         length = util.print_return(length,
                                    f"Finding TMDb ID {i}/{total_items}")
         tmdb_id = None
         expired = None
         if self.config.Cache:
             tmdb_id, expired = self.config.Cache.query_letterboxd_map(
                 item[0])
         if not tmdb_id or expired is not False:
             try:
                 tmdb_id = self.get_tmdb_from_slug(item[1], language)
             except Failed as e:
                 logger.error(e)
                 continue
             if self.config.Cache:
                 self.config.Cache.update_letterboxd(
                     expired, item[0], tmdb_id)
         movie_ids.append(tmdb_id)
     util.print_end(length, f"Processed {total_items} TMDb IDs")
     if status_message:
         logger.debug(f"TMDb IDs Found: {movie_ids}")
     return movie_ids, []
 def map_guids(self, library):
     movie_map = {}
     show_map = {}
     length = 0
     count = 0
     logger.info("Mapping {} Library: {}".format("Movie" if library.is_movie else "Show", library.name))
     items = library.Plex.all()
     for i, item in enumerate(items, 1):
         length = util.print_return(length, "Processing: {}/{} {}".format(i, len(items), item.title))
         try:
             id_type, main_id = self.get_id(item, library, length)
         except BadRequest:
             util.print_stacktrace()
             util.print_end(length, "{} {:<46} | {} for {}".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, error_message, item.title))
             continue
         if isinstance(main_id, list):
             if id_type == "movie":
                 for m in main_id:                               movie_map[m] = item.ratingKey
             elif id_type == "show":
                 for m in main_id:                               show_map[m] = item.ratingKey
         else:
             if id_type == "movie":                          movie_map[main_id] = item.ratingKey
             elif id_type == "show":                         show_map[main_id] = item.ratingKey
     util.print_end(length, "Processed {} {}".format(len(items), "Movies" if library.is_movie else "Shows"))
     return movie_map, show_map
Beispiel #3
0
 def _genre(self, genre_id, limit):
     data = self._jiken_request(f"/genre/anime/{genre_id}")
     if "item_count" not in data:
         raise Failed(
             f"MyAnimeList Error: No MyAnimeList IDs for Genre ID: {genre_id}"
         )
     total_items = data["item_count"]
     if total_items < limit or limit <= 0:
         limit = total_items
     mal_ids = []
     num_of_pages = math.ceil(int(limit) / 100)
     current_page = 1
     chances = 0
     while current_page <= num_of_pages:
         if chances > 6:
             logger.debug(data)
             raise Failed("AniList Error: Connection Failed")
         start_num = (current_page - 1) * 100 + 1
         util.print_return(
             f"Parsing Page {current_page}/{num_of_pages} {start_num}-{limit if current_page == num_of_pages else current_page * 100}"
         )
         if current_page > 1:
             data = self._jiken_request(
                 f"/genre/anime/{genre_id}/{current_page}")
         if "anime" in data:
             chances = 0
             mal_ids.extend([anime["mal_id"] for anime in data["anime"]])
             if len(mal_ids) > limit:
                 return mal_ids[:limit]
             current_page += 1
         else:
             chances += 1
     util.print_end()
     return mal_ids
Beispiel #4
0
    def get_imdb_ids_from_url(self, imdb_url, language, limit):
        imdb_url = imdb_url.strip()
        if not imdb_url.startswith(
                self.urls["list"]) and not imdb_url.startswith(
                    self.urls["search"]):
            raise Failed(
                f"IMDb Error: {imdb_url} must begin with either:\n| {self.urls['list']} (For Lists)\n| {self.urls['search']} (For Searches)"
            )

        if imdb_url.startswith(self.urls["list"]):
            try:
                list_id = re.search("(\\d+)", str(imdb_url)).group(1)
            except AttributeError:
                raise Failed(
                    f"IMDb Error: Failed to parse List ID from {imdb_url}")
            current_url = f"{self.urls['search']}lists=ls{list_id}"
        else:
            current_url = imdb_url
        header = {"Accept-Language": language}
        length = 0
        imdb_ids = []
        try:
            results = self.send_request(
                current_url,
                header).xpath("//div[@class='desc']/span/text()")[0].replace(
                    ",", "")
        except IndexError:
            raise Failed(f"IMDb Error: Failed to parse URL: {imdb_url}")
        try:
            total = int(re.findall("(\\d+) title", results)[0])
        except IndexError:
            raise Failed(f"IMDb Error: No Results at URL: {imdb_url}")
        if "&start=" in current_url:
            current_url = re.sub("&start=\\d+", "", current_url)
        if "&count=" in current_url:
            current_url = re.sub("&count=\\d+", "", current_url)
        if limit < 1 or total < limit: limit = total
        remainder = limit % 250
        if remainder == 0: remainder = 250
        num_of_pages = math.ceil(int(limit) / 250)
        for i in range(1, num_of_pages + 1):
            start_num = (i - 1) * 250 + 1
            length = util.print_return(
                length,
                f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * 250}"
            )
            response = self.send_request(
                f"{current_url}&count={remainder if i == num_of_pages else 250}&start={start_num}",
                header)
            imdb_ids.extend(
                response.xpath(
                    "//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst"
                ))
        util.print_end(length)
        if imdb_ids: return imdb_ids
        else: raise Failed(f"IMDb Error: No Movies Found at {imdb_url}")
Beispiel #5
0
    def add_to_collection(self, collection, items, filters, map={}):
        name = collection.title if isinstance(collection, Collections) else collection
        collection_items = collection.children if isinstance(collection, Collections) else []

        total = len(items)
        max_length = len(str(total))
        length = 0
        for i, item in enumerate(items, 1):
            current = self.get_item(item)
            match = True
            if filters:
                length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title))
                for f in filters:
                    modifier = f[0][-4:]
                    method = util.filter_alias[f[0][:-4]] if modifier in [".not", ".lte", ".gte"] else util.filter_alias[f[0]]
                    if method == "max_age":
                        threshold_date = datetime.now() - timedelta(days=f[1])
                        attr = getattr(current, "originallyAvailableAt")
                        if attr is None or attr < threshold_date:
                            match = False
                            break
                    elif modifier in [".gte", ".lte"]:
                        if method == "originallyAvailableAt":
                            threshold_date = datetime.strptime(f[1], "%m/%d/%y")
                            attr = getattr(current, "originallyAvailableAt")
                            if (modifier == ".lte" and attr > threshold_date) or (modifier == ".gte" and attr < threshold_date):
                                match = False
                                break
                        elif method in ["year", "rating"]:
                            attr = getattr(current, method)
                            if (modifier == ".lte" and attr > f[1]) or (modifier == ".gte" and attr < f[1]):
                                match = False
                                break
                    else:
                        terms = f[1] if isinstance(f[1], list) else str(f[1]).split(", ")
                        if method in ["video_resolution", "audio_language", "subtitle_language"]:
                            for media in current.media:
                                if method == "video_resolution":                                                                attrs = [media.videoResolution]
                                for part in media.parts:
                                    if method == "audio_language":                                                                  attrs = ([a.language for a in part.audioStreams()])
                                    if method == "subtitle_language":                                                               attrs = ([s.language for s in part.subtitleStreams()])
                        elif method in ["contentRating", "studio", "year", "rating", "originallyAvailableAt"]:          attrs = [str(getattr(current, method))]
                        elif method in ["actors", "countries", "directors", "genres", "writers", "collections"]:        attrs = [getattr(x, "tag") for x in getattr(current, method)]

                        if (not list(set(terms) & set(attrs)) and modifier != ".not") or (list(set(terms) & set(attrs)) and modifier == ".not"):
                            match = False
                            break
                length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title))
            if match:
                util.print_end(length, "{} Collection | {} | {}".format(name, "=" if current in collection_items else "+", current.title))
                if current in collection_items:             map[current.ratingKey] = None
                else:                                       current.addCollection(name)
        media_type = "{}{}".format("Movie" if self.is_movie else "Show", "s" if total > 1 else "")
        util.print_end(length, "{} {} Processed".format(total, media_type))
        return map
Beispiel #6
0
 def _ids_from_url(self, imdb_url, language, limit):
     total, item_count = self._total(imdb_url, language)
     headers = util.header(language)
     imdb_ids = []
     parsed_url = urlparse(imdb_url)
     params = parse_qs(parsed_url.query)
     imdb_base = parsed_url._replace(query=None).geturl()
     params.pop("start", None)  # noqa
     params.pop("count", None)  # noqa
     params.pop("page", None)  # noqa
     if self.config.trace_mode:
         logger.debug(f"URL: {imdb_base}")
         logger.debug(f"Params: {params}")
     search_url = imdb_base.startswith(urls["searches"])
     if limit < 1 or total < limit:
         limit = total
     remainder = limit % item_count
     if remainder == 0:
         remainder = item_count
     num_of_pages = math.ceil(int(limit) / item_count)
     for i in range(1, num_of_pages + 1):
         start_num = (i - 1) * item_count + 1
         util.print_return(
             f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * item_count}"
         )
         if search_url:
             params[
                 "count"] = remainder if i == num_of_pages else item_count  # noqa
             params["start"] = start_num  # noqa
         else:
             params["page"] = i  # noqa
         response = self.config.get_html(imdb_base,
                                         headers=headers,
                                         params=params)
         ids_found = response.xpath(
             "//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst"
         )
         if not search_url and i == num_of_pages:
             ids_found = ids_found[:remainder]
         imdb_ids.extend(ids_found)
         time.sleep(2)
     util.print_end()
     if len(imdb_ids) > 0:
         logger.debug(f"{len(imdb_ids)} IMDb IDs Found: {imdb_ids}")
         return imdb_ids
     raise Failed(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
Beispiel #7
0
    def get_imdb_ids_from_url(self, imdb_url, language, limit):
        current_url = self.fix_url(imdb_url)
        total, item_count = self.get_total(current_url, language)
        header = {"Accept-Language": language}
        length = 0
        imdb_ids = []
        if "&start=" in current_url:
            current_url = re.sub("&start=\\d+", "", current_url)
        if "&count=" in current_url:
            current_url = re.sub("&count=\\d+", "", current_url)
        if "&page=" in current_url:
            current_url = re.sub("&page=\\d+", "", current_url)
        if limit < 1 or total < limit: limit = total

        remainder = limit % item_count
        if remainder == 0: remainder = item_count
        num_of_pages = math.ceil(int(limit) / item_count)
        for i in range(1, num_of_pages + 1):
            start_num = (i - 1) * item_count + 1
            length = util.print_return(
                length,
                f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * item_count}"
            )
            if imdb_url.startswith(self.urls["keyword"]):
                response = self.send_request(f"{current_url}&page={i}", header)
            else:
                response = self.send_request(
                    f"{current_url}&count={remainder if i == num_of_pages else item_count}&start={start_num}",
                    header)
            if imdb_url.startswith(self.urls["keyword"]) and i == num_of_pages:
                imdb_ids.extend(
                    response.xpath(
                        "//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst"
                    )[:remainder])
            else:
                imdb_ids.extend(
                    response.xpath(
                        "//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst"
                    ))
        util.print_end(length)
        if imdb_ids: return imdb_ids
        else: raise Failed(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
Beispiel #8
0
 def get_items(self, method, data, language, status_message=True):
     pretty = util.pretty_names[
         method] if method in util.pretty_names else method
     if status_message:
         logger.debug(f"Data: {data}")
     show_ids = []
     movie_ids = []
     if method == "imdb_id":
         if status_message:
             logger.info(f"Processing {pretty}: {data}")
         tmdb_id, tvdb_id = self.config.convert_from_imdb(data, language)
         if tmdb_id: movie_ids.append(tmdb_id)
         if tvdb_id: show_ids.append(tvdb_id)
     elif method == "imdb_list":
         if status_message:
             status = f"{data['limit']} Items at " if data[
                 'limit'] > 0 else ''
             logger.info(f"Processing {pretty}: {status}{data['url']}")
         imdb_ids = self.get_imdb_ids_from_url(data["url"], language,
                                               data["limit"])
         total_ids = len(imdb_ids)
         length = 0
         for i, imdb_id in enumerate(imdb_ids, 1):
             length = util.print_return(
                 length, f"Converting IMDb ID {i}/{total_ids}")
             try:
                 tmdb_id, tvdb_id = self.config.convert_from_imdb(
                     imdb_id, language)
                 if tmdb_id: movie_ids.append(tmdb_id)
                 if tvdb_id: show_ids.append(tvdb_id)
             except Failed as e:
                 logger.warning(e)
         util.print_end(length, f"Processed {total_ids} IMDb IDs")
     else:
         raise Failed(f"IMDb Error: Method {method} not supported")
     if status_message:
         logger.debug(f"TMDb IDs Found: {movie_ids}")
         logger.debug(f"TVDb IDs Found: {show_ids}")
     return movie_ids, show_ids
Beispiel #9
0
 def get_items(self, method, data, language, status_message=True):
     pretty = util.pretty_names[
         method] if method in util.pretty_names else method
     if status_message:
         logger.debug("Data: {}".format(data))
     show_ids = []
     movie_ids = []
     if method == "imdb_id":
         if status_message:
             logger.info("Processing {}: {}".format(pretty, data))
         tmdb_id, tvdb_id = self.convert_from_imdb(data, language)
         if tmdb_id: movie_ids.append(tmdb_id)
         if tvdb_id: show_ids.append(tvdb_id)
     elif method == "imdb_list":
         if status_message:
             logger.info("Processing {}: {}".format(
                 pretty, "{} Items at {}".format(data["limit"], data["url"])
                 if data["limit"] > 0 else data["url"]))
         imdb_ids = self.get_imdb_ids_from_url(data["url"], language,
                                               data["limit"])
         total_ids = len(imdb_ids)
         length = 0
         for i, imdb_id in enumerate(imdb_ids, 1):
             length = util.print_return(
                 length, "Converting IMDb ID {}/{}".format(i, total_ids))
             try:
                 tmdb_id, tvdb_id = self.convert_from_imdb(
                     imdb_id, language)
                 if tmdb_id: movie_ids.append(tmdb_id)
                 if tvdb_id: show_ids.append(tvdb_id)
             except Failed as e:
                 logger.warning(e)
         util.print_end(length, "Processed {} IMDb IDs".format(total_ids))
     else:
         raise Failed("IMDb Error: Method {} not supported".format(method))
     if status_message:
         logger.debug("TMDb IDs Found: {}".format(movie_ids))
         logger.debug("TVDb IDs Found: {}".format(show_ids))
     return movie_ids, show_ids
Beispiel #10
0
 def get_items(self, method, data, language, status_message=True):
     pretty = util.pretty_names[
         method] if method in util.pretty_names else method
     movie_ids = []
     if status_message:
         logger.info(f"Processing {pretty}: {data}")
     slugs = self.parse_list_for_slugs(data, language)
     total_slugs = len(slugs)
     if total_slugs == 0:
         raise Failed(f"Letterboxd Error: No List Items found in {data}")
     length = 0
     for i, slug in enumerate(slugs, 1):
         length = util.print_return(length,
                                    f"Finding TMDb ID {i}/{total_slugs}")
         try:
             movie_ids.append(self.get_tmdb(slug, language))
         except Failed as e:
             logger.error(e)
     util.print_end(length, f"Processed {total_slugs} TMDb IDs")
     if status_message:
         logger.debug(f"TMDb IDs Found: {movie_ids}")
     return movie_ids, []
Beispiel #11
0
    def add_to_collection(self, collection, items, filters, show_filtered,
                          rating_key_map, movie_map, show_map):
        name = collection.title if isinstance(collection,
                                              Collections) else collection
        collection_items = collection.items() if isinstance(
            collection, Collections) else []
        total = len(items)
        max_length = len(str(total))
        length = 0
        for i, item in enumerate(items, 1):
            try:
                current = self.fetchItem(
                    item.ratingKey if isinstance(item, (Movie,
                                                        Show)) else int(item))
            except (BadRequest, NotFound):
                logger.error(f"Plex Error: Item {item} not found")
                continue
            match = True
            if filters:
                length = util.print_return(
                    length,
                    f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}"
                )
                for filter_method, filter_data in filters:
                    modifier = filter_method[-4:]
                    method = filter_method[:-4] if modifier in [
                        ".not", ".lte", ".gte"
                    ] else filter_method
                    if method in util.method_alias:
                        method_name = util.method_alias[method]
                        logger.warning(
                            f"Collection Warning: {method} attribute will run as {method_name}"
                        )
                    else:
                        method_name = method
                    if method_name == "max_age":
                        threshold_date = datetime.now() - timedelta(
                            days=filter_data)
                        if current.originallyAvailableAt is None or current.originallyAvailableAt < threshold_date:
                            match = False
                            break
                    elif method_name == "original_language":
                        movie = None
                        for key, value in movie_map.items():
                            if current.ratingKey == value:
                                try:
                                    movie = self.TMDb.get_movie(key)
                                    break
                                except Failed:
                                    pass
                        if movie is None:
                            logger.warning(
                                f"Filter Error: No TMDb ID found for {current.title}"
                            )
                            continue
                        if (modifier == ".not" and movie.original_language
                                in filter_data) or (modifier != ".not"
                                                    and movie.original_language
                                                    not in filter_data):
                            match = False
                            break
                    elif method_name == "audio_track_title":
                        jailbreak = False
                        for media in current.media:
                            for part in media.parts:
                                for audio in part.audioStreams():
                                    for check_title in filter_data:
                                        title = audio.title if audio.title else ""
                                        if check_title.lower() in title.lower(
                                        ):
                                            jailbreak = True
                                            break
                                    if jailbreak: break
                                if jailbreak: break
                            if jailbreak: break
                        if (jailbreak and modifier == ".not") or (
                                not jailbreak and modifier != ".not"):
                            match = False
                            break
                    elif modifier in [".gte", ".lte"]:
                        if method_name == "vote_count":
                            tmdb_item = None
                            for key, value in movie_map.items():
                                if current.ratingKey == value:
                                    try:
                                        tmdb_item = self.TMDb.get_movie(
                                            key
                                        ) if self.is_movie else self.TMDb.get_show(
                                            key)
                                        break
                                    except Failed:
                                        pass
                            if tmdb_item is None:
                                logger.warning(
                                    f"Filter Error: No TMDb ID found for {current.title}"
                                )
                                continue
                            attr = tmdb_item.vote_count
                        else:
                            attr = getattr(
                                current, method_name
                            ) / 60000 if method_name == "duration" else getattr(
                                current, method_name)
                        if (modifier == ".lte" and attr > filter_data) or (
                                modifier == ".gte" and attr < filter_data):
                            match = False
                            break
                    else:
                        attrs = []
                        if method_name in [
                                "video_resolution", "audio_language",
                                "subtitle_language"
                        ]:
                            for media in current.media:
                                if method_name == "video_resolution":
                                    attrs.extend([media.videoResolution])
                                for part in media.parts:
                                    if method_name == "audio_language":
                                        attrs.extend([
                                            a.language
                                            for a in part.audioStreams()
                                        ])
                                    if method_name == "subtitle_language":
                                        attrs.extend([
                                            s.language
                                            for s in part.subtitleStreams()
                                        ])
                        elif method_name in [
                                "contentRating", "studio", "year", "rating",
                                "originallyAvailableAt"
                        ]:
                            attrs = [str(getattr(current, method_name))]
                        elif method_name in [
                                "actors", "countries", "directors", "genres",
                                "writers", "collections"
                        ]:
                            attrs = [
                                getattr(x, "tag")
                                for x in getattr(current, method_name)
                            ]

                        if (not list(set(filter_data) & set(attrs))
                                and modifier != ".not") or (
                                    list(set(filter_data) & set(attrs))
                                    and modifier == ".not"):
                            match = False
                            break
                length = util.print_return(
                    length,
                    f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}"
                )
            if match:
                util.print_end(
                    length,
                    f"{name} Collection | {'=' if current in collection_items else '+'} | {current.title}"
                )
                if current in collection_items:
                    rating_key_map[current.ratingKey] = None
                else:
                    current.addCollection(name)
            elif show_filtered is True:
                logger.info(f"{name} Collection | X | {current.title}")
        media_type = f"{'Movie' if self.is_movie else 'Show'}{'s' if total > 1 else ''}"
        util.print_end(length, f"{total} {media_type} Processed")
        return rating_key_map
Beispiel #12
0
    def get_items(self, method, data, status_message=True):
        if status_message:
            logger.debug(f"Data: {data}")
        pretty = util.pretty_names[method] if method in util.pretty_names else method
        media_type = "Movie" if self.is_movie else "Show"
        items = []
        if method == "plex_all":
            if status_message:
                logger.info(f"Processing {pretty} {media_type}s")
            items = self.get_all()
        elif method == "plex_collection":
            if status_message:
                logger.info(f"Processing {pretty} {data}")
            items = data.items()
        elif method == "plex_search":
            search_terms = {}
            has_processed = False
            search_limit = None
            search_sort = None
            for search_method, search_data in data.items():
                if search_method == "limit":
                    search_limit = search_data
                elif search_method == "sort_by":
                    search_sort = search_data
                else:
                    search, modifier = os.path.splitext(str(search_method).lower())
                    final_search = search_translation[search] if search in search_translation else search
                    if search in ["added", "originally_available"] and modifier == "":
                        final_mod = ">>"
                    elif search in ["added", "originally_available"] and modifier == ".not":
                        final_mod = "<<"
                    elif search in ["critic_rating", "audience_rating"] and modifier == ".greater":
                        final_mod = "__gte"
                    elif search in ["critic_rating", "audience_rating"] and modifier == ".less":
                        final_mod = "__lt"
                    else:
                        final_mod = modifiers[modifier] if modifier in modifiers else ""
                    final_method = f"{final_search}{final_mod}"

                    if search == "duration":
                        search_terms[final_method] = search_data * 60000
                    elif search in ["added", "originally_available"] and modifier in ["", ".not"]:
                        search_terms[final_method] = f"{search_data}d"
                    else:
                        search_terms[final_method] = search_data

                    if status_message:
                        if search in ["added", "originally_available"] or modifier in [".greater", ".less", ".before", ".after"]:
                            ors = f"{search_method}({search_data}"
                        else:
                            ors = ""
                            conjunction = " AND " if final_mod == "&" else " OR "
                            for o, param in enumerate(search_data):
                                or_des = conjunction if o > 0 else f"{search_method}("
                                ors += f"{or_des}{param}"
                        if has_processed:
                            logger.info(f"\t\t      AND {ors})")
                        else:
                            logger.info(f"Processing {pretty}: {ors})")
                            has_processed = True
            if status_message:
                if search_sort:
                    logger.info(f"\t\t      SORT BY {search_sort})")
                if search_limit:
                    logger.info(f"\t\t      LIMIT {search_limit})")
                logger.debug(f"Search: {search_terms}")
            return self.search(sort=sorts[search_sort], maxresults=search_limit, **search_terms)
        elif method == "plex_collectionless":
            good_collections = []
            for col in self.get_all_collections():
                keep_collection = True
                for pre in data["exclude_prefix"]:
                    if col.title.startswith(pre) or (col.titleSort and col.titleSort.startswith(pre)):
                        keep_collection = False
                        break
                if keep_collection:
                    for ext in data["exclude"]:
                        if col.title == ext or (col.titleSort and col.titleSort == ext):
                            keep_collection = False
                            break
                if keep_collection:
                    good_collections.append(col.index)
            all_items = self.get_all()
            length = 0
            for i, item in enumerate(all_items, 1):
                length = util.print_return(length, f"Processing: {i}/{len(all_items)} {item.title}")
                add_item = True
                item.reload()
                for collection in item.collections:
                    if collection.id in good_collections:
                        add_item = False
                        break
                if add_item:
                    items.append(item)
            util.print_end(length, f"Processed {len(all_items)} {'Movies' if self.is_movie else 'Shows'}")
        else:
            raise Failed(f"Plex Error: Method {method} not supported")
        if len(items) > 0:
            return items
        else:
            raise Failed("Plex Error: No Items found in Plex")
Beispiel #13
0
    def get_imdb_ids_from_url(self, imdb_url, language, limit):
        imdb_url = imdb_url.strip()
        if not imdb_url.startswith(
                "https://www.imdb.com/list/ls") and not imdb_url.startswith(
                    "https://www.imdb.com/search/title/?"):
            raise Failed(
                "IMDb Error: {} must begin with either:\n| https://www.imdb.com/list/ls (For Lists)\n| https://www.imdb.com/search/title/? (For Searches)"
                .format(imdb_url))

        if imdb_url.startswith("https://www.imdb.com/list/ls"):
            try:
                list_id = re.search("(\\d+)", str(imdb_url)).group(1)
            except AttributeError:
                raise Failed(
                    "IMDb Error: Failed to parse List ID from {}".format(
                        imdb_url))
            current_url = "https://www.imdb.com/search/title/?lists=ls{}".format(
                list_id)
        else:
            current_url = imdb_url
        header = {"Accept-Language": language}
        length = 0
        imdb_ids = []
        try:
            results = self.send_request(
                current_url,
                header).xpath("//div[@class='desc']/span/text()")[0].replace(
                    ",", "")
        except IndexError:
            raise Failed(
                "IMDb Error: Failed to parse URL: {}".format(imdb_url))
        try:
            total = int(re.findall("(\\d+) title", results)[0])
        except IndexError:
            raise Failed("IMDb Error: No Results at URL: {}".format(imdb_url))
        if "&start=" in current_url:
            current_url = re.sub("&start=\d+", "", current_url)
        if "&count=" in current_url:
            current_url = re.sub("&count=\d+", "", current_url)
        if limit < 1 or total < limit: limit = total
        remainder = limit % 250
        if remainder == 0: remainder = 250
        num_of_pages = math.ceil(int(limit) / 250)
        for i in range(1, num_of_pages + 1):
            start_num = (i - 1) * 250 + 1
            length = util.print_return(
                length, "Parsing Page {}/{} {}-{}".format(
                    i, num_of_pages, start_num,
                    limit if i == num_of_pages else i * 250))
            response = self.send_request(
                "{}&count={}&start={}".format(
                    current_url, remainder if i == num_of_pages else 250,
                    start_num), header)
            imdb_ids.extend(
                response.xpath(
                    "//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst"
                ))
        util.print_end(length)
        if imdb_ids: return imdb_ids
        else:
            raise Failed("IMDb Error: No Movies Found at {}".format(imdb_url))
Beispiel #14
0
    def get_id(self, item, library, length):
        expired = None
        tmdb_id = None
        imdb_id = None
        tvdb_id = None
        anidb_id = None
        mal_id = None
        error_message = None
        if self.Cache:
            if library.is_movie:                            tmdb_id, expired = self.Cache.get_tmdb_id("movie", plex_guid=item.guid)
            else:                                           tvdb_id, expired = self.Cache.get_tvdb_id("show", plex_guid=item.guid)
            if not tvdb_id and library.is_show:
                tmdb_id, expired = self.Cache.get_tmdb_id("show", plex_guid=item.guid)
                anidb_id, expired = self.Cache.get_anidb_id("show", plex_guid=item.guid)
        if expired or (not tmdb_id and library.is_movie) or (not tvdb_id and not tmdb_id and library.is_show):
            guid = requests.utils.urlparse(item.guid)
            item_type = guid.scheme.split(".")[-1]
            check_id = guid.netloc

            if item_type == "plex" and library.is_movie:
                for guid_tag in item.guids:
                    url_parsed = requests.utils.urlparse(guid_tag.id)
                    if url_parsed.scheme == "tmdb":                 tmdb_id = int(url_parsed.netloc)
                    elif url_parsed.scheme == "imdb":               imdb_id = url_parsed.netloc
            elif item_type == "imdb":                       imdb_id = check_id
            elif item_type == "thetvdb":                    tvdb_id = int(check_id)
            elif item_type == "themoviedb":                 tmdb_id = int(check_id)
            elif item_type == "hama":
                if check_id.startswith("tvdb"):             tvdb_id = int(re.search("-(.*)", check_id).group(1))
                elif check_id.startswith("anidb"):          anidb_id = re.search("-(.*)", check_id).group(1)
                else:                                       error_message = "Hama Agent ID: {} not supported".format(check_id)
            elif item_type == "myanimelist":                mal_id = check_id
            elif item_type == "local":                      error_message = "No match in Plex"
            else:                                           error_message = "Agent {} not supported".format(item_type)

            if not error_message:
                if anidb_id and not tvdb_id:
                    try:                                            tvdb_id = self.AniDB.convert_anidb_to_tvdb(anidb_id)
                    except Failed:                                  pass
                if anidb_id and not imdb_id:
                    try:                                            imdb_id = self.AniDB.convert_anidb_to_imdb(anidb_id)
                    except Failed:                                  pass
                if mal_id:
                    try:
                        ids = self.MyAnimeListIDList.find_mal_ids(mal_id)
                        if "thetvdb_id" in ids and int(ids["thetvdb_id"]) > 0:                  tvdb_id = int(ids["thetvdb_id"])
                        elif "themoviedb_id" in ids and int(ids["themoviedb_id"]) > 0:          tmdb_id = int(ids["themoviedb_id"])
                        else:                                                                   raise Failed("MyAnimeList Error: MyAnimeList ID: {} has no other IDs associated with it".format(mal_id))
                    except Failed:
                        pass
                if mal_id and not tvdb_id:
                    try:                                            tvdb_id = self.MyAnimeListIDList.convert_mal_to_tvdb(mal_id)
                    except Failed:                                  pass
                if mal_id and not tmdb_id:
                    try:                                            tmdb_id = self.MyAnimeListIDList.convert_mal_to_tmdb(mal_id)
                    except Failed:                                  pass
                if not tmdb_id and imdb_id and isinstance(imdb_id, list) and self.TMDb:
                    tmdb_id = []
                    new_imdb_id = []
                    for imdb in imdb_id:
                        try:
                            temp_tmdb_id = self.TMDb.convert_imdb_to_tmdb(imdb)
                            tmdb_id.append(temp_tmdb_id)
                            new_imdb_id.append(imdb)
                        except Failed:
                            continue
                    imdb_id = new_imdb_id
                if not tmdb_id and imdb_id and self.TMDb:
                    try:                                            tmdb_id = self.TMDb.convert_imdb_to_tmdb(imdb_id)
                    except Failed:                                  pass
                if not tmdb_id and imdb_id and self.Trakt:
                    try:                                            tmdb_id = self.Trakt.convert_imdb_to_tmdb(imdb_id)
                    except Failed:                                  pass
                if not tmdb_id and tvdb_id and self.TMDb:
                    try:                                            tmdb_id = self.TMDb.convert_tvdb_to_tmdb(tvdb_id)
                    except Failed:                                  pass
                if not tmdb_id and tvdb_id and self.Trakt:
                    try:                                            tmdb_id = self.Trakt.convert_tvdb_to_tmdb(tvdb_id)
                    except Failed:                                  pass
                if not imdb_id and tmdb_id and self.TMDb:
                    try:                                            imdb_id = self.TMDb.convert_tmdb_to_imdb(tmdb_id)
                    except Failed:                                  pass
                if not imdb_id and tmdb_id and self.Trakt:
                    try:                                            imdb_id = self.Trakt.convert_tmdb_to_imdb(tmdb_id)
                    except Failed:                                  pass
                if not imdb_id and tvdb_id and self.Trakt:
                    try:                                            imdb_id = self.Trakt.convert_tmdb_to_imdb(tmdb_id)
                    except Failed:                                  pass
                if not tvdb_id and tmdb_id and self.TMDb and library.is_show:
                    try:                                            tvdb_id = self.TMDb.convert_tmdb_to_tvdb(tmdb_id)
                    except Failed:                                  pass
                if not tvdb_id and tmdb_id and self.Trakt and library.is_show:
                    try:                                            tvdb_id = self.Trakt.convert_tmdb_to_tvdb(tmdb_id)
                    except Failed:                                  pass
                if not tvdb_id and imdb_id and self.Trakt and library.is_show:
                    try:                                            tvdb_id = self.Trakt.convert_imdb_to_tvdb(imdb_id)
                    except Failed:                                  pass

                if (not tmdb_id and library.is_movie) or (not tvdb_id and not ((anidb_id or mal_id) and tmdb_id) and library.is_show):
                    service_name = "TMDb ID" if library.is_movie else "TVDb ID"

                    if self.TMDb and self.Trakt:                    api_name = "TMDb or Trakt"
                    elif self.TMDb:                                 api_name = "TMDb"
                    elif self.Trakt:                                api_name = "Trakt"
                    else:                                           api_name = None

                    if tmdb_id and imdb_id:                         id_name = "TMDb ID: {} or IMDb ID: {}".format(tmdb_id, imdb_id)
                    elif imdb_id and tvdb_id:                       id_name = "IMDb ID: {} or TVDb ID: {}".format(imdb_id, tvdb_id)
                    elif tmdb_id:                                   id_name = "TMDb ID: {}".format(tmdb_id)
                    elif imdb_id:                                   id_name = "IMDb ID: {}".format(imdb_id)
                    elif tvdb_id:                                   id_name = "TVDb ID: {}".format(tvdb_id)
                    else:                                           id_name = None

                    if anidb_id and not tmdb_id and not tvdb_id:    error_message = "Unable to convert AniDb ID: {} to TMDb ID or TVDb ID".format(anidb_id)
                    elif mal_id and not tmdb_id and not tvdb_id:    error_message = "Unable to convert MyAnimeList ID: {} to TMDb ID or TVDb ID".format(mal_id)
                    elif id_name and api_name:                      error_message = "Unable to convert {} to {} using {}".format(id_name, service_name, api_name)
                    elif id_name:                                   error_message = "Configure TMDb or Trakt to covert {} to {}".format(id_name, service_name)
                    else:                                           error_message = "No ID to convert to {}".format(service_name)
            if self.Cache and (tmdb_id and library.is_movie) or ((tvdb_id or ((anidb_id or mal_id) and tmdb_id)) and library.is_show):
                if isinstance(tmdb_id, list):
                    for i in range(len(tmdb_id)):
                        util.print_end(length, "Cache | {} | {:<46} | {:<6} | {:<10} | {:<6} | {:<5} | {:<5} | {}".format("^" if expired is True else "+", item.guid, tmdb_id[i] if tmdb_id[i] else "None", imdb_id[i] if imdb_id[i] else "None", tvdb_id if tvdb_id else "None", anidb_id if anidb_id else "None", mal_id if mal_id else "None", item.title))
                        self.Cache.update_guid("movie" if library.is_movie else "show", item.guid, tmdb_id[i], imdb_id[i], tvdb_id, anidb_id, mal_id, expired)
                else:
                    util.print_end(length, "Cache | {} | {:<46} | {:<6} | {:<10} | {:<6} | {:<5} | {:<5} | {}".format("^" if expired is True else "+", item.guid, tmdb_id if tmdb_id else "None", imdb_id if imdb_id else "None", tvdb_id if tvdb_id else "None", anidb_id if anidb_id else "None", mal_id if mal_id else "None", item.title))
                    self.Cache.update_guid("movie" if library.is_movie else "show", item.guid, tmdb_id, imdb_id, tvdb_id, anidb_id, mal_id, expired)
        if tmdb_id and library.is_movie:                return "movie", tmdb_id
        elif tvdb_id and library.is_show:               return "show", tvdb_id
        elif (anidb_id or mal_id) and tmdb_id:          return "movie", tmdb_id
        else:
            util.print_end(length, "{} {:<46} | {} for {}".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, error_message, item.title))
            return None, None
Beispiel #15
0
    def add_to_collection(self, collection, items, filters, show_filtered, map,
                          movie_map, show_map):
        name = collection.title if isinstance(collection,
                                              Collections) else collection
        collection_items = collection.items() if isinstance(
            collection, Collections) else []
        total = len(items)
        max_length = len(str(total))
        length = 0
        for i, item in enumerate(items, 1):
            try:
                current = self.fetchItem(
                    item.ratingKey if isinstance(item, (Movie,
                                                        Show)) else int(item))
            except (BadRequest, NotFound):
                logger.error("Plex Error: Item {} not found".format(item))
                continue
            match = True
            if filters:
                length = util.print_return(
                    length, "Filtering {}/{} {}".format(
                        (" " * (max_length - len(str(i)))) + str(i), total,
                        current.title))
                for f in filters:
                    modifier = f[0][-4:]
                    method = util.filter_alias[f[0][:-4]] if modifier in [
                        ".not", ".lte", ".gte"
                    ] else util.filter_alias[f[0]]
                    if method == "max_age":
                        threshold_date = datetime.now() - timedelta(days=f[1])
                        attr = getattr(current, "originallyAvailableAt")
                        if attr is None or attr < threshold_date:
                            match = False
                            break
                    elif method == "original_language":
                        terms = util.get_list(f[1], lower=True)
                        tmdb_id = None
                        movie = None
                        for key, value in movie_map.items():
                            if current.ratingKey == value:
                                try:
                                    movie = self.TMDb.get_movie(key)
                                    break
                                except Failed:
                                    pass
                        if movie is None:
                            logger.warning(
                                "Filter Error: No TMDb ID found for {}".format(
                                    current.title))
                            continue
                        if (modifier == ".not"
                                and movie.original_language in terms) or (
                                    modifier != ".not"
                                    and movie.original_language not in terms):
                            match = False
                            break
                    elif modifier in [".gte", ".lte"]:
                        if method == "originallyAvailableAt":
                            threshold_date = datetime.strptime(
                                f[1], "%m/%d/%y")
                            attr = getattr(current, "originallyAvailableAt")
                            if (modifier == ".lte" and attr > threshold_date
                                ) or (modifier == ".gte"
                                      and attr < threshold_date):
                                match = False
                                break
                        elif method in ["year", "rating"]:
                            attr = getattr(current, method)
                            if (modifier == ".lte"
                                    and attr > f[1]) or (modifier == ".gte"
                                                         and attr < f[1]):
                                match = False
                                break
                    else:
                        terms = util.get_list(f[1])
                        if method in [
                                "video_resolution", "audio_language",
                                "subtitle_language"
                        ]:
                            for media in current.media:
                                if method == "video_resolution":
                                    attrs = [media.videoResolution]
                                for part in media.parts:
                                    if method == "audio_language":
                                        attrs = ([
                                            a.language
                                            for a in part.audioStreams()
                                        ])
                                    if method == "subtitle_language":
                                        attrs = ([
                                            s.language
                                            for s in part.subtitleStreams()
                                        ])
                        elif method in [
                                "contentRating", "studio", "year", "rating",
                                "originallyAvailableAt"
                        ]:
                            attrs = [str(getattr(current, method))]
                        elif method in [
                                "actors", "countries", "directors", "genres",
                                "writers", "collections"
                        ]:
                            attrs = [
                                getattr(x, "tag")
                                for x in getattr(current, method)
                            ]

                        if (not list(set(terms) & set(attrs)) and modifier !=
                                ".not") or (list(set(terms) & set(attrs))
                                            and modifier == ".not"):
                            match = False
                            break
                length = util.print_return(
                    length, "Filtering {}/{} {}".format(
                        (" " * (max_length - len(str(i)))) + str(i), total,
                        current.title))
            if match:
                util.print_end(
                    length, "{} Collection | {} | {}".format(
                        name, "=" if current in collection_items else "+",
                        current.title))
                if current in collection_items: map[current.ratingKey] = None
                else: current.addCollection(name)
            elif show_filtered is True:
                logger.info("{} Collection | X | {}".format(
                    name, current.title))
        media_type = "{}{}".format("Movie" if self.is_movie else "Show",
                                   "s" if total > 1 else "")
        util.print_end(length, "{} {} Processed".format(total, media_type))
        return map
def update_libraries(config):
    global stats
    for library in config.libraries:
        try:
            os.makedirs(os.path.join(default_dir, "logs", library.mapping_name, "collections"), exist_ok=True)
            col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, "library.log")
            should_roll_over = os.path.isfile(col_file_logger)
            library_handler = RotatingFileHandler(col_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8")
            util.apply_formatter(library_handler)
            if should_roll_over:
                library_handler.doRollover()
            logger.addHandler(library_handler)

            plexapi.server.TIMEOUT = library.timeout
            logger.info("")
            util.separator(f"{library.name} Library")

            logger.debug("")
            logger.debug(f"Mapping Name: {library.original_mapping_name}")
            logger.debug(f"Folder Name: {library.mapping_name}")
            logger.debug(f"Missing Path: {library.missing_path}")
            for ad in library.asset_directory:
                logger.debug(f"Asset Directory: {ad}")
            logger.debug(f"Asset Folders: {library.asset_folders}")
            logger.debug(f"Create Asset Folders: {library.create_asset_folders}")
            logger.debug(f"Sync Mode: {library.sync_mode}")
            logger.debug(f"Collection Minimum: {library.collection_minimum}")
            logger.debug(f"Delete Below Minimum: {library.delete_below_minimum}")
            logger.debug(f"Delete Not Scheduled: {library.delete_not_scheduled}")
            logger.debug(f"Missing Only Released: {library.missing_only_released}")
            logger.debug(f"Only Filter Missing: {library.only_filter_missing}")
            logger.debug(f"Show Unmanaged: {library.show_unmanaged}")
            logger.debug(f"Show Filtered: {library.show_filtered}")
            logger.debug(f"Show Missing: {library.show_missing}")
            logger.debug(f"Show Missing Assets: {library.show_missing_assets}")
            logger.debug(f"Save Missing: {library.save_missing}")
            logger.debug(f"Assets For All: {library.assets_for_all}")
            logger.debug(f"Delete Collections With Less: {library.delete_collections_with_less}")
            logger.debug(f"Delete Unmanaged Collections: {library.delete_unmanaged_collections}")
            logger.debug(f"Mass Genre Update: {library.mass_genre_update}")
            logger.debug(f"Mass Audience Rating Update: {library.mass_audience_rating_update}")
            logger.debug(f"Mass Critic Rating Update: {library.mass_critic_rating_update}")
            logger.debug(f"Mass Trakt Rating Update: {library.mass_trakt_rating_update}")
            logger.debug(f"Split Duplicates: {library.split_duplicates}")
            logger.debug(f"Radarr Add All: {library.radarr_add_all}")
            logger.debug(f"Sonarr Add All: {library.sonarr_add_all}")
            logger.debug(f"TMDb Collections: {library.tmdb_collections}")
            logger.debug(f"Genre Mapper: {library.genre_mapper}")
            logger.debug(f"Clean Bundles: {library.clean_bundles}")
            logger.debug(f"Empty Trash: {library.empty_trash}")
            logger.debug(f"Optimize: {library.optimize}")
            logger.debug(f"Timeout: {library.timeout}")

            if not library.is_other:
                logger.info("")
                util.separator(f"Mapping {library.name} Library", space=False, border=False)
                logger.info("")
                library.map_guids()
            for metadata in library.metadata_files:
                logger.info("")
                util.separator(f"Running Metadata File\n{metadata.path}")
                if not config.test_mode and not config.resume_from and not collection_only:
                    try:
                        metadata.update_metadata()
                    except Failed as e:
                        library.notify(e)
                        logger.error(e)
                collections_to_run = metadata.get_collections(config.requested_collections)
                if config.resume_from and config.resume_from not in collections_to_run:
                    logger.info("")
                    logger.warning(f"Collection: {config.resume_from} not in Metadata File: {metadata.path}")
                    continue
                if collections_to_run and not library_only:
                    logger.info("")
                    util.separator(f"{'Test ' if config.test_mode else ''}Collections")
                    logger.removeHandler(library_handler)
                    run_collection(config, library, metadata, collections_to_run)
                    logger.addHandler(library_handler)
            if library.run_sort:
                logger.info("")
                util.separator(f"Sorting {library.name} Library's Collections", space=False, border=False)
                logger.info("")
                for builder in library.run_sort:
                    logger.info("")
                    util.separator(f"Sorting {builder.name} Collection", space=False, border=False)
                    logger.info("")
                    builder.sort_collection()

            if not config.test_mode and not collection_only:
                library_operations(config, library)

            logger.removeHandler(library_handler)
        except Exception as e:
            library.notify(e)
            util.print_stacktrace()
            util.print_multiline(e, critical=True)

    has_run_again = False
    for library in config.libraries:
        if library.run_again:
            has_run_again = True
            break

    if has_run_again and not library_only:
        logger.info("")
        util.separator("Run Again")
        logger.info("")
        for x in range(1, config.general["run_again_delay"] + 1):
            util.print_return(f"Waiting to run again in {config.general['run_again_delay'] - x + 1} minutes")
            for y in range(60):
                time.sleep(1)
        util.print_end()
        for library in config.libraries:
            if library.run_again:
                try:
                    col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, f"library.log")
                    library_handler = RotatingFileHandler(col_file_logger, mode="w", backupCount=3, encoding="utf-8")
                    util.apply_formatter(library_handler)
                    logger.addHandler(library_handler)
                    library_handler.addFilter(fmt_filter)
                    os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
                    logger.info("")
                    util.separator(f"{library.name} Library Run Again")
                    logger.info("")
                    library.map_guids()
                    for builder in library.run_again:
                        logger.info("")
                        util.separator(f"{builder.name} Collection")
                        logger.info("")
                        try:
                            builder.run_collections_again()
                        except Failed as e:
                            library.notify(e, collection=builder.name, critical=False)
                            util.print_stacktrace()
                            util.print_multiline(e, error=True)
                    logger.removeHandler(library_handler)
                except Exception as e:
                    library.notify(e)
                    util.print_stacktrace()
                    util.print_multiline(e, critical=True)

    used_url = []
    for library in config.libraries:
        if library.url not in used_url:
            used_url.append(library.url)
            if library.empty_trash:
                library.query(library.PlexServer.library.emptyTrash)
            if library.clean_bundles:
                library.query(library.PlexServer.library.cleanBundles)
            if library.optimize:
                library.query(library.PlexServer.library.optimize)