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

Rename to allow all exiv2 tags as placeholders #2791

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions app/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType
the file to.
@return 0 if successful, -1 if the file was skipped, 1 on error.
*/
int renameFile(std::string& path, const tm* tm);
int renameFile(std::string& path, const tm* tm, Exiv2::ExifData& exifData);

/*!
@brief Make a file path from the current file path, destination
Expand Down Expand Up @@ -652,7 +652,7 @@ int Rename::run(const std::string& path) {
std::cout << _("Updating timestamp to") << " " << v << std::endl;
}
} else {
rc = renameFile(newPath, &tm);
rc = renameFile(newPath, &tm, exifData);
if (rc == -1)
return 0; // skip
}
Expand Down Expand Up @@ -1821,7 +1821,7 @@ void replace(std::string& text, const std::string& searchText, const std::string
}
}

int renameFile(std::string& newPath, const tm* tm) {
int renameFile(std::string& newPath, const tm* tm, Exiv2::ExifData& exifData) {
auto p = fs::path(newPath);
std::string path = newPath;
auto oldFsPath = fs::path(path);
Expand All @@ -1844,6 +1844,41 @@ int renameFile(std::string& newPath, const tm* tm) {
}

newPath = (p.parent_path() / (basename + p.extension().string())).string();

// rename using exiv2 tags
// is done after calling setting date/time: the value retrieved from tag might include something like %Y, which then
// should not be replaced by year
std::regex format_regex(":{1}?(Exif\\..*?):{1}?");
#if defined(_WIN32)
std::string illegalChars = "\\/:*?\"<>|";
#else
std::string illegalChars = "/:";
#endif
std::regex_token_iterator<std::string::iterator> rend;
std::regex_token_iterator<std::string::iterator> token(format.begin(), format.end(), format_regex);
while (token != rend) {
std::string tag = token->str().substr(1, token->str().length() - 2);
const auto key = exifData.findKey(Exiv2::ExifKey(tag));
std::string val = "";
if (key != exifData.end()) {
val = key->print(&exifData);
if (val.length() == 0) {
std::cerr << path << ": " << _("Warning: ") << tag << _(" is empty.") << std::endl;
} else {
// replace characters invalid in file name
for (std::string::iterator it = val.begin(); it < val.end(); ++it) {
bool found = illegalChars.find(*it) != std::string::npos;
if (found) {
*it = '_';
}
}
}
} else {
std::cerr << path << ": " << _("Warning: ") << tag << _(" is not included.") << std::endl;
}
replace(newPath, *token++, val);
}

p = fs::path(newPath);

if (p.parent_path() == oldFsPath.parent_path() && p.filename() == oldFsPath.filename()) {
Expand Down
208 changes: 208 additions & 0 deletions tests/bash_tests/test_rename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# -*- coding: utf-8 -*-

import sys
from system_tests import CaseMeta, CopyFiles, CopyTmpFiles, DeleteFiles, path

###########################################################
# rename with different formats
###########################################################

infile ="_DSC8437.exv"
outfile = "_DSC8437_02_Sep_2018.exv"
renformat = ":basename:_%d_%b_%Y"

@CopyTmpFiles("$data_path/" + infile)
@DeleteFiles("$tmp_path/" + outfile)
class Rename_dbY(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
outfilename = path("$tmp_path/" + outfile)
commands = [
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"""File 1/1: $infilename
Renaming file to $outfilename
"""
]
stderr = [""] * len(commands)
retval = [0] * len(commands)

###########################################################

infile ="_DSC8437.exv"
outfile = "_DSC8437_2018-09-02-19-40.exv"
renformat = ":basename:_%Y-%m-%d-%H-%M"

@CopyTmpFiles("$data_path/" + infile)
@DeleteFiles("$tmp_path/" + outfile)
class Rename_YmdHM(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
outfilename = path("$tmp_path/" + outfile)
commands = [
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"""File 1/1: $infilename
Renaming file to $outfilename
"""
]
stderr = [""] * len(commands)
retval = [0] * len(commands)

###########################################################

infile ="_DSC8437.exv"
outfile = "_DSC8437_NIKON D850_46.0 mm.exv"
renformat = ":basename:_:Exif.Image.Model:_:Exif.Photo.FocalLengthIn35mmFilm:"

@CopyTmpFiles("$data_path/" + infile)
@DeleteFiles("$tmp_path/" + outfile)
class Rename_ExifTags(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
outfilename = path("$tmp_path/" + outfile)
commands = [
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"""File 1/1: $infilename
Renaming file to $outfilename
"""
]
stderr = [""] * len(commands)
retval = [0] * len(commands)

###########################################################

infile ="_DSC8437.exv"
if sys.platform == 'win32':
outfile = "_DSC8437_a_b_c_d_e_f_g_h_i.exv"
else:
outfile = "_DSC8437_a\\b_c_d*e?f<g>h|i.exv"

renformat = ":basename:_:Exif.Image.ImageDescription:"

@CopyTmpFiles("$data_path/" + infile)
@DeleteFiles("$tmp_path/" + outfile)
class Rename_ExifTagsInvalidChar(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
outfilename = path("$tmp_path/" + outfile)
commands = [
"""$exiv2 -M"set Exif.Image.ImageDescription Ascii a\\b/c:d*e?f<g>h|i" $infilename""",
"$exiv2 --grep Exif.Image.ImageDescription $infilename",
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"",
"""Exif.Image.ImageDescription Ascii 18 a\\b/c:d*e?f<g>h|i
""",
"""File 1/1: $infilename
Renaming file to $outfilename
"""
]
stderr = [""] * len(commands)
retval = [0] * len(commands)

###########################################################
# rename with keeping suffix
###########################################################

basename ="_DSC8437"
outfile = "02_Sep_2018.PANO.exv"
renformat = "%d_%b_%Y:basesuffix:"

@CopyTmpFiles("$data_path/_DSC8437.exv")
@DeleteFiles("$tmp_path/" + outfile)
class Rename_basesuffix(metaclass=CaseMeta):
infilename1 = path("$tmp_path/" + basename + ".exv")
infilename2 = path("$tmp_path/" + basename + ".PANO.exv")
outfilename = path("$tmp_path/" + outfile)
commands = [
# first command to prepare a file name with suffix
"$exiv2 --verbose --rename :basename:.PANO " + infilename1,
"$exiv2 --verbose --rename " + renformat + " " + infilename2
]
stdout = [
"""File 1/1: $infilename1
Renaming file to $infilename2
""",
"""File 1/1: $infilename2
Renaming file to $outfilename
"""
]
stderr = [""] * len(commands)
retval = [0] * len(commands)

###########################################################
# rename error: tag is not included
###########################################################

infile ="_DSC8437.exv"
outfile = "_DSC8437_.exv"
renformat = ":basename:_:Exif.Image.ImageDescription:"

@CopyTmpFiles("$data_path/" + infile)
@DeleteFiles("$tmp_path/" + outfile)
class Rename_TagNotIncluded(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
outfilename = path("$tmp_path/" + outfile)
commands = [
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"""File 1/1: $infilename
Renaming file to $outfilename
"""
]
stderr = ["""$infilename: Warning: Exif.Image.ImageDescription is not included.
"""]
retval = [0] * len(commands)

###########################################################
# rename error: invalid tag name
###########################################################

infile ="_DSC8437.exv"
renformat = ":basename:_:Exif.Image.ImageDescript:"

@CopyTmpFiles("$data_path/" + infile)
class Rename_InvalidTagName(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
commands = [
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"""File 1/1: $infilename
"""
]
stderr = ["""Exiv2 exception in rename action for file $infilename:
Invalid tag name or ifdId `ImageDescript', ifdId 1
"""]
retval = [1] * len(commands)

###########################################################
# rename error: file contains no Exif data
###########################################################

infile ="_DSC8437.exv"
outfile = "_DSC8437_.exv"
renformat = ":basename:_:Exif.Image.ImageDescription:"

@CopyTmpFiles("$data_path/" + infile)
#@DeleteFiles("$tmp_path/" + outfile)
class Rename_NoExifData(metaclass=CaseMeta):
infilename = path("$tmp_path/" + infile)
outfilename = path("$tmp_path/" + outfile)
commands = [
"$exiv2 --delete a " + infilename,
"$exiv2 --verbose --rename " + renformat + " " + infilename
]
stdout = [
"",
"""File 1/1: $infilename
"""
]
stderr = ["",
"""$infilename: No Exif data found in the file
"""]
retval = [0, 253]

Loading