Exemple #1
0
    def _handle_indexed_bracket(self, bracket):
        split_range = bracket.content.split("-")

        if len(split_range) > 2:
            raise exceptions.InvalidRequest(
                f"Invalid start-end range: {self.content[0]}"
            )
        start = int(split_range[0].strip())
        end = start + 1

        if len(split_range) > 1:
            end = int(split_range[1].strip()) + 1  # Human sintax lmao

        if start > end:
            raise exceptions.InvalidRequest(f"Negative index found: {split_range}")

        if (end - start) > 8:
            raise exceptions.InvalidRequest(
                f"Expected less than 9 items, found {end - start}"
            )

        for index in range(start, end):
            logger.debug("Appending index: %d", index)
            # self.frames.append((self.subtitles[index - 1], 0))
            if index > len(self.subtitles):
                raise exceptions.InvalidRequest(f"Index not found: {index}")

            self.frames.extend(bracket.process_subtitle(self.subtitles[index - 1]))

        self._unify_dialogue()
Exemple #2
0
    def _load_frames(self):
        if len(self.items) != 2:
            raise exceptions.InvalidRequest("Expected 2 items for swap")

        ids = [item.media.id for item in self.items]
        if ids[0] == ids[1]:
            raise exceptions.InvalidRequest("Can't swap the same movie")

        brackets = self._get_brackets()

        # Just left the last media item
        temp_item = self.items[-1]
        sliced = np.array_split(brackets, 2)

        source, dest = sliced
        for old, new in zip(source, dest):
            if not new.postproc.empty:
                new.update_from_swap(old)
            else:
                logger.debug("Ignoring swap for bracket: %s", new)

            frame_ = Frame(temp_item.media, new)
            frame_.load_frame()

            logger.debug("Appending frame: %s", frame_)

            self.frames.append(frame_)

        # For stories
        self._raw = self.frames[0].pil

        logger.debug("Loaded frames: %s", len(self.frames))
Exemple #3
0
    def _milli_cheks(self):
        try:
            self.milli -= self._args.get("minus", 0)
            self.milli += self._args.get("plus", 0)
        except TypeError:
            raise exceptions.InvalidRequest(
                f"Millisecond value is not an integer: {self._args}"
            ) from None

        if abs(self.milli) > 3000:
            raise exceptions.InvalidRequest("3000ms limit exceeded. Are you dumb?")
Exemple #4
0
    def _sanity_checks(self):
        if self.media.type == "song":
            raise exceptions.InvalidRequest(
                "Songs doesn't support GIF requests")

        if len(self.brackets) > 4:
            raise exceptions.InvalidRequest(
                f"Expected less than 5 quotes, found {len(self.brackets[0])}.")

        if len(self.brackets) > 1 and isinstance(self.brackets[0], tuple):
            raise exceptions.InvalidRequest(
                "No more than one range brackets are allowed")
Exemple #5
0
    def _check_image_size(cls, val):
        if val is None:
            return val

        try:
            value = float(val.strip())
        except ValueError as error:
            raise exceptions.InvalidRequest(error) from None

        if value > 3:
            raise exceptions.InvalidRequest(f"Expected =<3, found {value}")

        return value
Exemple #6
0
    def _check_border(cls, val):
        if val is None:
            return None

        try:
            x_border, y_border = [int(item) for item in val.split(",")]
        except ValueError:
            raise exceptions.InvalidRequest(f"`{val}`") from None

        if any(item > 20 for item in (x_border, y_border)):
            raise exceptions.InvalidRequest("Expected `<20` value")

        return x_border, y_border
Exemple #7
0
    def _cv2_trim(self) -> bool:
        """
        Remove black borders from a cv2 image array.

        This method is a f*****g waste of time as most sources are already
        properly cropped. We need to use it because of a few shitty WEB sources.
        F*****g unbelievable.

        :param cv2_image: cv2 image array
        """
        logger.info("Trying to remove black borders with cv2")
        og_w, og_h = self._cv2.shape[1], self._cv2.shape[0]
        logger.debug("Original dimensions: %dx%d", og_w, og_h)
        og_quotient = og_w / og_h

        first_img = _remove_lateral_cv2(self._cv2)

        tmp_img = cv2.transpose(first_img)
        tmp_img = cv2.flip(tmp_img, flipCode=1)

        if tmp_img is None:
            raise exceptions.InvalidRequest("Possible all-black image found")

        final = _remove_lateral_cv2(tmp_img)

        out = cv2.transpose(final)

        final_img = cv2.flip(out, flipCode=0)
        if final_img is None:
            raise exceptions.InvalidRequest("Possible all-black image found")

        new_w, new_h = final_img.shape[1], final_img.shape[0]

        logger.debug("New dimensions: %dx%d", new_w, new_h)
        new_quotient = new_w / new_h

        if abs(new_quotient - og_quotient) > 0.9:
            logger.info("Possible bad quotient found: %s -> %s", og_quotient,
                        new_quotient)
            return False

        width_percent = (100 / og_w) * new_w
        height_percent = (100 / og_h) * new_h

        if any(percent <= 65 for percent in (width_percent, height_percent)):
            logger.info("Possible bad trim found: %s -> %s", width_percent,
                        height_percent)
            return False

        self._cv2 = final_img
        return True
Exemple #8
0
    def __init__(
        self,
        content,
        movie_list,
        episode_list,
        req_dictionary,
        multiple=False,
    ):
        search_func = search_episode if req_dictionary[
            "is_episode"] else search_movie
        self.movie = search_func(
            episode_list if req_dictionary["is_episode"] else movie_list,
            req_dictionary["movie"],
            req_dictionary["parallel"] is None,
        )

        self.discriminator, self.chain, self.quote = None, None, None
        self.pill = []
        self.content = convert_request_content(content)
        self.req_dictionary = req_dictionary
        self.is_minute = self.content != content
        self.multiple = multiple
        self.dar = self.movie.get("dar")
        self.path = self.movie["path"]
        self.verified = req_dictionary["verified"]
        self.legacy_palette = "!palette" == self.req_dictionary["type"]

        if self.legacy_palette and len(req_dictionary["content"]) > 1:
            raise exceptions.InvalidRequest(req_dictionary["comment"])
Exemple #9
0
    def compute_brackets(self):
        "Find quotes, ranges, indexes, and timestamps."
        self._compute_brackets()

        if len(self.brackets) > 8:
            raise exceptions.InvalidRequest(
                f"Expected less than 8 frames, found {len(self.brackets)}")
Exemple #10
0
def _get_box(val, limit=4) -> list:
    try:
        box = [int(item.strip()) for item in val.split(",")]
    except ValueError:
        raise exceptions.InvalidRequest(
            f"Non-int values found: {val}") from None

    if len(box) != limit:
        raise exceptions.InvalidRequest(
            f"Expected {limit} values, found {len(box)}")

    if any(0 < value > 100 for value in box):
        raise exceptions.InvalidRequest(
            f"Negative or greater than 100 value found: {box}")

    return box
Exemple #11
0
    def _get_frame_capture(self, timestamps: Tuple[int, int]):
        """
        Get an image array based on seconds and milliseconds with cv2.
        """
        # fixme
        path_ = (self.path or "").lower()
        if "hevc" in path_ or "265" in path_:
            raise exceptions.InvalidRequest(
                "This format of video is not available. Please wait for the upcoming Kinobot V3"
            )

        if self.capture is None:
            self.load_capture_and_fps()

        seconds, milliseconds = timestamps
        extra_frames = int(self.fps * (milliseconds * 0.001))

        frame_start = int(self.fps * seconds) + extra_frames

        logger.debug("Frame to extract: %s from %s", frame_start, self.path)

        self.capture.set(1, frame_start)
        frame = self.capture.read()[1]

        if frame is not None:
            if self._dar is None:
                self._dar = get_dar(self.path)

            return self._fix_dar(frame)

        raise exceptions.InexistentTimestamp(f"`{seconds}` not found in video")
Exemple #12
0
    def __init__(
        self,
        content,
        movie_list,
        episode_list,
        req_dictionary,
        multiple=False,
    ):
        self.on_demand = req_dictionary.get("on_demand", False)
        search_func = search_episode if req_dictionary[
            "is_episode"] else search_movie

        raise_resting = ((req_dictionary["parallel"] is None)
                         if not self.on_demand else not self.on_demand)

        self.movie = search_func(
            episode_list if req_dictionary["is_episode"] else movie_list,
            req_dictionary["movie"],
            raise_resting,
        )

        self.discriminator, self.chain, self.quote = None, None, None
        self.pill = []
        self.content = convert_request_content(content)
        self.req_dictionary = req_dictionary
        self.is_minute = self.content != content
        self.dar = self.movie.get("dar")
        self.path = self.movie["path"]
        self.verified = req_dictionary["verified"]
        self.legacy_palette = "!palette" == self.req_dictionary["type"]
        self.multiple = multiple or self.legacy_palette

        if self.legacy_palette and len(req_dictionary["content"]) > 1:
            raise exceptions.InvalidRequest(
                "Palette requests only support one bracket.")
Exemple #13
0
    def _check_ap(cls, val):
        if val is None:
            return None

        if 1 > val < 2.5:
            raise exceptions.InvalidRequest(f"Expected 1>|<2.5, found {val}")

        return val
Exemple #14
0
    def _check_dimensions(cls, val):
        if val is None:
            return None

        values = [number.strip() for number in val.split("x")]

        if len(values) != 2 or any(not val.isdigit() for val in values):
            raise exceptions.InvalidRequest(f"Invalid dimensions: {val}")

        values = int(values[0]), int(values[1])

        if values not in _VALID_COLLAGES:
            raise exceptions.InvalidRequest(
                f"Invalid collage. Choose between: `{_VALID_COLLAGES}`")

        logger.debug("Found dimensions value: %s", values)
        return values
Exemple #15
0
    def subtitle(self) -> str:
        assert self.media.path is not None

        suffix = LANGUAGE_SUFFIXES.get(self._language)
        if suffix is None:
            raise exceptions.InvalidRequest(
                f"Language not found: {self._language}")

        return f"{os.path.splitext(self.media.path)[0]}.{suffix}.srt"
Exemple #16
0
    def from_request(cls, request):
        """Load an item from a Request class.

        :param request:
        :type request: Request
        """
        assert request.type == "!gif"
        items = request.items

        if any(mreq.media.type == "song" for mreq in items):
            raise exceptions.InvalidRequest("Songs don't support GIF requests")

        if len(items) > 1:
            raise exceptions.InvalidRequest(
                "GIF requests don't support multiple items")

        item = items[0]
        item.compute_brackets()
        return cls(item.media, item.brackets, request.id)
Exemple #17
0
    def _get_gif_tuple(self):
        logger.debug("Loading GIF tuple: %s", self._content)

        tuple_ = [
            _get_seconds(_sec.split(":")) for _sec in self._content.split("-")
        ]

        if len(tuple_) == 2:
            start, end = tuple_
            if (end - start) > 7:
                raise exceptions.InvalidRequest(
                    "Too long GIF request (expected less than 8 seconds)")
            if start > end:
                raise exceptions.InvalidRequest("Negative range found")

            self.content = tuple(tuple_)

        else:
            raise exceptions.InvalidRequest(
                f"Invalid GIF range request: {self._content}")
Exemple #18
0
    def _check_custom_crop(cls, val):
        if val is None:
            return val

        box = _get_box(val)

        if box[0] >= box[2] or box[1] >= box[3]:
            raise exceptions.InvalidRequest(
                "The next coordinate (e.g. left -> right) can't have an "
                f"equal or lesser value: {val}")

        return box
Exemple #19
0
    def __init__(self, query: str, filter_: str = "", limit: int = 10, lang="en"):
        if len(query.strip()) < 5:
            raise exceptions.InvalidRequest(f"Too short query (<5): {query}")

        self.query = query.strip()
        self.pattern = self.query
        self._glob_pattern = _glob_pattern_map[lang]
        logger.debug("Glob pattern: %s", self._glob_pattern)
        self.filter_ = filter_
        self.limit = limit
        self.media_items: List[Union[Movie, Episode]] = []
        self.items: List[dict] = []
Exemple #20
0
    def _check_image_rotate(cls, val):
        if val is None:
            return val

        try:
            value = float(val.strip())
        except ValueError:
            return None

        if abs(value) > 360:
            raise exceptions.InvalidRequest(value)

        return value
Exemple #21
0
    def _check_glitch(cls, val):
        # --glitch glitch_amount=3,color_offset=True,scan_lines=True
        if val is None:
            return None

        glitch_dict = {
            "glitch_amount": 4,
            "color_offset": True,
            "scan_lines": True
        }

        fields = val.split(",")
        for field in fields:
            field_split = field.split("=")
            key = field_split[0]

            if key not in glitch_dict:
                continue

            if len(field_split) != 2:
                raise exceptions.InvalidRequest(f"`{field_split}`")

            if key == "glitch_amount":
                try:
                    value = abs(int(field_split[-1]))
                    if value > 10:
                        raise exceptions.InvalidRequest(
                            "Expected <10") from None
                except ValueError:
                    raise exceptions.InvalidRequest(
                        "Expected integer") from None
                glitch_dict["glitch_amount"] = value or 1
            else:
                glitch_dict[key] = "true" in field_split[-1].lower()

        logger.debug("Updated glitch dict: %s", glitch_dict)
        return glitch_dict
Exemple #22
0
def _extract_id_from_url(video_url: str) -> str:
    """
    :param video_url: YouTube URL (classic or mobile)
    """
    video_url = video_url.strip()
    parsed = parse.parse_qs(parse.urlparse(video_url).query).get("v")
    if parsed is not None:
        return parsed[0]

    # Mobile fallback
    if "youtu.be" in video_url:
        parsed = parse.urlsplit(video_url)
        return parsed.path.replace("/", "")

    raise exceptions.InvalidRequest(f"Invalid video URL: {video_url}")
Exemple #23
0
def _get_seconds(split_timestamp: Sequence[str]) -> int:
    """
    :param split_timestamp:
    :type split_timestamp: Sequence[str]
    :raises exceptions.InvalidRequest
    """
    if len(split_timestamp) == 2:  # mm:ss
        return int(split_timestamp[0]) * 60 + int(split_timestamp[1])

    if len(split_timestamp) == 3:  # hh:mm:ss
        return ((int(split_timestamp[0]) * 3600) +
                (int(split_timestamp[1]) * 60) + int(split_timestamp[2]))

    raise exceptions.InvalidRequest(
        f"Invalid format: {split_timestamp}. Use mm:ss or hh:mm:ss")
Exemple #24
0
    def _get_rg_pattern(self) -> str:
        """
        Generate a punctuation-insensitive regex for ripgrep.
        """
        if len(self.query) < 4:
            raise exceptions.InvalidRequest("Too short query (<4)")

        after_word = r"(\s|\W|$|(\W\s))"
        pattern = r"(^|\s|\W)"
        for word in self.query.split():
            word = re.sub(r"\W", "", word)
            pattern = pattern + word + after_word

        logger.debug("Generated pattern: %s", pattern)
        return pattern
Exemple #25
0
    def __init__(self, items: Sequence[RequestItem], type_: str, id_: str,
                 **kwargs):
        self.items = items
        self.id: str = id_
        self.type: str = type_

        self.frames: List[Frame] = []

        self._paths = []

        try:
            self.postproc = PostProc(**kwargs)
        except ValidationError as error:
            raise exceptions.InvalidRequest(error) from None

        self._raw: Optional[Image.Image] = None
Exemple #26
0
    def _check_apply_to(cls, val):
        if not val:  # Falsy
            return None

        range_ = val.split("-")
        try:
            if len(range_) == 1:  # --apply-to x
                num = int(range_[0].split(".")[0])
                final = tuple(range(num - 1, num))
            else:  # --apply-to x-x
                final = tuple(range(int(range_[0]) - 1, int(range_[1])))
        except ValueError:
            raise exceptions.InvalidRequest(f"`{range_}`") from None

        logger.debug("Parsed apply to: %s", final)
        return final
Exemple #27
0
    async def rate(self, ctx: commands.Context, *args):
        rating = args[-1].split("/")[0]

        try:
            rating = float(rating)
        except ValueError:
            raise exceptions.InvalidRequest(
                "Number not found: {rating}") from None

        logger.debug("Passed rating: %s", rating)

        movie = Movie.from_query(" ".join(args))
        user = User.from_discord(ctx.author)

        user.rate_media(movie, rating)

        await ctx.send(f"You rating for `{movie.simple_title}`: **{rating}/5**"
                       )
Exemple #28
0
def _get_transparent_from_image_url(url: str) -> Image.Image:
    name = f"{uuid.uuid3(uuid.NAMESPACE_URL, url)}.png"
    path = os.path.join(CACHED_FRAMES_DIR, name)

    if not os.path.isfile(path):
        download_image(url, path)

    try:
        image = Image.open(path)
        _test_transparency_mask(image)
    except (ValueError, UnidentifiedImageError):
        raise exceptions.InvalidRequest(
            "Image has no transparent mask. If you can't find"
            " your desired image on Internet, upload your own to "
            "<https://imgur.com/> and use the generated URL.") from None

    image = image.crop(image.getbbox())
    image.thumbnail((1280, 720))
    return image
Exemple #29
0
    def __init__(self, media: hints, bracket: Bracket):
        self.media = media
        self.bracket = bracket
        self.message: Union[str, None] = None

        content = self.bracket.content
        if isinstance(content, Subtitle):
            self.seconds = content.start.seconds
            self.milliseconds = content.start.microseconds / 1000
            self.message = content.content  # Subtitle message
        elif isinstance(content, int):
            self.seconds = content
            self.milliseconds = bracket.milli
        else:
            raise exceptions.InvalidRequest(
                "Frames must contain quotes or timestamps")

        self._cv2: np.ndarray
        self.pil: Image.Image
Exemple #30
0
    def _image_list_check(self, frames):
        if (self.dimensions is not None and len(frames) !=
            (self.dimensions[0] * self.dimensions[1])  # type: ignore
                and self.no_collage is False):
            raise exceptions.InvalidRequest(
                f"Kinobot returned {len(frames)} frames; such amount is compatible"
                f" with the requested collage dimensions: {self.dimensions}")

        logger.debug("Requested dimensions: %s", self.dimensions)

        if self.dimensions is None:
            self.dimensions = _POSSIBLES.get(len(frames))  # Still can be None

        if (self.dimensions is not None
                and self.dimensions in _LATERAL_COLLAGES
                and self.font_size == _DEFAULT_FONT_SIZE):
            self.font_size += 2

        logger.debug("Found dimensions: %s", self.dimensions)