Ejemplo n.º 1
0
def test_resize_height(png_image, jpg_image, tmp_path, fmt):
    src, dst = get_src_dst(png_image, jpg_image, tmp_path, fmt)

    width, height = 100, 50
    resize_image(png_image, width, height, to=dst, method="height")
    tw, th = get_image_size(dst)
    assert th == height
Ejemplo n.º 2
0
def test_resize_thumbnail(png_image, jpg_image, tmp_path, fmt):
    src, dst = get_src_dst(png_image, jpg_image, tmp_path, fmt)

    width, height = 100, 50
    resize_image(png_image, width, height, to=dst, method="thumbnail")
    tw, th = get_image_size(dst)
    assert tw <= width
    assert th <= height
Ejemplo n.º 3
0
def test_resize_contain(png_image, jpg_image, tmp_path, fmt):
    src, dst = get_src_dst(png_image, jpg_image, tmp_path, fmt)

    width, height = 5, 50
    resize_image(png_image, width, height, to=dst, method="contain")
    tw, th = get_image_size(dst)
    assert tw <= width
    assert th <= height
Ejemplo n.º 4
0
    def check_branding_values(self):
        """ checks that user-supplied images and colors are valid (so to fail early)

            Images are checked for existence or downloaded then resized
            Colors are check for validity """

        # skip this step if none of related values were supplied
        if not sum([
                bool(x) for x in (
                    self.profile_image,
                    self.banner_image,
                    self.main_color,
                    self.secondary_color,
                )
        ]):
            return
        logger.info("checking your branding files and values")
        if self.profile_image:
            if self.profile_image.startswith("http"):
                save_file(self.profile_image, self.profile_path)
            else:
                if not self.profile_image.exists():
                    raise IOError(
                        f"--profile image could not be found: {self.profile_image}"
                    )
                shutil.move(self.profile_image, self.profile_path)
            resize_image(self.profile_path,
                         width=100,
                         height=100,
                         method="thumbnail")
        if self.banner_image:
            if self.banner_image.startswith("http"):
                save_file(self.banner_image, self.banner_path)
            else:
                if not self.banner_image.exists():
                    raise IOError(
                        f"--banner image could not be found: {self.banner_image}"
                    )
                shutil.move(self.banner_image, self.banner_path)
            resize_image(self.banner_path,
                         width=1060,
                         height=175,
                         method="thumbnail")

        if self.main_color and not is_hex_color(self.main_color):
            raise ValueError(
                f"--main-color is not a valid hex color: {self.main_color}")

        if self.secondary_color and not is_hex_color(self.secondary_color):
            raise ValueError(
                f"--secondary_color-color is not a valid hex color: {self.secondary_color}"
            )
Ejemplo n.º 5
0
    def check_branding_values(self):
        """ checks that user-supplied images and colors are valid (so to fail early)

            Images are checked for existence or downloaded then resized
            Colors are check for validity """

        # skip this step if none of related values were supplied
        if not sum([
                bool(x) for x in (
                    self.favicon,
                    self.main_logo,
                    self.secondary_logo,
                    self.main_color,
                    self.secondary_color,
                    self.about,
                )
        ]):
            return

        logger.info("checking your branding files and values")

        images = [
            (self.favicon, self.build_dir.joinpath("favicon.png"), 48, 48),
            (self.main_logo, self.main_logo_path, 300, 65),
            (self.secondary_logo, self.secondary_logo_path, 300, 65),
        ]

        for src, dest, width, height in images:
            if src:
                handle_user_provided_file(source=src, dest=dest)
                resize_image(dest,
                             width=width,
                             height=height,
                             method="thumbnail")

        if self.main_color and not is_hex_color(self.main_color):
            raise ValueError(
                f"--main-color is not a valid hex color: {self.main_color}")

        if self.secondary_color and not is_hex_color(self.secondary_color):
            raise ValueError(
                f"--secondary_color-color is not a valid hex color: {self.secondary_color}"
            )

        if self.about:
            handle_user_provided_file(source=self.about,
                                      dest=self.build_dir / "about.html")
Ejemplo n.º 6
0
def save_channel_branding(channels_dir, channel_id, save_banner=False):
    """ download, save and resize profile [and banner] of a channel """
    channel_json = get_channel_json(channel_id)

    thumbnails = channel_json["snippet"]["thumbnails"]
    for quality in ("medium", "default"):  # high:800px, medium:240px, default:88px
        if quality in thumbnails.keys():
            thumnbail = thumbnails[quality]["url"]
            break

    profile_path = channels_dir.joinpath(channel_id, "profile.jpg")
    if not profile_path.exists():
        save_file(thumnbail, profile_path)
        # resize profile as we only use up 100px/80 sq
        resize_image(profile_path, width=100, height=100)

    if save_banner:
        banner = channel_json["brandingSettings"]["image"]["bannerImageUrl"]
        banner_path = channels_dir.joinpath(channel_id, "banner.jpg")
        if not banner_path.exists():
            save_file(banner, banner_path)
Ejemplo n.º 7
0
def post_process_video(video_dir,
                       video_id,
                       video_format,
                       low_quality,
                       skip_recompress=False):
    # apply custom post-processing to downloaded video
    # - resize thumbnail
    # - recompress video if incorrect video_format or low_quality requested
    # find downloaded video from video_dir
    files = [
        p for p in video_dir.iterdir()
        if p.stem == "video" and p.suffix != ".jpg"
    ]
    if len(files) == 0:
        logger.error(f"Video file missing in {video_dir} for {video_id}")
        logger.debug(list(video_dir.iterdir()))
        raise FileNotFoundError(f"Missing video file in {video_dir}")
    if len(files) > 1:
        logger.warning(
            f"Multiple video file candidates for {video_id} in {video_dir}. Picking {files[0]} out of {files}"
        )
    src_path = files[0]

    # resize thumbnail. we use max width:248x187px in listing
    resize_image(src_path.parent.joinpath("thumbnail.jpg"),
                 width=248,
                 height=187,
                 method="cover")

    # don't reencode if not requesting low-quality and received wanted format
    if skip_recompress or (not low_quality
                           and src_path.suffix[1:] == video_format):
        return

    dst_path = src_path.parent.joinpath(f"video.{video_format}")
    recompress_video(src_path, dst_path, video_format)
Ejemplo n.º 8
0
    def update_metadata(self):
        # we use title, description, profile and banner of channel/user
        # or channel of first playlist
        main_channel_json = get_channel_json(self.main_channel_id)
        save_channel_branding(self.channels_dir,
                              self.main_channel_id,
                              save_banner=True)

        # if a single playlist was requested, use if for names;
        # otherwise, use main_channel's details.
        auto_title = (self.playlists[0].title
                      if self.is_playlist and len(self.playlists) == 1 else
                      main_channel_json["snippet"]["title"].strip())
        auto_description = (clean_text(self.playlists[0].description)
                            if self.is_playlist and len(self.playlists) == 1
                            else clean_text(
                                main_channel_json["snippet"]["description"]))
        self.title = self.title or auto_title or "-"
        self.description = self.description or auto_description or "-"

        if self.creator is None:
            if self.is_single_channel:
                self.creator = _("Youtube Channel “{title}”").format(
                    title=main_channel_json["snippet"]["title"])
            else:
                self.creator = _("Youtube Channels")
        self.publisher = self.publisher or "Kiwix"

        self.tags = self.tags or ["youtube"]
        if "_videos:yes" not in self.tags:
            self.tags.append("_videos:yes")

        self.zim_info.update(
            title=self.title,
            description=self.description,
            creator=self.creator,
            publisher=self.publisher,
            name=self.name,
            tags=self.tags,
        )

        # copy our main_channel branding into /(profile|banner).jpg if not supplied
        if not self.profile_path.exists():
            shutil.copy(
                self.channels_dir.joinpath(self.main_channel_id,
                                           "profile.jpg"),
                self.profile_path,
            )
        if not self.banner_path.exists():
            shutil.copy(
                self.channels_dir.joinpath(self.main_channel_id, "banner.jpg"),
                self.banner_path,
            )

        # set colors from images if not supplied
        if self.main_color is None or self.secondary_color is None:
            profile_main, profile_secondary = get_colors(self.profile_path)
        self.main_color = self.main_color or profile_main
        self.secondary_color = self.secondary_color or profile_secondary

        resize_image(
            self.profile_path,
            width=48,
            height=48,
            method="thumbnail",
            to=self.build_dir.joinpath("favicon.jpg"),
        )
Ejemplo n.º 9
0
def hook_youtube_dl_ffmpeg(video_format, low_quality, data):
    """ youtube-dl hook to convert video at end of download

        - if low_quality was request
        - if received format is not requested one """

    if data.get("status") != "finished":
        return

    src_path = pathlib.Path(data["filename"])
    tmp_path = src_path.parent.joinpath(
        "video.tmp.{fmt}".format(src=src_path.name, fmt=video_format)
    )
    dst_path = src_path.parent.joinpath("video.{fmt}".format(fmt=video_format))

    # resize thumbnail. we use max width:248x187px in listing
    # but our posters are 480x270px
    resize_image(
        src_path.parent.joinpath("video.jpg"), width=480, height=270, method="cover"
    )

    # don't reencode if not requesting low-quality and received wanted format
    if not low_quality and src_path.suffix[1:] == video_format:
        return

    video_codecs = {"mp4": "h264", "webm": "libvpx"}
    audio_codecs = {"mp4": "aac", "webm": "libvorbis"}
    params = {"mp4": ["-movflags", "+faststart"], "webm": []}

    args = ["ffmpeg", "-y", "-i", "file:{}".format(str(src_path))]

    if low_quality:
        args += [
            "-codec:v",
            video_codecs[video_format],
            "-quality",
            "best",
            "-cpu-used",
            "0",
            "-b:v",
            "300k",
            "-qmin",
            "30",
            "-qmax",
            "42",
            "-maxrate",
            "300k",
            "-bufsize",
            "1000k",
            "-threads",
            "8",
            "-vf",
            "scale='480:trunc(ow/a/2)*2'",
            "-codec:a",
            audio_codecs[video_format],
            "-b:a",
            "128k",
        ]
    else:
        args += [
            "-codec:v",
            video_codecs[video_format],
            "-quality",
            "best",
            "-cpu-used",
            "0",
            "-bufsize",
            "1000k",
            "-threads",
            "8",
            "-codec:a",
            audio_codecs[video_format],
        ]
    args += params[video_format]
    args += ["file:{}".format(str(tmp_path))]

    logger.info(
        "  converting {src} into {dst}".format(src=str(src_path), dst=str(dst_path))
    )
    logger.debug(nicer_args_join(args))

    ffmpeg = subprocess.run(args)
    ffmpeg.check_returncode()

    # delete original
    src_path.unlink()
    # rename temp filename with final one
    tmp_path.replace(dst_path)