- PVSM.RU - https://www.pvsm.ru -
Наверное каждому электрочитателю хотелось бы всю свою коллекцию книг содержать прямо на электронной книге-читалке, и при этом, не смотря на общую тормознутость устройства, иметь удобную навигацию.
Зачастую в электронной книге проблематично содержать сотни и тысячи книжек: либо аппарат долго тупит, считывая информацию о каждой книге из ее внутренностей, либо вручную поддерживать коллекцию с разбивкой по каталогам — тот еще геморрой.
Помню, в ранних редакциях электрокниг от Sony была такая проблема: закачиваешь туда несколько сотен книжек, и аппарат при включении долго висит, составляя список закачанного. Тогда не было еще поддержки коллекций и Сонька долго бегала по всей карточке, собирая информацию о закачанных книжках. Многие жаловались.
Более удобна была работа с книжками от PocketBook — в них поддерживалась навигация по файловой системе, поэтому можно было вручную раскидать книги по папкам, и считывал аппарат только книги в той папке, в которую мы зашли.
Вручную с компа уже можно было что-то формировать и как-то жить.
Тогда же меня познакомили с ЛибРусЕк'ом(теперь уже более актуальна Флибуста, кто не знает, что это, вам сюда [1]). И в какой-то момент мне пришла идея, а не попробовать ли загнать всю библиотеку в электрокнижку, слегка автоматизировав этот процесс?
А теперь важное! Чтобы не тратить зря внимание людей, которым данная статья вероятно помочь ничем не сможет(к сожалению такие будут), дам фильтрующую установку:
Если вы являетесь владельцем электронной читалки PocketBook, и вас заинтересовала идея загнать в аппарат как можно больше книг с удобной навигацией — вам сюда однозначно. Если вы владелец другой электронной книги, которая тем не менее поддерживает формат FB2 и навигацию по директориям, вам вероятно тоже сюда, смотрите ближе к концу статьи описание настройки $no_leased_storage. Остальным, к сожалению, данная статья мало чем сможет помочь. Извините.
Немного поковырявшись в функционале PocketBook'а, выяснил одну интересную особенность: он поддерживает файлы-ссылки. Что-то вроде ярлыков в Windows или симлинков в юниксах. Я позже объясню, зачем они нам нужны и в чем прелесть этой фичи, а сейчас скажу лишь, что используются они в покетбуке штатно для функционирования раздела «Избранное». Когда вы посмещаете туда книгу, в специальную папку на устройстве помещается лишь ссылка на реальный файл книжки. Сама книжка в избранное не копируется.
Так же, пообщавшись с разработчиками, было установлено, что на тот момент уже поддерживались SDHC емкостью вплоть до 32Gb. В общем-то для современных моделей, которые со слотом microSD, в ТТХ указаны те же 32Gb, что несколько разочаровывает. Но не сильно. Да и проверить надо, спецификации спецификациями, а реально поддерживать может и больше. Только карточку на 64Gb только для негарантированной проверки влом покупать.
Ну и еще есть замечательная фича — поддержка .fb2.zip, это когда каждая книжка формата fb2 упакована в свой zip-архив. PocketBook такие книги видит прозрачно, то есть так же, как и неупакованные.
Сразу стоит пояснить один момент, упреждая необдуманные вопросы: никто не собирается читать сотни тысяч книг(а речь идет о таких количествах). Держать такую коллекцию на устройстве в надежде все это когда-либо прочесть — просто безумие.
Удобство такого каталогизированного объема вовсе в другом. Например, кто-то вам советует почитать конкретную книгу, вы глядь, а у вас это уже есть. Или конкретного автора например. Вы сразу находите это в своей книге, и делаете закладку в избранное. Да-да, директорию автора тоже можно положить в избранное, ссылки на директории в Покетбуках тоже работают.
Типа «Почитай Гаррисона», «ОК, почитаю», и сразу его шмяк в избранное. Иначе ведь забудется, не в блокноты же записывать.
Так вот, перейдем к делу. Был написан скрипт на PHP, который берет из исходной папки файлы fb2, включая заархивированные пачками в zip-архивы, и создает основанную на файловой системе коллекцию книг, предназначенную для заливки в PocketBook.
Создает он ее хитро. Тут нужно отдельно сказать о нюансах формата fb2.
Во-первых, книга свое название(а так же информацию об авторах, жанрах и сериях) хранит внутри себя. Имя файла здесь не важно в принципе.
Во-вторых, книга может быть написана несколькими авторами, относиться к нескольким жанрам и входить в несколько серий. А поиск и каталогизацию хотелось бы иметь и по авторам, и по жанрам и по сериям. При чем, если написана двумя авторами, хотелось бы, чтобы книжка присутствовала в каталогах обоих.
Копировать в каждую соответствующую директорию книгу? Некомильфо.
И вот тут нам на помощь приходят файлы-ссылки.
Скрипт помещает тела книг(в zip'ах) в отдельную директорию, создавая внутри вложенные поддиректории и раскидывая их таким образом, чтобы в конечной директории было не более сотни книг, да и в промежуточных директориях — не более сотни поддиректорий. Это чтобы книжка не тупила при доступе к конкретному zip'у, перебирая много файлов в одной директории.
Рядом, например в корне флешки, создается директория каталогизатора, в которой внутри сделана вся необходимая навигация по авторам, сериям и жанрам, и которая в конечных нодах вместо книг содержит вышеописанные файлы-ссылки, ссылающиеся на соответствующие zip'ы. Умный покетбук на месте этих ссылок покажет сами книжки, и будет открывать их так же, как если бы они и лежали в каталогизаторе.
Для примера я покажу вам, как в итоге будут храниться произвольные 10 книг, во что они превратятся в файловой системе SD-карты:
/_zipstorage_/00000000/00000000/00000001.zip
/_zipstorage_/00000000/00000000/00000002.zip
/_zipstorage_/00000000/00000000/00000003.zip
/_zipstorage_/00000000/00000000/00000004.zip
/_zipstorage_/00000000/00000000/00000005.zip
/_zipstorage_/00000000/00000000/00000006.zip
/_zipstorage_/00000000/00000000/00000007.zip
/_zipstorage_/00000000/00000000/00000008.zip
/_zipstorage_/00000000/00000000/00000009.zip
/_zipstorage_/00000000/00000000/0000000a.zip
/Библиотека/Авторы/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000001.flk
/Библиотека/Авторы/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000002.flk
/Библиотека/Авторы/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000003.flk
/Библиотека/Авторы/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000004.flk
/Библиотека/Авторы/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000005.flk
/Библиотека/Авторы/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000006.flk
/Библиотека/Авторы/Буквы А-Я/Д/Дун/Дункан Дэйв/00000008.flk
/Библиотека/Авторы/Буквы А-Я/М/Мил/Милкова Елена/0000000a.flk
/Библиотека/Авторы/Буквы А-Я/П/Пал/Пальм Карл Магнус/00000007.flk
/Библиотека/Авторы/Буквы А-Я/С/Сем/Семенова Мария Васильевна/00000009.flk
/Библиотека/Жанры_Авторы/Деловая литература/Искусство и Дизайн/Буквы А-Я/П/Пал/Пальм Карл Магнус/00000007.flk
/Библиотека/Жанры_Авторы/Детективы и Триллеры/Детектив/Буквы А-Я/М/Мил/Милкова Елена/0000000a.flk
/Библиотека/Жанры_Авторы/Поэзия, Драматургия/Поэзия/Буквы А-Я/С/Сем/Семенова Мария Васильевна/00000009.flk
/Библиотека/Жанры_Авторы/Проза/Современная проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000001.flk
/Библиотека/Жанры_Авторы/Проза/Современная проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000002.flk
/Библиотека/Жанры_Авторы/Проза/Современная проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000003.flk
/Библиотека/Жанры_Авторы/Проза/Современная проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000004.flk
/Библиотека/Жанры_Авторы/Проза/Современная проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000005.flk
/Библиотека/Жанры_Авторы/Проза/Современная проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000006.flk
/Библиотека/Жанры_Авторы/Фантастика/Фэнтези/Буквы А-Я/Д/Дун/Дункан Дэйв/00000008.flk
/Библиотека/Жанры_Авторы/Юмор/Юмористическая проза/Буквы А-Я/Д/Дро/Дрозд Володимир Григорович/00000001.flk
/Библиотека/Жанры_Серии/Деловая литература/Искусство и Дизайн/Буквы А-Я/А/Амфора/00000007.flk
/Библиотека/Жанры_Серии/Деловая литература/Искусство и Дизайн/Весь список/Амфора/00000007.flk
/Библиотека/Жанры_Серии/Детективы и Триллеры/Детектив/Буквы А-Я/Э/Эгида/0000000a.flk
/Библиотека/Жанры_Серии/Детективы и Триллеры/Детектив/Весь список/Эгида/0000000a.flk
/Библиотека/Серии/Буквы А-Я/А/Амфора/00000007.flk
/Библиотека/Серии/Буквы А-Я/Д/Дискография/00000007.flk
/Библиотека/Серии/Буквы А-Я/Э/Эгида/0000000a.flk
/Библиотека/Серии/Весь список/Амфора/00000007.flk
/Библиотека/Серии/Весь список/Дискография/00000007.flk
/Библиотека/Серии/Весь список/Эгида/0000000a.flk
Внутри каждого zip-файла в директории "_zipstorage_" лежит книга в формате .fb2 с тем же именем, что и у zip-файла.
Внутри каждого flk-файла находится путь к соответствующему zip-файлу с книгой.
На месте файлов .flk в устройстве отображаются соответствующие книжки, и ведут себя они точно так же, как если бы они и лежали вместо .flk файлов. То есть никаких «00000007.flk», «00000007.zip» и «00000007.fb2» пользователь электрокнижки не увидит.
Как видите, без ссылок .flk, если бы нам пришлось копировать каждый файл туда, где он должен присутствовать, наших 10 файлов превратились бы уже 31 файлик с теми же размерами на флешке.
Но, благодаря использованию этой феньки со ссылками, этого не произошло.
Вам в директорию "_zipstorage_" ходить не надо совсем — это хранилище.
Ходить нужно в каталогизатор под названием «Библиотека».
Там вам выпишут карточку посетителя, по вашему запросу библиотекарь сам пойдет в хранилище и принесет вам оттуда…
Вам может быть не сразу понятно, почему внутри этой библиотеки добираться до книжки «Кубик из красной пластмассы» нужно по такому длинному пути: "/Авторы/Буквы А-Я/С/Сем/Семенова Мария Васильевна".
Но я напомню, что речь идет не о десятке файлов, а о сотнях тысяч. Гораздо быстрее будет войти последовательно в директории «Буквы А-Я», «С», «Сем» и найти там «Семенову Марию Васильевну», чем долго-долго ждать отображения, а потом долго-долго листать полный список авторов, пока доберемся до буквы «С».
Точно так же, если нужно найти все книги Гарри Гаррисона, можно вполне логично предположить, что путь будет "/Авторы/Буквы А-Я/Г/Гар/Гаррисон Гарри". Так как к примеру на букву «Г» авторов дофигища, пришлось ввести дополнительный уровень директорий с первыми тремя буквами фамилии. Это кстати рулится в скрипте «process.php» параметрами к вызовам функции get_splitten_dirs().
Кроме всего прочего, вам не нужно каждый раз ходить в «библиотеку», чтобы начать чтение очередной книжки Гаррисона — вам нужно туда добраться только один раз. А дальше, как я уже писал выше, кидаем всего Гаррисона в избранное и имеем всегда под рукой. Это действительно удобно.
И я этим всем пользуюсь почти «в одно лицо»(не считая нескольких друзей, которые брали у меня уже готовую «заливку», либо просто дублировали мою SD-карту) уже года три, или около того. Непорядок, наверное, пришло время делиться.
Ладно, думаю, о теории достаточно. Кто в теме, поймут все сразу, в т.ч. где на флибусте брать нужное «сырье», а кто не в теме и кому не интересно, вряд ли дочитают до этого места.
Так что перейдем к описанию самого скрипта, ссылку на zip-архив с которым вы найдете в конце статьи.
Я не буду описывать проблемы и решения, которые пришлось пережить. Это тема для отдельной статьи, вероятно в разделе о PHP.
Опишу только общую схему работы скрипта и его настройки.
Кроме вычитки исходной директории и формирования каталогизатора, скрипт так же пропускает каждый fb2-файл сквозь себя. То есть разбирает структуру и пересохраняет файл заново — парсит.
Это нужно для исправления ошибок формата. Некоторые файлы изначально приходят битыми — то недопустимый символ не переведен в HTML-код, то левые теги встречаются, которые содраны с интернет-страничек, но недопустимы в fb2-формате, то незакрытые теги, то еще что-то. Не то, чтобы этих ошибок так уж много, но они встречаются относительно регулярно, и электрокнижка такие файлы открыть не может.
Так же скрипт переводит жанры в более читаемые названия.
Итак. Когда вы скачаете zip-архив, в нем вы обнаружите несколько php-файлов и в поддиректориях несколько книжек для примера.
Есть директория «out_dirsrc», из которых будем читать книжки, в т.ч. в зипах.
И есть директория «out_dirdest», в которую будем складывать каталогизатор и хранилище. Собственно после работы скрипта все, что будет находиться в этой директории, нужно перенести в корень SD-карты.
Ну либо можно скрипт настроить сразу, чтобы писал прямо на флешку.
Тут надо оговориться о паре нюансов касательно записи на SD-карты. Приготовьтесь сразу к тому, что процесс этот не быстрый, учитывая количество информации и фрагментацию.
Скрипт писался для машины на Windows, и соответственно PHP был установлен под Windows, версия 5.2.9-2. По идее ему пофиг на какой системе работать, но может у кого-то что-то не завестись.
Именно из-за Windows в арихиве пристутсвуют .bat-файлы для запуска скриптов.
Здесь представлена обрезанная версия скрипта. Полная состоит из двух частей и базы данных под Interbase, в которую сначала все книги импортируются, там фильтруются, убираются дубляжи, исправляются разночтения авторов, жанров, серий и названий(типа «Пушкин А.С.»=>«Пушкин Александр Сергеевич»), собираются хеши MD5 для дальнейших пополнений библиотеки и т.п.
Сюда не вижу смысла это все вываливать, ибо одно дело поднять PHP для выполнения скрипта, другое — настроить базу данных, установить все библиотеки и т.п. Врядли многие из вас будут с этим заморачиваться. Проще каждому для себя допилить свой функционал БД к скрипту, если понадобится. Поэтому скрипт обрезан для автономной работы без всяких БД. Чуть меньше функционала, но не критично.
Рядом со скриптом во время его работы будет создан лог-файл с ошибками(«prc_errors.txt»). Это задано в bat-файле запуска — перенаправление STDERR в этот файл. Если ошибок не было, он будет нулевого размера.
Собственно основной скрипт находится в файле «process.php», его и надо запускать.
Настройки хранятся в нем же в начале. Вот они:
$out_file='./out.txt'; // в этот лог сохраняется информация об обработанных файлах. можно установить в false и лог писаться не будет.
$src_dir='./out_dir/src'; // исходная директория. это то, куда кидать все скачанное с интернетов.
$dest_dir='./out_dir/dest'; // целевая директория. это то, откуда забирать потом все на SD-карту.
$storagename='_zipstorage_'; // название для директории-хранилища. оно будет прописано в каждый flk-файл, эта директория должна в итоге находиться в корне SD-карты.
$libname='Библиотека'; // название для директории-каталогизатора
$compress_storage=true; // зиповать ли файлы в хранилище. если нет, будут валяться как fb2. не знаю, зачем это может понадобиться. я зипую по-умолчанию.
$control_genres_export=false; // ограничивать ли список жанров по флагам(см. genres.inc)
$no_leased_storage=false; // не использовать выделенное хранилище и файлы-ссылки.
$dbid=0; // переменная для инкремента ID книги, который используется как техническое имя файла в хранилище. ну можно не с нуля начать )))
$GLOBALS['process_config']=array(
'struct_only'=>false, // только для отладки
'unknown_tags_processing'=>XMLP_UT_CUT, // способ обработки неизвестных тегов(невалидных для fb2 например, см. 'tags_hash')
'strip_comments'=>false, // вырезать комментарии <!-- -->
'tags_hash'=>$GLOBALS['XMLP_FB2_elements'], // набор валидных тегов
'tags_processing'=>array( // параметры генерации регулярки для тега
'name_first_alpha'=>false,
'name_len'=>20,
'content_len'=>512,
'pattern_type'=>XMLP_TPS_STRICT,
'include_comments'=>true,
),
);
Опишу параметр $GLOBALS['process_config']['unknown_tags_processing']. Он рулит парсером, а точнее тем, что парсер будет делать с обнаруженными тегами, которые не являются валидными для формата fb2(FictionBook 2.0):
Очевидно для исправления ошибок наиболее подходят два варианта: XMLP_UT_CONVERT и XMLP_UT_CUT. Так как в большинстве случаев пользователя не интересуют эти теги вообще, являются мусором, то наиболее правильным на мой взгляд их просто вырезать с помощью опции XMLP_UT_CUT.
Сами теги формата FictionBook передаются парсеру в параметре $GLOBALS['process_config']['tags_hash']. Массив $GLOBALS['XMLP_FB2_elements'] предопределен в начале файла «xmlp.inc».
Параметр $GLOBALS['process_config']['tags_processing'] рулит генерацией регулярки восприятия тегов в тексте. Там лучше лишнего не трогать, по-умолчанию нормально. Можно разве что параметр 'name_first_alpha' установить в true — в таком случае движок будет требовать альфа-символ в начале имени тега, и к примеру вот это <:> будет восприниматься не как неизвестный тег, а как мусор в тексте, со всеми вытекающими — при опции XMLP_UT_CUT такие теги не будут вырезаться, а будут превращены в html entities и будут отображаться конечному читателю книги.
Если же здесь же отключить опцию 'include_comments', будет генериться более простая регулярка, в результате производительность скрипта возрастет раза в два как минимум, но при этом в файлах книг комментарии в тегах <!-- --> превратятся в обфусцированную кашу и станут видны конечному читателю книги. Если комментарий попадет в зону текста книги — оно еще ничего, но если в зону заголовка — книжка вероятно вообще не откроется на устройстве, т.к. это будет нарушение XML-структуры документа(произвольный текст между XML-тегами).
Настройка $no_leased_storage. Если установить в true, отдельное хранилище для книг использоваться не будет. То есть никакого '_zipstorage_' и flk-файлов. На место flk-файлов в библиотеке будут ложиться fb2 или zip(в зависимости от параметра $compress_storage). Это будет жрать больше места на SD-карте за счет нескольких копий одной и той же книги(см. описание проблемы выше), но зато позволит владельцам других электрокнижек(не PocketBook), если их книжка поддерживает формат fb2 и навигацию по директориям на флешке, использовать этот же скрипт для автоматической каталогизации своих библиотек.
Еще одна настройка $control_genres_export. Ее правила находятся в файле «genres.inc».
Там находится массив расшифровок жанров, выглядит он так:
''=>array('_Без жанра_','_Без жанра_', true),
'biography'=>array('Биографии','Биография', true),
'biogr_historical'=>array('Биографии исторических личностей','Биография', true),
'biogr_sports'=>array('Биографии спортсменов','Биография', true),
'biogr_arts'=>array('Люди искусства','Биография', true),
'banking'=>array('Банковское дело','Деловая литература', true),
'accounting'=>array('Бухучет, налогообложение, аудит','Деловая литература', true),
'design'=>array('Искусство и Дизайн','Деловая литература', true),
'org_behavior'=>array('Корпоративная культура','Деловая литература', true),
Видите, в конце каждого массива есть элемент со значением true? Так вот, при включенной опции $control_genres_export скрипт будет выливать только те жанры, напротив которых в этом последнем элементе стоит true. Книжки, в которых не прописан ни один жанр, помеченный в «genres.inc» как true, будут игнорироваться, их тела тоже не попадут на карту памяти.
Это очень удобно, если вы понимаете, что размер вашей коллекции больше, чем у вас есть места на SD-карте.
В таком случае вы просто редактируете список жанров, исключая из него ненужные вам. Это позволяет вылить библиотеку на карту частично, только с интересующими вас жанрами. Например вы любите фантастику, а поэзия, любовные романы и деловая литература вас не интересует в принципе.
Так же никто не мешает с помощью этого механизма разбить библиотеку по наборам жанров на несколько разных карт памяти.
В списке жанров есть еще один механизм — переназначение.
Ниже в том же файле(и в том же массиве) вы можете увидеть подобное:
'психология'=>array('sci_psychology','r', true),
'science_history_philosophy'=>array('sci_philosophy','r', true),
'языкознание'=>array('sci_linguistic','r', true),
'adv_history_avant'=>array('adv_history','r', true),
'приключения'=>array('adventure','r', true),
'историческая проза'=>array('prose_history','r', true),
Видите в предпоследнем значении метатега стоит сиротливая буква 'r'?
Для скрипта это означает, к примеру, что если в книжке обнаружен код жанра 'приключения', он должен смотреть на запись жанра с кодом 'adventure'(в этом же массиве). И книжку в каталогизаторе он отнесет в результате к правильному жанру. Хотя сам fb2-файл править не будет.
Не сделана каталогизация по собственно названиям книжек. Чесно говоря, я не представляю, как это будет выглядеть, и как сделать удобную разбивку директорий для быстрой навигации. По большому счету, если вы кроме названия ничего не знаете, можно по-быстрому хоть с телефона в инете пробить автора, и уже найти книгу на устройстве. Время, затраченное на поиск, против времени, затраченного на последующее чтение, ИМХО, вполне позволяют на этом не заморачиваться.
Не очень нормально сделано отображение прогресса. В исходной директории могут валяться как просто fb2-файлы, так и zip'ы с ними, а так же поддиректории с такими же наборами.
Для zip'файлов прогресс высчитывается наобум, для каждой поддиректории отображается свой прогресс. По хорошему надо бы в начале работы скрипта сканировать все что есть отдельным проходом и потом уже показывать общий прогресс выполнения.
Основательно перелопачен только модуль парсера «xmlp.inc». В остальных исходниках есть некоторый бардак с именами переменных, есть и лишние переменные.
Ну и еще по-мелочи разные некритичные штуки не сделаны.
До обработки:
<p><:>В следующий раз <tag>Якуб</tag> пошел на исповедь <одиннадцать лет спустя>, и </b>это> совершенно <другая история.<:></p>
<p>— Вот те на! — выразил <a>свое изумление Якуб.</p>
<p>Зубами он оторвал уголок </a>конверта и достал плотно сложенную бумагу. Развернул.</p>
После(настройка XMLP_UT_CONVERT):
<p><:>В следующий раз <tag>Якуб</tag> пошел на исповедь <одиннадцать лет спустя>, и </b>это> совершенно <другая история.<:></p>
<p>— Вот те на! — выразил <a>свое изумление Якуб.</a></p>
<p>Зубами он оторвал уголок конверта и достал плотно сложенную бумагу. Развернул.</p>
После(настройка XMLP_UT_CUT):
<p>В следующий раз Якуб пошел на исповедь <одиннадцать лет спустя>, и это> совершенно <другая история.</p>
<p>— Вот те на! — выразил <a>свое изумление Якуб.</a></p>
<p>Зубами он оторвал уголок конверта и достал плотно сложенную бумагу. Развернул.</p>
А вот здесь [2].
P.S. Да, я знаю, что существуют десктопные программы, позволяющие хранить свою коллекцию. Но…
Во-первых, делалось это все, когда таких программ еще не было.
Во-вторых, они не позволят мне быстро сделать для себя то, что я могу сделать сам(допилить что-то в коде).
В третьих, вряд ли эти программы умеют править критические ошибки в книгах. Я в скрипте умышленно не использовал более быстрый парсинг XML с помощью встроенных в PHP Си-шных библиотек, ибо они падают на ошибках. Можно отключить вылет скрипта при ошибках, но нельзя нормально допарсить файл и автоматически его исправить.
Данный скрипт хоть и написан на «нативном» PHP, тем не менее оптимизирован по скорости обработки.
И много чего еще.
Кстати, никто не мешает же совместить и то и другое. Из десктопной программы отбираем нужные нам книги по условиям(там ведь в GIU гораздо удобнее условия всякие ставить и выливать все по фильтрам), а дальше скармливаем этому скрипту и он формирует каталог на карту памяти.
Автор: dsd_corp
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/7142
Ссылки в тексте:
[1] сюда: http://bit.ly/KBR6xy
[2] здесь: http://cs.loving.ru/to_habr/fb2p_release.zip
Нажмите здесь для печати.