mirror of
https://github.com/sashacmc/photo-importer.git
synced 2024-11-16 10:08:40 +02:00
Add Windows support
This commit is contained in:
parent
0fa0acfc99
commit
c90a5fa127
26
README.md
26
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
|
||||
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
||||
photo-importer (1.1.0) stable; urgency=medium
|
||||
|
||||
* Add Windows support
|
||||
|
||||
-- Alexander Bushnev <Alexander@Bushnev.ru> 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
|
||||
|
4
photo-importer-server.bat
Executable file
4
photo-importer-server.bat
Executable file
@ -0,0 +1,4 @@
|
||||
start "" http://localhost:8080/
|
||||
cd photo_importer
|
||||
server.py -c ..\photo-importer-win.cfg
|
||||
pause
|
59
photo-importer-win.cfg
Normal file
59
photo-importer-win.cfg
Normal file
@ -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
|
@ -55,7 +55,7 @@ class PhotoImporterHandler(http.server.BaseHTTPRequestHandler):
|
||||
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 @@ class PhotoImporterHandler(http.server.BaseHTTPRequestHandler):
|
||||
'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 @@ class PhotoImporterHandler(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
try:
|
||||
out_path = params['o'][0]
|
||||
except Exception as ex:
|
||||
except Exception:
|
||||
out_path = self.server.out_path()
|
||||
|
||||
result = None
|
||||
@ -235,7 +262,7 @@ class PhotoImporterHandler(http.server.BaseHTTPRequestHandler):
|
||||
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 @@ class PhotoImporterServer(http.server.HTTPServer):
|
||||
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 @@ class PhotoImporterServer(http.server.HTTPServer):
|
||||
|
||||
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,
|
||||
|
2
setup.py
2
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',
|
||||
|
@ -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 += "<input onclick=\"sendCommand('start', '" + data[dev].path + "');\" type=button class=\"btn btn-success btn-sm\" value=\"Import\"/> ";
|
||||
action += "<input onclick=\"sendCommand('start', '" + path + "');\" type=button class=\"btn btn-success btn-sm\" value=\"Import\"/> ";
|
||||
}
|
||||
} else {
|
||||
if (state == "mounted") {
|
||||
action += "<input onclick=\"sendCommand('umount', '" + dev + "');\" type=button class=\"btn btn-primary btn-sm\" value=\"Unmount\"/> ";
|
||||
if (data[dev].allow_start) {
|
||||
action += "<input onclick=\"sendCommand('start', '" + data[dev].path + "');\" type=button class=\"btn btn-success btn-sm\" value=\"Import\"/> ";
|
||||
action += "<input onclick=\"sendCommand('start', '" + path + "');\" type=button class=\"btn btn-success btn-sm\" value=\"Import\"/> ";
|
||||
}
|
||||
} else if (state == "unmounted") {
|
||||
action = "<input onclick=\"sendCommand('mount', '" + dev + "');\" type=button class=\"btn btn-primary btn-sm\" value=\"Mount\"/> ";
|
||||
@ -74,7 +77,6 @@
|
||||
} else if (state == "error") {
|
||||
stat += "<br/><font size=\"-2\">(" + data[dev].details + ")<font>"
|
||||
}
|
||||
var name = dev
|
||||
if (data[dev].read_only) {
|
||||
name += "<br/><font size=\"-2\">(read only)<font>"
|
||||
}
|
||||
@ -85,7 +87,7 @@
|
||||
+ "<td>" + action + "</td>"
|
||||
+ "<td>" + data[dev].size + " GB</td>"
|
||||
+ "<td>" + progress(data[dev].usage, "", "bg-info") + "</td>"
|
||||
+ "<td><a href=\"import?a=getlog&p=" + data[dev].path + "\"><img src=\"log.png\"/></a></td>"
|
||||
+ "<td><a href=\"import?a=getlog&p=" + path + "\"><img src=\"log.png\"/></a></td>"
|
||||
+ "</tr>"
|
||||
}
|
||||
html += "</table>"
|
||||
|
Loading…
Reference in New Issue
Block a user