Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structure upgrade and refactoring #13

Merged
merged 14 commits into from
Jul 8, 2024
56 changes: 56 additions & 0 deletions .github/workflows/build-debian-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Build Debian Package

on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Build Debian package
run: |
sudo apt-get update
sudo apt-get install -y devscripts debhelper curl
# Ensure you have the necessary build dependencies
sudo apt-get build-dep .
# Build the Debian package
debuild -us -uc -b

- name: Create artifact directory
run: mkdir -p artifacts

- name: Move Debian package to artifact directory
run: mv ../*.deb artifacts/

- name: Test Debian package installation
run: |
sudo apt install -y pip python3-exif python3-progressbar exiftran python3-psutil
sudo pip install PyExifTool
sudo dpkg -i artifacts/*.deb
sudo systemctl enable photo-importer.service
sudo systemctl restart photo-importer.service
sudo systemctl status photo-importer.service
photo-importer -h

- name: Upload Debian package as artifact
uses: actions/upload-artifact@v2
with:
name: debian-package
path: artifacts/*.deb
24 changes: 24 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Pylint

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
pip install .
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py') --disable missing-function-docstring,missing-module-docstring,missing-class-docstring,broad-exception-caught
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ sudo pip install photo-importer
#### Installing as debian package
```bash
debuild -b
sudo dpkg -i ../photo-importer_1.2.0_all.deb
sudo apt install pip python3-exif python3-progressbar exiftran python3-psutil
sudo pip install PyExifTool
sudo dpkg -i ../photo-importer_1.2.5_all.deb
```
#### Installing via setup.py
```bash
Expand Down
7 changes: 7 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
photo-importer (1.2.5) stable; urgency=medium

* Package refactoring
* Code cleanup

-- Alexander Bushnev <[email protected]> Tue, 09 Jul 2024 00:24:52 +0200

photo-importer (1.2.4) stable; urgency=medium

* Add time_shift options
Expand Down
2 changes: 1 addition & 1 deletion debian/compat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9
10
4 changes: 2 additions & 2 deletions photo_importer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import configparser


class Config(object):
class Config:
DEFAULT_CONFIG_FILES = (
os.path.expanduser('~/.photo-importer.cfg'),
'/etc/photo-importer.cfg',
Expand Down Expand Up @@ -66,7 +66,7 @@ def __create_if_not_exists(self):
if os.path.exists(self.DEFAULT_CONFIG_FILES[0]):
return

with open(self.DEFAULT_CONFIG_FILES[0], 'w') as conffile:
with open(self.DEFAULT_CONFIG_FILES[0], 'w', encoding='utf-8') as conffile:
self.__config.write(conffile)

def __getitem__(self, sect):
Expand Down
73 changes: 34 additions & 39 deletions photo_importer/fileprop.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/python3
# pylint: disable=too-many-arguments

import os
import re
import stat
import time
import logging
import exiftool
import datetime
import exiftool


IGNORE = 0
Expand All @@ -16,7 +17,7 @@
GARBAGE = 4


class FileProp(object):
class FileProp:
DATE_REGEX = [
(
re.compile(r'\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}'),
Expand Down Expand Up @@ -59,8 +60,6 @@ class FileProp(object):
IGNORE: 'file_ext_ignore',
}

EXT_TO_TYPE = {}

DATE_TAGS = [
'EXIF:DateTimeOriginal',
'H264:DateTimeOriginal',
Expand All @@ -70,8 +69,8 @@ class FileProp(object):
'EXIF:CreateDate',
]

def __init__(self, config):
self.__config = config
def __init__(self, conf):
self.__config = conf
self.__prepare_ext_to_type()
self.__out_list = set()
self.__exiftool = exiftool.ExifToolHelper()
Expand All @@ -80,38 +79,38 @@ def __del__(self):
self.__exiftool.terminate()

def __prepare_ext_to_type(self):
self.EXT_TO_TYPE = {}
self.ext_to_type = {}
for tp, cfg in self.FILE_EXT_CFG.items():
for ext in self.__config['main'][cfg].split(','):
ext = '.' + ext.lower()
if ext in self.EXT_TO_TYPE:
logging.fatal('Double ext: ' + ext)
self.EXT_TO_TYPE[ext] = tp
if ext in self.ext_to_type:
logging.fatal('Double ext: %s', ext)
self.ext_to_type[ext] = tp

def __type_by_ext(self, ext):
try:
return self.EXT_TO_TYPE[ext]
return self.ext_to_type[ext]
except KeyError:
logging.warning('Unknown ext: ' + ext)
logging.warning('Unknown ext: %s', ext)
return IGNORE

def __time(self, fullname, name, tp):
if tp not in (IMAGE, VIDEO, AUDIO):
return None

for src in self.__config['main'][self.TIME_SRC_CFG[tp]].split(','):
time = None
ftime = None
if src == 'exif':
time = self.__time_by_exif(fullname)
ftime = self.__time_by_exif(fullname)
elif src == 'name':
time = self.__time_by_name(name)
ftime = self.__time_by_name(name)
elif src == 'attr':
time = self.__time_by_attr(fullname)
ftime = self.__time_by_attr(fullname)
else:
raise Exception('Wrong time_src: ' + src)
raise UserWarning(f'Wrong time_src: {src}')

if time:
return time
if ftime:
return ftime

return None

Expand All @@ -120,11 +119,11 @@ def __time_by_name(self, fname):
mat = exp.findall(fname)
if len(mat):
try:
time = datetime.datetime.strptime(mat[0], fs)
if time.year < 1990 or time.year > 2100:
ftime = datetime.datetime.strptime(mat[0], fs)
if ftime.year < 1990 or ftime.year > 2100:
continue

return time
return ftime
except ValueError:
pass
return None
Expand All @@ -140,34 +139,32 @@ def __time_by_exif(self, fullname):
md = md[0:pos]
return datetime.datetime.strptime(md, '%Y:%m:%d %H:%M:%S')

logging.warning(
'time by exif (%s) not found tags count: %s'
% (fullname, len(metadata))
)
logging.warning('time by exif (%s) not found tags count: %s', fullname, len(metadata))
for tag, val in metadata.items():
logging.debug('%s: %s' % (tag, val))
return None
logging.debug('%s: %s', tag, val)
except Exception as ex:
logging.warning('time by exif (%s) exception: %s' % (fullname, ex))
logging.warning('time by exif (%s) exception: %s', fullname, ex)
return None

def __time_by_attr(self, fullname):
try:
return datetime.datetime.fromtimestamp(
time.mktime(time.localtime(os.stat(fullname)[stat.ST_MTIME]))
)
except (FileNotFoundError, KeyError) as ex:
logging.warning('time by attr (%s) exception: %s' % (fullname, ex))
logging.warning('time by attr (%s) exception: %s', fullname, ex)
return None

def __calc_orig_name(self, fname):
if not int(self.__config['main']['add_orig_name']):
return ''
for exp, fs in self.DATE_REGEX:
for exp, _ in self.DATE_REGEX:
mat = exp.findall(fname)
if len(mat):
return ''
return '_' + self.SPACE_REGEX.sub('_', fname)

def _out_name_full(self, path, out_name, ext):
def out_name_full(self, path, out_name, ext):
res = os.path.join(path, out_name) + ext

i = 1
Expand Down Expand Up @@ -206,11 +203,11 @@ def get(self, fullname):
return FilePropRes(self, tp, ftime, path, ext, out_name, ok)


class FilePropRes(object):
def __init__(self, prop_ptr, tp, time, path, ext, out_name, ok):
class FilePropRes:
def __init__(self, prop_ptr, tp, ftime, path, ext, out_name, ok):
self.__prop_ptr = prop_ptr
self.__type = tp
self.__time = time
self.__time = ftime
self.__path = path
self.__ext = ext
self.__out_name = out_name
Expand Down Expand Up @@ -238,9 +235,7 @@ def out_name_full(self, path=None):
if path is None:
path = self.__path

return self.__prop_ptr._out_name_full(
path, self.__out_name, self.__ext
)
return self.__prop_ptr.out_name_full(path, self.__out_name, self.__ext)


if __name__ == '__main__':
Expand All @@ -251,7 +246,7 @@ def out_name_full(self, path=None):
from photo_importer import log
from photo_importer import config

log.initLogger(None, logging.DEBUG)
log.init_logger(None, logging.DEBUG)

fp = FileProp(config.Config()).get(sys.argv[1])
print(fp.type(), fp.time(), fp.ok())
5 changes: 3 additions & 2 deletions photo_importer/fileprop_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/python3
# pylint: disable=too-many-public-methods

import unittest
import datetime

from . import config
from . import fileprop
from photo_importer import config
from photo_importer import fileprop


class TestFileProp(unittest.TestCase):
Expand Down
Loading
Loading