1# -*- coding: utf-8 -*- 2"""Rename file using metadata.""" 3 4import logging 5import re 6import string 7import sys 8 9from isbnlib.dev._bouth23 import b2u3, u 10from isbnlib.dev.helpers import File, cutoff_tokens, cwdfiles, last_first 11 12from ....app import config, ean13, get_isbnlike, meta 13 14LOGGER = logging.getLogger(__name__) 15 16DEFAULT_PATT = '{firstAuthorLastName}{year}_{title}_{isbn}' 17PATTERN = config.options.get('REN_FORMAT', DEFAULT_PATT) 18CUTOFF = 65 19 20 21def checkpattern(pattern): 22 """Check the validity of pattern for renaming a file.""" 23 placeholders = ( 24 '{authorsFullNames}', 25 '{authorsLastNames}', 26 '{firstAuthorLastName}', 27 '{year}', 28 '{publisher}', 29 '{title}', 30 '{isbn}', 31 '{language}', 32 ) 33 tocheck = pattern[:] 34 35 placeholderfound = False 36 for placeholder in placeholders: 37 if placeholder in tocheck: 38 tocheck = tocheck.replace(placeholder, '') 39 placeholderfound = True 40 if not placeholderfound or '{' in tocheck: 41 LOGGER.warning('Not valid pattern %s', pattern) 42 return False 43 44 validchars = '-_.,() {0}{1}'.format(string.ascii_letters, string.digits) 45 46 for char in tocheck: 47 if char not in validchars: 48 LOGGER.warning('Invalid character in pattern (%s)', char) 49 return False 50 return True 51 52 53PATTERN = PATTERN if checkpattern(PATTERN) else DEFAULT_PATT 54 55 56def get_isbn(filename): 57 """Extract the ISBN from file's name.""" 58 isbnlikes = get_isbnlike(filename, level='normal') 59 eans = [ean13(isbnlike) for isbnlike in isbnlikes] if isbnlikes else None 60 isbn = eans[0] if eans else None 61 if not isbn: # pragma: no cover 62 LOGGER.warning('No ISBN found in name of file %s', filename) 63 sys.stderr.write('no ISBN found in name of file %s \n' % filename) 64 return None 65 return isbn 66 67 68def cleannewname(newname): 69 """Clean and Strip '._!? ' from newname.""" 70 regex1 = re.compile(r'[!?/\\]') 71 regex2 = re.compile(r'\s\s+') 72 newname = regex1.sub(' ', newname) 73 newname = regex2.sub(' ', newname) 74 return newname.strip('_., ') 75 76 77def newfilename(metadata, pattern=PATTERN): 78 """Return a new file name created from book metadata.""" 79 pattern = pattern if pattern else PATTERN 80 for key in metadata.keys(): 81 if not metadata[key]: 82 metadata[key] = u('UNKNOWN') 83 84 d = { 85 'authorsFullNames': ','.join(metadata['Authors']), 86 'year': metadata['Year'], 87 'publisher': metadata['Publisher'], 88 'title': metadata['Title'], 89 'language': metadata['Language'], 90 'isbn': metadata['ISBN-13'], 91 } 92 93 if d['title'] == u('UNKNOWN') or d['isbn'] == u('UNKNOWN'): 94 LOGGER.critical('Not enough metadata') 95 return None 96 # Drop subtitle from title (if available) 97 d['title'] = cleannewname(d['title']).split(' - ')[0] 98 cutoff = min(len(d['title']), CUTOFF) 99 d['title'] = ' '.join(cutoff_tokens(d['title'].split(' '), cutoff)) 100 101 authorslastnames = [ 102 last_first(authorname)['last'] for authorname in metadata['Authors'] 103 ] 104 d['authorsLastNames'] = ','.join(authorslastnames) 105 d['firstAuthorLastName'] = authorslastnames[0] 106 107 try: 108 formatted = u(pattern).format(**d) 109 return cleannewname(formatted) 110 except KeyError as e: 111 LOGGER.warning('Error with placeholder: %s', e) 112 return None 113 114 115def renfile(filename, isbn, service, pattern=PATTERN): 116 """Rename file with associate ISBN.""" 117 service = service if service else 'default' 118 metadata = meta(isbn, service) 119 if not metadata: # pragma: no cover 120 LOGGER.warning('No metadata for %s', filename) 121 sys.stderr.write('No metadata for %s\n' % filename) 122 return None 123 newname = newfilename(metadata, pattern) 124 if not newname: # pragma: no cover 125 LOGGER.warning('%s NOT renamed!', filename) 126 sys.stderr.write('%s NOT renamed \n' % filename) 127 return None 128 oldfile = File(filename) 129 ext = oldfile.ext 130 newbasename = b2u3(newname + ext) 131 oldbasename = oldfile.basename 132 if oldfile.mkwinsafe(newbasename) == oldbasename: # pragma: no cover 133 return True 134 success = oldfile.baserename(newbasename) 135 if success: 136 try: # pragma: no cover 137 sys.stdout.write('%s renamed to %s \n' % 138 (oldbasename, oldfile.basename)) 139 except Exception as e: # pragma: no cover 140 LOGGER.info('Error when writing for stdout: %s', e) 141 return True 142 return None # pragma: no cover 143 144 145def rencwdfiles(fnpatt='*', service='default', pattern=PATTERN): 146 """Rename cwd files with a ISBN in their filenames and within fnpatt.""" 147 files = [(get_isbn(f), f) for f in cwdfiles(fnpatt) if get_isbn(f)] 148 for isbn, f in files: 149 renfile(f, isbn, service, pattern) 150 return True 151