Esempio n. 1
0
def test_optimize_images_task_failure(tmp_path, font):
    tmp_fl = tmp_path / "tmp.jpg"
    dst = tmp_path / "out.jpg"

    # send an unreadable file

    tmp_fl.touch(mode=0o377)
    task = Task(
        src_path=str(tmp_fl.resolve()),
        quality=50,
        remove_transparency=False,
        reduce_colors=False,
        max_colors=256,
        max_w=0,
        max_h=0,
        keep_exif=False,
        convert_all=False,
        conv_big=False,
        force_del=False,
        bg_color=(255, 255, 255),
        grayscale=False,
        no_size_comparison=True,
        fast_mode=False,
    )
    with pytest.raises(Exception):
        run_optimize_images_task(task, tmp_fl, dst)

    assert not tmp_fl.exists()
    assert not dst.exists()
Esempio n. 2
0
    def on_created(self, event):
        t = self.t
        if '~temp~' in event.src_path:
            return
        if event.is_directory:
            return
        if event.src_path in self.paths_to_ignore:
            return

        self.paths_to_ignore.append(event.src_path)
        if is_image(event.src_path) and '~temp~' not in event.src_path:
            self.wait_for_write_finish(event.src_path)
            self.new_files += 1

            img_task = Task(event.src_path, t.quality, t.remove_transparency,
                            t.reduce_colors, t.max_colors, t.max_w, t.max_h,
                            t.keep_exif, t.convert_all, t.conv_big,
                            t.force_del, t.bg_color, t.grayscale,
                            t.no_size_comparison, t.fast_mode)

            r = do_optimization(img_task)
            self.total_src_size += r.orig_size
            if r.was_optimized:
                self.optimized_files += 1
                self.total_bytes_saved += r.orig_size - r.final_size

            show_file_status(r, self.line_width, self.icons)
Esempio n. 3
0
    def on_created(self, event):
        if (event.is_directory
                or not is_image(event.src_path)
                or '~temp~' in event.src_path
                or event.src_path in self.paths_to_ignore):
            return

        self.paths_to_ignore.append(event.src_path)
        self.wait_for_write_finish(event.src_path)
        self.new_files += 1

        task = self.task
        img_task = Task(event.src_path, task.quality, task.remove_transparency,
                        task.reduce_colors, task.max_colors, task.max_w,
                        task.max_h, task.keep_exif, task.convert_all,
                        task.conv_big, task.force_del, task.bg_color,
                        task.grayscale, task.no_size_comparison, task.fast_mode)

        result: TaskResult = do_optimization(img_task)
        self.total_src_size += result.orig_size
        if result.was_optimized:
            self.optimized_files += 1
            self.total_bytes_saved += result.orig_size - result.final_size

        show_file_status(result, self.line_width, self.icons)
Esempio n. 4
0
def optimize(filepath):
    remove_transparency = True
    reduce_colors = False
    max_colors = 255
    max_w = 0
    max_h = 0
    keep_exif = True
    convert_all = False
    conv_big = False
    force_del = False
    bg_color = (255, 255, 255)  # By default, apply a white background
    grayscale = False
    ignore_size_comparison = False
    fast_mode = False

    task = Task(
        filepath, constants.DEFAULT_QUALITY, remove_transparency,
        reduce_colors, max_colors, max_w, max_h, keep_exif, convert_all,
        conv_big, force_del, bg_color, grayscale, ignore_size_comparison,
        fast_mode
    )

    r = do_optimization(task)
    total_src_size = r.orig_size
    report = json_report(r, filepath)
    print(report)
    return report
Esempio n. 5
0
def optimize_jpeg(
    src: pathlib.Path,
    dst: pathlib.Path,
    quality: Optional[int] = 85,
    fast_mode: Optional[bool] = True,
    keep_exif: Optional[bool] = True,
    grayscale: Optional[bool] = False,
    **options,
) -> bool:
    """method to optimize JPEG files using a pure python external optimizer
    quality: JPEG quality (integer between 1 and 100)
        values: 50 | 55 | 35 | 100 | XX
    keep_exif: Whether to keep EXIF data in JPEG (boolean)
        values: True | False
    grayscale: Whether to convert image to grayscale (boolean)
        values: True | False
    fast_mode: Whether to use faster but weaker compression (boolean)
        values: True | False"""

    ensure_matches(src, "JPEG")

    # use a temporary file as source as optimization is done destructively
    tmp_path = get_temporary_copy(src)

    # generate JPEG task for optimize_images

    optimization_task = Task(
        src_path=str(tmp_path.resolve()),
        quality=quality,
        # remove_transparency is specific to PNG and hence is ignored, but we need to supply the default value
        remove_transparency=False,
        # reduce_colors is specific to PNG and hence is ignored, but we need to supply the default value
        reduce_colors=False,
        # max_colors is specific to PNG and hence is ignored, but we need to supply the default value
        max_colors=256,
        # max_w and max_h is 0 because we have a better image resizing function in scraperlib already
        max_w=0,
        max_h=0,
        keep_exif=keep_exif,
        # convert_all is specific to PNG and hence is ignored, but we need to supply the default value
        convert_all=False,
        # convert_big is specific to PNG and hence is ignored, but we need to supply the default value
        conv_big=False,
        # force_del is specific to PNG and hence is ignored, but we need to supply the default value
        force_del=False,
        # bg_color is specific to PNG and hence is ignored, but we need to supply the default value
        bg_color=(255, 255, 255),
        grayscale=grayscale,
        no_size_comparison=True,
        fast_mode=fast_mode,
    )

    # optimize the image
    return run_optimize_images_task(optimization_task, tmp_path, dst)
Esempio n. 6
0
def main():
    appstart = timer()
    line_width, our_pool_executor, workers = adjust_for_platform()
    (src_path, recursive, quality, remove_transparency, reduce_colors,
     max_colors, max_w, max_h, keep_exif, convert_all, conv_big, force_del,
     bg_color, grayscale, ignore_size_comparison, fast_mode) = get_args()
    found_files = 0
    optimized_files = 0
    total_src_size = 0
    total_bytes_saved = 0

    # Optimize all images in a directory
    if os.path.isdir(src_path):
        recursion_txt = 'Recursively searching' if recursive else 'Searching'
        opt_msg = 'and optimizing image files'
        exif_txt = '(keeping exif data) ' if keep_exif else ''
        print(f"\n{recursion_txt} {opt_msg} {exif_txt}in:\n{src_path}\n")

        tasks = (Task(img_path, quality, remove_transparency, reduce_colors,
                      max_colors, max_w, max_h, keep_exif, convert_all,
                      conv_big, force_del, bg_color, grayscale,
                      ignore_size_comparison, fast_mode)
                 for img_path in search_images(src_path, recursive=recursive))

        with our_pool_executor(max_workers=workers) as executor:
            for r in executor.map(do_optimization, tasks):
                found_files += 1
                total_src_size += r.orig_size
                if r.was_optimized:
                    optimized_files += 1
                    total_bytes_saved += r.orig_size - r.final_size
                show_file_status(r, line_width)

    # Optimize a single image
    elif os.path.isfile(src_path):
        found_files += 1

        img_task = Task(src_path, quality, remove_transparency, reduce_colors,
                        max_colors, max_w, max_h, keep_exif, convert_all,
                        conv_big, force_del, bg_color, grayscale,
                        ignore_size_comparison, fast_mode)

        r = do_optimization(img_task)
        total_src_size = r.orig_size
        if r.was_optimized:
            optimized_files = 1
            total_bytes_saved = r.orig_size - r.final_size
        show_file_status(r, line_width)
    else:
        print(
            "No image files were found. Please enter a valid path to the "
            "image file or the folder containing any images to be processed.")
        exit()

    if found_files:
        time_passed = timer() - appstart
        show_final_report(found_files, optimized_files, total_src_size,
                          total_bytes_saved, time_passed)
    else:
        print(
            "No supported image files were found in the specified directory.\n"
        )
Esempio n. 7
0
def optimize_batch(src_path, watch_dir, recursive, quality, remove_transparency,
                   reduce_colors, max_colors, max_w, max_h, keep_exif, convert_all,
                   conv_big, force_del, bg_color, grayscale, ignore_size_comparison, 
                   fast_mode, jobs, output_config):
    appstart = timer()
    line_width, our_pool_executor, workers = adjust_for_platform()

    if jobs != 0:
        workers = jobs

    found_files = 0
    optimized_files = 0
    skipped_files = 0
    total_src_size = 0
    total_bytes_saved = 0

    if watch_dir:
        if not os.path.isdir(os.path.abspath(src_path)):
            msg = "\nPlease specify a valid path to an existing folder."
            raise OIInvalidPathError(msg)

        watch_task = Task(src_path, quality, remove_transparency, reduce_colors,
                          max_colors, max_w, max_h, keep_exif, convert_all,
                          conv_big, force_del, bg_color, grayscale,
                          ignore_size_comparison, fast_mode, output_config)

        from optimize_images.watch import watch_for_new_files
        watch_for_new_files(watch_task)
        return

    # Optimize all images in a directory
    elif os.path.isdir(src_path):

        if not output_config.quiet_mode:
            icons = IconGenerator()
            recursion_txt = 'Recursively searching' if recursive else 'Searching'
            opt_msg = 'and optimizing image files'
            exif_txt = '(keeping exif data) ' if keep_exif else ''
            print(f"\n{recursion_txt} {opt_msg} {exif_txt}in:\n{src_path}\n")

        tasks = (Task(img_path, quality, remove_transparency, reduce_colors,
                      max_colors, max_w, max_h, keep_exif, convert_all, conv_big,
                      force_del, bg_color, grayscale, ignore_size_comparison, fast_mode,
                      output_config)
                 for img_path in search_images(src_path, recursive=recursive))

        num_images, tasks = count_gen(tasks)
        with our_pool_executor(max_workers=workers) as executor:
            current_img = ''
            try:
                for result in executor.map(do_optimization, tasks):
                    current_img = result.img
                    found_files += 1
                    total_src_size += result.orig_size
                    if result.was_optimized:
                        optimized_files += 1
                        total_bytes_saved += result.orig_size - result.final_size
                    else:
                        skipped_files += 1

                    if result.output_config.quiet_mode:
                        continue

                    if result.output_config.show_overall_progress:
                        cur_time_passed = round(timer() - appstart)
                        perc_done = found_files / num_images * 100
                        message = f"[{cur_time_passed:.1f}s {perc_done:.1f}%] {icons.optimized} {optimized_files} {icons.skipped} {skipped_files}, saved {human(total_bytes_saved)}"
                        print(message, end='\r')
                    else:
                        show_file_status(result, line_width, icons)
                    
            except concurrent.futures.process.BrokenProcessPool as bppex:
                show_img_exception(bppex, current_img)
            except KeyboardInterrupt:
                msg = "\b \n\n  == Operation was interrupted by the user. ==\n"
                raise OIKeyboardInterrupt(msg)

    # Optimize a single image
    elif os.path.isfile(src_path) and '~temp~' not in src_path:
        found_files += 1

        img_task = Task(src_path, quality, remove_transparency, reduce_colors,
                        max_colors, max_w, max_h, keep_exif, convert_all, conv_big,
                        force_del, bg_color, grayscale, ignore_size_comparison, fast_mode, 
                        output_config)

        result = do_optimization(img_task)
        total_src_size = result.orig_size
        if result.was_optimized:
            optimized_files = 1
            total_bytes_saved = result.orig_size - result.final_size
        
        if not result.output_config.quiet_mode:
            icons = IconGenerator()
            show_file_status(result, line_width, icons)
    else:
        msg = "\nNo image files were found. Please enter a valid path to the " \
              "image file or the folder containing any images to be processed."
        raise OIImagesNotFoundError(msg)

    if found_files:
        time_passed = timer() - appstart
        show_final_report(found_files, optimized_files, total_src_size,
                          total_bytes_saved, time_passed, output_config)
    else:
        msg = "\nNo supported image files were found in the specified directory."
        raise OIImagesNotFoundError(msg)
Esempio n. 8
0
def main():
    appstart = timer()
    line_width, our_pool_executor, workers = adjust_for_platform()

    (watch_dir, src_path, recursive, quality, remove_transparency,
     reduce_colors, max_colors, max_w, max_h, keep_exif, convert_all, conv_big,
     force_del, bg_color, grayscale, ignore_size_comparison, fast_mode, jobs) \
        = get_args()

    if jobs != 0:
        workers = jobs

    found_files = 0
    optimized_files = 0
    total_src_size = 0
    total_bytes_saved = 0

    if watch_dir:
        if not os.path.isdir(os.path.abspath(src_path)):
            print("\nPlease secify a valid path to an existing folder.")
            sys.exit(1)

        watch_task = Task(src_path, quality, remove_transparency,
                          reduce_colors, max_colors, max_w, max_h, keep_exif,
                          convert_all, conv_big, force_del, bg_color,
                          grayscale, ignore_size_comparison, fast_mode)

        watch_for_new_files(watch_task)
        sys.exit()

    # Optimize all images in a directory
    elif os.path.isdir(src_path):
        icons = IconGenerator()
        recursion_txt = 'Recursively searching' if recursive else 'Searching'
        opt_msg = 'and optimizing image files'
        exif_txt = '(keeping exif data) ' if keep_exif else ''
        print(f"\n{recursion_txt} {opt_msg} {exif_txt}in:\n{src_path}\n")

        tasks = (Task(img_path, quality, remove_transparency, reduce_colors,
                      max_colors, max_w, max_h, keep_exif, convert_all,
                      conv_big, force_del, bg_color, grayscale,
                      ignore_size_comparison, fast_mode)
                 for img_path in search_images(src_path, recursive=recursive))

        with our_pool_executor(max_workers=workers) as executor:
            current_img = ''
            try:
                for result in executor.map(do_optimization, tasks):
                    current_img = result.img
                    found_files += 1
                    total_src_size += result.orig_size
                    if result.was_optimized:
                        optimized_files += 1
                        total_bytes_saved += result.orig_size - result.final_size
                    show_file_status(result, line_width, icons)
            except concurrent.futures.process.BrokenProcessPool as bppex:
                show_img_exception(bppex, current_img)
            except KeyboardInterrupt:
                print(
                    "\b \n\n  == Operation was interrupted by the user. ==\n")

    # Optimize a single image
    elif os.path.isfile(src_path) and '~temp~' not in src_path:
        icons = IconGenerator()
        found_files += 1

        img_task = Task(src_path, quality, remove_transparency, reduce_colors,
                        max_colors, max_w, max_h, keep_exif, convert_all,
                        conv_big, force_del, bg_color, grayscale,
                        ignore_size_comparison, fast_mode)

        result = do_optimization(img_task)
        total_src_size = result.orig_size
        if result.was_optimized:
            optimized_files = 1
            total_bytes_saved = result.orig_size - result.final_size
        show_file_status(result, line_width, icons)
    else:
        print(
            "\nNo image files were found. Please enter a valid path to the "
            "image file or the folder containing any images to be processed.")
        sys.exit()

    if found_files:
        time_passed = timer() - appstart
        show_final_report(found_files, optimized_files, total_src_size,
                          total_bytes_saved, time_passed)
    else:
        print(
            "\nNo supported image files were found in the specified directory.\n"
        )
Esempio n. 9
0
def optimize_png(
    src: pathlib.Path,
    dst: pathlib.Path,
    reduce_colors: Optional[bool] = False,
    max_colors: Optional[int] = 256,
    fast_mode: Optional[bool] = True,
    remove_transparency: Optional[bool] = False,
    background_color: Optional[Tuple[int, int, int]] = (255, 255, 255),
    grayscale: Optional[bool] = False,
    **options,
) -> bool:
    """method to optimize PNG files using a pure python external optimizer

    Arguments:
        reduce_colors: Whether to reduce colors using adaptive color pallette (boolean)
            values: True | False
        max_colors: Maximum number of colors if reduce_colors is True (integer between 1 and 256)
            values: 35 | 64 | 256 | 128 | XX
        fast_mode: Whether to use faster but weaker compression (boolean)
            values: True | False
        remove_transparency: Whether to remove transparency (boolean)
            values: True | False
        background_color: Background color if remove_transparency is True (tuple containing RGB values)
            values: (255, 255, 255) | (221, 121, 108) | (XX, YY, ZZ)
        grayscale: Whether to convert image to grayscale (boolean)
            values: True | False"""

    ensure_matches(src, "PNG")

    # use a temporary file as source as optimization is done destructively
    tmp_path = get_temporary_copy(src)

    # generate PNG task for optimize_images

    optimization_task = Task(
        src_path=str(tmp_path.resolve()),
        # quality is specific to JPEG and hence is ignored, but we need to supply the default value
        quality=90,
        remove_transparency=remove_transparency,
        reduce_colors=reduce_colors,
        # max_colors is ignored if reduce_colors is False, but we need to provide a default value
        max_colors=max_colors,
        # max_w and max_h is 0 because we have a better image resizing function in scraperlib already
        max_w=0,
        max_h=0,
        # keep_exif is specific to JPEG and hence is ignored, but we need to supply the default value
        keep_exif=False,
        # convert_all converts all PNG to JPEG, hence set to False
        convert_all=False,
        # conv_big converts big PNG images to JPEG, hence set to False
        conv_big=False,
        # force_del deletes the original PNG after convertion to JPEG if the above two options are used, hence kept False
        force_del=False,
        # bg_color is only used if remove_transparency is True, but we need to supply a default value always
        bg_color=background_color,
        grayscale=grayscale,
        no_size_comparison=True,
        fast_mode=fast_mode,
    )

    # optimize the image
    return run_optimize_images_task(optimization_task, tmp_path, dst)