Skip to content

Commit

Permalink
Merge pull request #113 from MikeGawi/convert
Browse files Browse the repository at this point in the history
No processing option #112
  • Loading branch information
MikeGawi authored Aug 18, 2024
2 parents e3f4c0b + c7bf1c3 commit 2388048
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 35 deletions.
6 changes: 6 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [Local Source](#local-source)
* [Cloud sync](#cloud-sync)
* [Other sources](#other-sources)
* [Pre-process photos](#pre-process-photos)
* [Google Photos](#google-photos)
* [Activate from ePiframe device](#activate-from-epiframe-device)
* [Activate from other device](#activate-from-other-device)
Expand Down Expand Up @@ -150,6 +151,10 @@ It is possible to download photos to a local storage (and use them by ePiframe)
* [gallery-dl](https://github.com/mikf/gallery-dl) - a command-line program to download image galleries and collections from DeviantArt, Flickr, Instagram, Pinterest and many more
* [iCloud Photos Downloader](https://github.com/icloud-photos-downloader/icloud_photos_downloader) - a command-line tool to download all your iCloud photos

### Pre-process photos

If you want photos to be pre-processed and ready for display in the frame (to save resources during conversion or to convert them earlier, on another device), this is possible using the `--convert` option command in [ePiframe commands](#command-line) and disabling conversion in [*config.cfg*](https://github.com/MikeGawi/ePiframe/blob/master/config.cfg) by switching the `convert` flag off.

## Google Photos

ePiframe needs to have credentials and *access token* to access Google Photos of Google account in an unsupervised way when Google Photos source is enabled in the configuration. For this You need to activate Google Photos API for the account used by ePiframe and configure application in Google Cloud Console.
Expand Down Expand Up @@ -407,6 +412,7 @@ Syntax: ```ePiframe.py [option]```
* ```--test``` - tests whole chain: credentials, pickle file and downloads photo **but without** sending it to the display. Used to test configuration, photo filtering, etc
* ```--test-display [file]``` - displays the photo ```file``` on attached display with current ePiframe configuration. If no file is provided the ```photo_convert_filename``` from the configuration is used. __Only__ converted photos should be put on display! Use ```--test-convert``` for that
* ```--test-convert [file]``` - converts the photo ```file``` to configured ```photo_convert_filename``` with current ePiframe configuration. If no file is provided the ```photo_download_name``` from the configuration is used
* ```--convert [source file] [target file or location]``` - converts the photo ```source file``` to target file or under specified location with current ePiframe configuration. Just convert, no thumbnails, no pre file. Used just to convert file to ePiframe quality.
* ```--no-skip``` - like ```--test``` but is not skipping to another photo, not marking photo as showed, etc.
* ```--users``` - manage users for the WebUI: add, change passwords, delete, etc.
* ```--help``` - show help
Expand Down
6 changes: 6 additions & 0 deletions config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ allow_triggers=1

[Image]

# Convert before displaying.
# If disabled, then You must have your photos pre-converted via the ePiframe command line!
# Command line actions will always work.
# Default: 1
convert=1

# Used for e-Paper SPI _black&white_ displays!
# There are 6 standard types of conversion to black&white image (add more in
# convertmanager module). Every option gives slightly different
Expand Down
97 changes: 69 additions & 28 deletions ePiframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import signal
import sys
from datetime import datetime
from typing import List

import pandas
import starlette.status
from pandas import DataFrame
Expand Down Expand Up @@ -68,11 +70,13 @@ def __init__(self):
if (
not self.check_arguments("--test-display")
and not self.check_arguments("--test-convert")
and not self.check_arguments("--convert")
and not self.check_arguments("--users")
):
self.process_flow()

self.process_test_convert()
self.process_convert_command()
self.process_test_display()

end_time = Logs.end_time()
Expand Down Expand Up @@ -466,6 +470,10 @@ def process_test_convert(self):
if self.check_arguments("--test-convert"):
self.test_convert()

def process_convert_command(self):
if self.check_arguments("--convert"):
self.convert_command()

def process_test_display(self):
if self.check_arguments("--test-display"):
self.test_display()
Expand Down Expand Up @@ -621,7 +629,12 @@ def process_file(
self.logging.log("Success!")
self.interval_multiplication(filename, photo)
self.save_index()
self.convert_file(filename, photo)
if bool(self.config.getint("convert")):
self.convert_file(filename, photo)
else:
self.logging.log(
"Conversion disabled in config by 'config' flag! Make sure your photos are pre-converted!"
)

def save_index(self):
# save index of current photo for next run
Expand Down Expand Up @@ -656,12 +669,9 @@ def save_pid(self):
raise

def check_system(self):
if (
not self.check_arguments("--test")
and not self.check_arguments("--test-convert")
and not self.check_arguments("--no-skip")
and not DisplayManager.is_hdmi(self.config.get("display_type"))
):
if not self.check_multiple_arguments(
["--test", "--test-convert", "--convert", "--no-skip"]
) and not DisplayManager.is_hdmi(self.config.get("display_type")):
self.process_check_system()

def process_check_system(self):
Expand Down Expand Up @@ -713,6 +723,11 @@ def show_help(self):
print(" with current ePiframe configuration")
print("--test-convert [file] converts the photo file to configured")
print(" photo_convert_filename with current ePiframe configuration")
print("--convert [source_file] [target_file_or_loc]")
print(" converts the [source_file] photo file to")
print(
" [target_file_or_loc] (if location - the filename is the same)"
)
print("--no-skip like --test but is not skipping to another photo")
print(
"--refresh force Google API data refresh even if refresh_rate flag is set to 'once'"
Expand All @@ -722,6 +737,13 @@ def show_help(self):

sys.exit(0)

@staticmethod
def check_multiple_arguments(names: List[str]) -> bool:
return_value = False
for name in names:
return_value = return_value or EPiframe.check_arguments(name)
return return_value

@staticmethod
def check_arguments(name: str) -> bool:
return name in [argument.lower() for argument in sys.argv]
Expand Down Expand Up @@ -870,6 +892,27 @@ def test_convert(self):
self.copy_file(filename, source)
self.process_convert(returned_value, source, target_filename)

def convert_command(self):
files = list(self.get_files())
if not files or len(files) != 2:
raise Exception(f"Files not specified!")

source, target = files
self.check_file_exist(source)
error, _ = ConvertManager().get_image_format(
self.config.get("convert_bin_path"), source, Constants.FIRST_FRAME_GIF
)

if error:
raise Exception(f"Unknown file {source} format ! - {error}")

if os.path.isdir(target):
target = os.path.join(target, source.split(os.sep)[-1])

self.process_convert(
None, source, "".join(target.rsplit(".")[:-1]) + ".bmp", thumbs=False
)

def get_source(
self, error: str, image_type, returned_value, source: str
) -> (str, str):
Expand All @@ -892,20 +935,15 @@ def check_file_exist(filename: str):
raise Exception(f"No file: {filename}!")

def process_convert(
self,
returned_value,
source: str,
target_filename: str,
self, returned_value, source: str, target_filename: str, thumbs: bool = True
):
if not os.path.exists(source):
self.logging.log(f"Fail! File was not retrieved! : {str(returned_value)}")
else:
import pandas as pandas_engine

if not self.convert(
source,
target_filename,
pandas_engine.DataFrame(),
source, target_filename, pandas_engine.DataFrame(), thumbs
):
self.logging.log("Fail!")

Expand All @@ -926,34 +964,36 @@ def test_display(self):
@staticmethod
def get_next_file():
return next(
(
element
for element in [argument.lower() for argument in sys.argv[1:]]
if "--" not in element
),
EPiframe.get_files(),
"",
)

@staticmethod
def get_files():
return (element for element in sys.argv[1:] if "--" not in element)

def show_image(self, target_filename: str):
try:
self.display_manager.show_image(target_filename)
except Exception as exception:
self.logging.log(f"Error sending photo to display: {exception}")
raise

def convert(
self,
filename: str,
target_filename: str,
photo,
):
def convert(self, filename: str, target_filename: str, photo, thumbs: bool = True):
return_value = False

self.auto_orient(filename)
filename_pre = os.path.join(
os.path.split(filename)[0], "pre_" + os.path.split(filename)[1]

filename_pre = (
os.path.join(
os.path.split(filename)[0], "pre_" + os.path.split(filename)[1]
)
if thumbs
else filename
)
self.copy_file(filename, filename_pre)

if thumbs:
self.copy_file(filename, filename_pre)
self.plugins_preprocess(photo, filename_pre)
error, width, height = ConvertManager().get_image_size(
self.config.get("convert_bin_path"), filename_pre, Constants.FIRST_FRAME_GIF
Expand All @@ -969,6 +1009,7 @@ def convert(
target_filename,
self.config,
DisplayManager.is_hdmi(self.config.get("display_type")),
thumbs,
)
is not None
):
Expand Down
6 changes: 6 additions & 0 deletions misc/config.default
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ allow_triggers=1

[Image]

# Convert before displaying.
# If disabled, then You must have your photos pre-converted via the ePiframe command line!
# Command line actions will always work.
# Default: 1
convert=1

# Used for e-Paper SPI _black&white_ displays!
# There are 6 standard types of conversion to black&white image (add more in
# convertmanager module). Every option gives slightly different
Expand Down
2 changes: 1 addition & 1 deletion misc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class Constants:

EPIFRAME_VERSION = "v1.9.5"
EPIFRAME_VERSION = "v1.9.6"
EPIFRAME_SECRET = "ePiframeSecretlyLovesYourPhotos"

# minimal needed python version
Expand Down
1 change: 1 addition & 0 deletions modules/configmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def load_settings(self):
ConfigProperty(
"photo_convert_path", self, prop_type=ConfigProperty.FILE_TYPE
),
ConfigProperty("convert", self, prop_type=ConfigProperty.BOOLEAN_TYPE),
ConfigProperty("log_files", self, not_empty=False),
ConfigProperty(
"convert_bin_path", self, prop_type=ConfigProperty.FILE_TYPE
Expand Down
19 changes: 13 additions & 6 deletions modules/convertmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,13 @@ def __convert_option(
target: str,
config: ConfigManager,
hdmi: bool,
thumbs: bool = True,
) -> str:
option = int(config.get("convert_option"))
width = config.getint("image_width")
height = config.getint("image_height")
back = config.get("background_color")

if int(option) > len(self.__CONVERT_OPTIONS) or int(option) < 1:
option = 1
option = self.__get_option(option)

# space at the end as those flag are optional
negate = self.__INVERT_FLAG if config.getint("invert_colors") == 1 else ""
Expand Down Expand Up @@ -158,14 +157,21 @@ def __convert_option(
option,
rotate,
target,
thumb1st,
thumb2nd,
thumb1st if thumbs else "",
thumb2nd if thumbs else "",
width,
)

print(return_value.replace("(", "\(").replace(")", "\)"))
return return_value

def __get_option(self, option: int) -> int:
return (
1
if int(option) > len(self.__CONVERT_OPTIONS) or int(option) < 1
else option
)

def __get_background(self, back, height, original_height, original_width, width):
if back == Constants.BACK_PHOTO:
back = Constants.BACK_WHITE
Expand Down Expand Up @@ -298,10 +304,11 @@ def convert_image(
target: str,
config: ConfigManager,
hdmi: bool = False,
thumbs: bool = True,
) -> bytes:
out, error = self.__subproc(
self.__convert_option(
original_width, original_height, target, config, hdmi
original_width, original_height, target, config, hdmi, thumbs
),
source_file,
)
Expand Down
51 changes: 51 additions & 0 deletions tests/test_convertmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,57 @@ def test_convert_6():
]


def test_convert_no_thumb():
remove_file(filename)
set_file(
"\n".join(
[
"[test_section]",
"convert_option=6",
"convert_bin_path=bin",
"image_width=800",
"image_height=600",
"background_color=white",
"invert_colors=0",
"rotation=90",
"horizontal=1",
"turned=0",
"auto_gamma=0",
"auto_level=0",
"normalize=0",
"grayscale=0",
"brightness=0",
"contrast=10",
"photo_convert_path=path",
"thumb_photo_download_name=download_name",
"thumb_photo_convert_filename=convert_name",
"epaper_color=BW",
"colors_num=",
]
),
filename,
)

config = get_config()
with patch.object(subprocess, "Popen", MockedPopen) as popen, Capturing() as output:
popen.set_data("".encode())
ConvertManager().convert_image(
original_width=1200,
original_height=960,
source_file="source",
config=config,
target="target",
thumbs=False,
)

assert output == [
"bin [] -limit thread 1 \\( +clone -sample 800x600 -brightness-contrast 0,10 -colors 2 +dither -background "
"white -gravity center -extent 800x600 -type bilevel -write target \\) NULL:",
"bin source -limit thread 1 ( +clone -sample 800x600 -brightness-contrast 0,10 -colors 2 +dither -background "
"white -gravity center -extent 800x600 -type bilevel -write target ) NULL:",
]


def test_convert_background():
remove_file(filename)
set_file(
Expand Down

0 comments on commit 2388048

Please sign in to comment.