]> git.phdru.name Git - m_librarian.git/commitdiff
Allow to set download format
authorOleg Broytman <phd@phdru.name>
Mon, 6 Jun 2016 19:42:13 +0000 (22:42 +0300)
committerOleg Broytman <phd@phdru.name>
Mon, 6 Jun 2016 20:52:25 +0000 (23:52 +0300)
docs-ru/command_line.rst
docs/command_line.rst
m_librarian/config.py
m_librarian/download.py
sample/m_librarian.conf
scripts/ml-search.py
tests/test_format.py [new file with mode: 0755]

index 417955838a061b4bd3f709d27b5d1a6e66e5d2ac..872ef5c9d1ebf16b02a51f960a5e9e69273b7a97 100644 (file)
@@ -116,8 +116,8 @@ ml-search.py
 При использовании опции `-v` также выводится id из БД.
 
 
-Поиск книг
-^^^^^^^^^^
+п÷п╬п╦я│п╨ п╦ п╥п╟пЁя─я┐п╥п╨п╟ п╨п╫п╦пЁ
+^^^^^^^^^^^^^^^^^^^^^
 
 Использование::
 
@@ -170,6 +170,30 @@ ml-search.py
 использовать эту команду — совместно с опцией `--id`. Файл сохраняется в
 текущую директорию с тем именем, под которым он храниться в библиотеке.
 
+Ключ `format` в секции `[download]` файла конфигурации m_librarian.conf
+
+|    [download]
+|    format = %a/%s/%n %t
+
+позволяет управлять именами директорий и именем файла, куда m_Librarian
+будет сохранять файлы. Формат по умолчанию `%f`, т.е. просто имя файла.
+Другие доступные спецификаторы::
+
+    %a - автор (один из, если их несколько)
+    %e - расширение имени файла
+    %f - имя файла
+    %G - жанр (один из, если их несколько), имя из БД
+    %g - жанр (один из, если их несколько), название
+    %l - язык
+    %n - номер в серии (или 0)
+    %s - серия
+    %t - название
+
+Формат не должен заканчиваться на разделитель директорий (`/` или `\\`).
+Если спецификатор `%e` (расширение) не найден в формате, он добавляется
+в конец с точкой в качестве разделителя. Т.о. формат `%f` эквивалентен
+формату `%f.%e`.
+
 
 Поиск расширений
 ^^^^^^^^^^^^^^^^
index f9911a8f5f258aca274e080b7964d69165a218b1..ef27723649b84b110a3a7086f44493ae00edee23 100644 (file)
@@ -113,8 +113,8 @@ name starts with "mack", case insensitive.
 With one option `-v` it also prints database id.
 
 
-Book search
-^^^^^^^^^^^
+Book searching and downloading
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Usage::
 
@@ -167,6 +167,29 @@ file. The option allows to download exactly one book. The simplest way
 to use it is via option `--id`. The file is downloaded into the current
 directory with the name from the library.
 
+Configuration key
+
+|    [download]
+|    format = %a/%s/%n %t
+
+allows to set format for the download file pathname. Default format is
+`%f`, i.e. just filename. Other format specifiers are::
+
+    %a - author (one of if many)
+    %e - file extension
+    %f - file name in archive
+    %G - genre (one of if many), name
+    %g - genre (one of if many), title
+    %l - language
+    %n - series number (or 0)
+    %s - series
+    %t - title
+
+Format must not end in directory separator (`/` or `\\`). If specifier
+`%e` (extension) is not found in the format it is appended
+unconditionally with a dot. That is, format `%f` is equivalent to
+`%f.%e`.
+
 
 Extension search
 ^^^^^^^^^^^^^^^^
index d896bf58cdb3c3d3d3c1ba9326690b3a1ed84856..ca7eadd4fa95a168979c99672d3b6766ac6b19aa 100755 (executable)
@@ -1,7 +1,7 @@
 #! /usr/bin/env python
 
 import os
-from ConfigParser import SafeConfigParser
+from ConfigParser import RawConfigParser
 
 __all__ = ['get_config']
 
@@ -43,7 +43,7 @@ def get_config(config_filename=None):
     if _ml_config is None:
         if config_filename is None:
             config_filename = find_config_file()
-        _ml_config = SafeConfigParser()
+        _ml_config = RawConfigParser()
         _ml_config.read(config_filename)
     return _ml_config
 
index 361f7aa735549a783e80a0f4adcef6ae67464f43..a66e6925a6e160d4d4913ee89832bc030eaf6b42 100755 (executable)
@@ -9,22 +9,103 @@ from .config import get_config
 __all__ = ['download']
 
 
+_format = '%f'
+_compile_format = True
+_compiled_format = '%(file)s'
+
+
+def _do_compile_format():
+    global _format, _compile_format, _compiled_format
+    if _compile_format:
+        _compile_format = False
+        try:
+            _format = get_config().get('download', 'format')
+        except:
+            return
+    got_percent = False
+    compiled = []
+    for c in _format:
+        if c == '%':
+            if got_percent:
+                got_percent = False
+                compiled.append('%')
+            else:
+                got_percent = True
+        else:
+            if got_percent:
+                got_percent = False
+                if c == 'a':
+                    new_format = u'%(author)s'
+                elif c == 'e':
+                    new_format = u'%(extension)s'
+                elif c == 'f':
+                    new_format = u'%(file)s'
+                elif c == 'G':
+                    new_format = u'%(gname)s'
+                elif c == 'g':
+                    new_format = u'%(gtitle)s'
+                elif c == 'l':
+                    new_format = u'%(language)s'
+                elif c == 'n':
+                    new_format = u'%(ser_no)d'
+                elif c == 's':
+                    new_format = u'%(series)s'
+                elif c == 't':
+                    new_format = u'%(title)s'
+                else:
+                    raise ValueError('Bad format specifier "%%%c"' % c)
+                compiled.append(new_format)
+            else:
+                compiled.append(c)
+    _compiled_format = ''.join(compiled)
+
+
 _library_path = None
 
 
-def download(archive, filename, date, path=None):
+def download(book, path=None):
     if path is None:
         global _library_path
         if _library_path is None:
             _library_path = get_config().get('library', 'path')
         path = _library_path
 
-    zf = ZipFile(os.path.join(path, archive),  'r')
-    infile = zf.open(filename)
+    global _compiled_format
+    _do_compile_format()
+    if _compiled_format[-1] in ('\0', '\\', '/'):
+        raise ValueError('Bad format: "%s"' % _compiled_format)
+    bdict = {}
+    bdict['author'] = book.authors[0].fullname
+    bdict['extension'] = book.extension.name
+    bdict['file'] = book.file
+    genre = book.genres[0]
+    bdict['gname'] = genre.name
+    bdict['gtitle'] = genre.title
+    bdict['language'] = book.language.name
+    bdict['ser_no'] = book.ser_no or 0
+    bdict['series'] = book.series
+    bdict['title'] = book.title
+    if '%(extension)s' not in _compiled_format:
+        _compiled_format += '.%(extension)s'
+    filename = _compiled_format % bdict
+    try:
+        os.makedirs(os.path.dirname(filename))
+    except OSError:
+        pass  # Already exists
+    zf = ZipFile(os.path.join(path, book.archive),  'r')
+    infile = zf.open('%s.%s' % (book.file, book.extension.name))
     outfile = open(filename, 'wb')
     copyfileobj(infile, outfile)
     outfile.close()
     infile.close()
     zf.close()
-    dt = mktime(date.timetuple())
+    dt = mktime(book.date.timetuple())
     os.utime(filename, (dt, dt))
+
+
+def test():
+    _do_compile_format()
+    print _compiled_format
+
+if __name__ == '__main__':
+    test()
index e3863aafca6c3f22a3bff70de664fd395b1d8224..2ed0f8a39d601607257f1e0892f6bd6ba81d1ab5 100644 (file)
 
 [library]
 path = /var/lib/LRE_Flibusta
+
+[download]
+# Download formats:
+# %a - author (one of)
+# %e - file extension
+# %f - file name in archive
+# %G - genre (one of), name
+# %g - genre (one of), title
+# %l - language
+# %n - series number (or 0)
+# %s - series
+# %t - title
+# Examples:
+# format = %f (this is the default)
+# format = %a/%s/%n %t
index 0d995e505f1bd359a7b3b134e5861aa6c86fbcca..d19c5b4f6408b8ce4d629fe7913e2fd816cf63a2 100755 (executable)
@@ -144,8 +144,7 @@ def _search_books(case_sensitive, search_type, args):
                              "(found %d).\n" % count)
             sys.exit(1)
         book = books[0]
-        download(book.archive, '%s.%s' % (book.file, book.extension.name),
-                 book.date, args.path)
+        download(book, args.path)
         return
     count = 0
     for book in books:
diff --git a/tests/test_format.py b/tests/test_format.py
new file mode 100755 (executable)
index 0000000..9d52fb9
--- /dev/null
@@ -0,0 +1,18 @@
+#! /usr/bin/env python
+
+
+import unittest
+from tests import main
+from m_librarian import config, download
+
+
+class TestFormat(unittest.TestCase):
+    def test_compile_format(self):
+        config.get_config().set('download', 'format', '%a/%s/%n %t')
+        download._do_compile_format()
+        self.assertEqual(download._compiled_format,
+                         u'%(author)s/%(series)s/%(ser_no)d %(title)s')
+
+
+if __name__ == "__main__":
+    main()