diff --git a/README.md b/README.md index 06e0b39..a8a853f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Standalone web server for fast media import for headless computer * python3-psutil * exiftran or jpegtran * pmount (only for server) + * pypiwin32 (only for windows) ### Installation Options: @@ -45,6 +46,21 @@ sudo dpkg -i ../photo-importer_1.0.1_all.deb sudo python3 ./setup.py install ``` +#### Installing for Windows +Download and install python3 for you Windows distributive +https://www.python.org/downloads/windows/ + +Download and install exiftool +https://exiftool.org/ + +Download and extract jpegtran to photo_importer folder +http://sylvana.net/jpegcrop/jpegtran/ + +Install python dependencies +```bash +python -m pip install progressbar psutil pyexiftool pypiwin32 +``` + ## Usage ### Command-Line Interface @@ -70,6 +86,16 @@ Will import (by default move, but it can be changed in config) files from /path/ ![Web interface example](https://user-images.githubusercontent.com/28735879/76140174-f1995300-6057-11ea-8718-19c38650c786.png) +### Windows command line +```bash +cd photo_importer +run.py -c ..\photo-importer-win.cfg path\to\media\files \output\path +``` +### Windows web +```bash +photo-importer-server.bat +``` + ## Configuration The server config file located in /etc/photo-importer.cfg diff --git a/debian/changelog b/debian/changelog index 285ea06..5b71778 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +photo-importer (1.1.0) stable; urgency=medium + + * Add Windows support + + -- Alexander Bushnev Mon, 31 Jan 2022 00:10:46 +0100 + photo-importer (1.0.9) stable; urgency=medium * Add windows compatibility by means of use_shutil option diff --git a/photo-importer-server.bat b/photo-importer-server.bat new file mode 100755 index 0000000..d8523c4 --- /dev/null +++ b/photo-importer-server.bat @@ -0,0 +1,4 @@ +start "" http://localhost:8080/ +cd photo_importer +server.py -c ..\photo-importer-win.cfg +pause diff --git a/photo-importer-win.cfg b/photo-importer-win.cfg new file mode 100644 index 0000000..714795d --- /dev/null +++ b/photo-importer-win.cfg @@ -0,0 +1,59 @@ +[main] +# time source order +time_src_image = exif,name +time_src_video = exif,name,attr +time_src_audio = exif,name,attr + +# Date/Time formats +out_date_format = %%Y/%%Y-%%m-%%d +out_time_format = %%Y-%%m-%%d_%%H-%%M-%%S + +# Output sub directorines +out_subdir_image = Foto +out_subdir_video = Video +out_subdir_audio = Audio + +# File extensions +file_ext_image = jpeg,jpg,cr2 +file_ext_video = mp4,mpg,mpeg,mov,avi,mts,3gp,m4v +file_ext_audio = mp3,3gpp,m4a,wav +file_ext_garbage = thm,ctg +file_ext_ignore = ini,zip,db + +# Thread count +threads_count = 2 + +# Remove garbage files (photo config, thumbnails, etc.) 0/1 +remove_garbage = 1 + +# Remove empty directories +remove_empty_dirs = 1 + +# Remove source files (in case of out_path specified) 0/1 +move_mode = 1 + +# umask for new folder and copied files +umask = 0o000 + +# use jpegtran in place of exiftran 0/1 +use_jpegtran = 1 + +# use shutil libarary in place of system calls 0/1 +# slower but provide more cross platform compatibility +use_shutil = 1 + +[server] +# server port +port = 8080 + +# path to html files +web_path = ..\web + +# imported output path +out_path = C:\ + +# fixed input path +in_path = + +# log file +log_file = ..\photo-importer-server.log diff --git a/photo_importer/server.py b/photo_importer/server.py index a91705c..b95b14d 100755 --- a/photo_importer/server.py +++ b/photo_importer/server.py @@ -55,7 +55,7 @@ def __get_mounted_list(self): def __bytes_to_gbytes(self, b): return round(b / 1024. / 1024. / 1024., 2) - def __get_removable_devices(self): + def __get_removable_devices_posix(self): mount_list = self.__get_mounted_list() res = {} for path in glob.glob('/sys/block/*/device'): @@ -82,6 +82,33 @@ def __get_removable_devices(self): 'read_only': read_only } + return res + + def __get_removable_devices_win(self): + import win32api + import win32con + import win32file + + res = {} + for d in win32api.GetLogicalDriveStrings().split('\x00'): + if d and win32file.GetDriveType(d) == win32con.DRIVE_REMOVABLE: + dev_name = FIXED_IN_PATH_NAME + d + res[dev_name] = { + 'dev_path': dev_name, + 'mount_path': d, + 'read_only': not os.access(d, os.W_OK) + } + return res + + def __get_removable_devices(self): + res = {} + if os.name == 'nt': + res = self.__get_removable_devices_win() + elif os.name == 'posix': + res = self.__get_removable_devices_posix() + else: + raise Exception('Unsupported os: %s' % os.name) + if self.server.fixed_in_path() != '': res[FIXED_IN_PATH_NAME] = { 'dev_path': FIXED_IN_PATH_NAME, @@ -214,7 +241,7 @@ def __import_request(self, params): try: out_path = params['o'][0] - except Exception as ex: + except Exception: out_path = self.server.out_path() result = None @@ -235,7 +262,7 @@ def __import_request(self, params): def __sysinfo_request(self, params): try: path = params['p'][0] - except Exception as ex: + except Exception: path = self.server.out_path() res = {} du = psutil.disk_usage(path) @@ -338,7 +365,7 @@ def __init__(self, cfg): self.__cfg = cfg self.__importers = {} port = int(cfg['server']['port']) - self.__web_path = cfg['server']['web_path'] + self.__web_path = os.path.normpath(cfg['server']['web_path']) self.__out_path = cfg['server']['out_path'] self.__fixed_in_path = cfg['server']['in_path'] self.__move_mode = int(cfg['main']['move_mode']) @@ -358,8 +385,6 @@ def move_mode(self): def import_start(self, in_path, out_path): logging.info('import_start: %s', in_path) - if in_path in self.__importers and in_path != self.fixed_in_path(): - raise Exception('Already started') self.__importers[in_path] = importer.Importer( self.__cfg, diff --git a/setup.py b/setup.py index 0f3f741..955e783 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup setup(name='photo-importer', - version='1.0.9', + version='1.1.0', description='Photo importer tool', author='Alexander Bushnev', author_email='Alexander@Bushnev.ru', diff --git a/web/index.html b/web/index.html index 967d333..f4db2e4 100644 --- a/web/index.html +++ b/web/index.html @@ -48,15 +48,18 @@ for (var dev in data) { var action = "" var state = data[dev].state - if (dev == "none") { + var name = dev + var path = encodeURIComponent(data[dev].path) + if (dev.startsWith("none")) { + name = "" if (state == "mounted" || state == "done") { - action += " "; + action += " "; } } else { if (state == "mounted") { action += " "; if (data[dev].allow_start) { - action += " "; + action += " "; } } else if (state == "unmounted") { action = " "; @@ -74,7 +77,6 @@ } else if (state == "error") { stat += "
(" + data[dev].details + ")" } - var name = dev if (data[dev].read_only) { name += "
(read only)" } @@ -85,7 +87,7 @@ + "" + action + "" + "" + data[dev].size + " GB" + "" + progress(data[dev].usage, "", "bg-info") + "" - + "" + + "" + "" } html += ""