GrabDuck

Электронная библиотека для PocketBook: автоматическая обработка

:

Наверное каждому электрочитателю хотелось бы всю свою коллекцию книг содержать прямо на электронной книге-читалке, и при этом, не смотря на общую тормознутость устройства, иметь удобную навигацию.
Зачастую в электронной книге проблематично содержать сотни и тысячи книжек: либо аппарат долго тупит, считывая информацию о каждой книге из ее внутренностей, либо вручную поддерживать коллекцию с разбивкой по каталогам — тот еще геморрой.

Преамбула


Помню, в ранних редакциях электрокниг от Sony была такая проблема: закачиваешь туда несколько сотен книжек, и аппарат при включении долго висит, составляя список закачанного. Тогда не было еще поддержки коллекций и Сонька долго бегала по всей карточке, собирая информацию о закачанных книжках. Многие жаловались.
Более удобна была работа с книжками от PocketBook — в них поддерживалась навигация по файловой системе, поэтому можно было вручную раскидать книги по папкам, и считывал аппарат только книги в той папке, в которую мы зашли.
Вручную с компа уже можно было что-то формировать и как-то жить.

Тогда же меня познакомили с ЛибРусЕк'ом(теперь уже более актуальна Флибуста, кто не знает, что это, вам сюда). И в какой-то момент мне пришла идея, а не попробовать ли загнать всю библиотеку в электрокнижку, слегка автоматизировав этот процесс?

А теперь важное! Чтобы не тратить зря внимание людей, которым данная статья вероятно помочь ничем не сможет(к сожалению такие будут), дам фильтрующую установку:
Если вы являетесь владельцем электронной читалки 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_dir\src», из которых будем читать книжки, в т.ч. в зипах.
И есть директория «out_dir\dest», в которую будем складывать каталогизатор и хранилище. Собственно после работы скрипта все, что будет находиться в этой директории, нужно перенести в корень SD-карты.
Ну либо можно скрипт настроить сразу, чтобы писал прямо на флешку.

Тут надо оговориться о паре нюансов касательно записи на SD-карты. Приготовьтесь сразу к тому, что процесс этот не быстрый, учитывая количество информации и фрагментацию.

  • Файлов будет очень много. Кроме количества исходных файлов, на флешку так же запишется минимум в три раза больше мелких flk-файлов. Каждый из них содержит ссылку на zip-архив и занимает всего несколько байт, но на носителе занимает целый кластер. Нужно это помнить. Поэтому есть смысл отформатировать флешку с как можно меньшим размером кластера. Иначе мегабайт этих файлов по сумме их размеров в реальности может запросто сожрать на флешке уже гигабайт места.
  • Флешка(SD-карта, не важно) будет греться. Кроме того, что она затормозит через короткое время из-за заполненности буфферов, она еще и по мере разогрева будет снижать скорость записи. Я не знаю, почему так происходит, но есть такое дело. Ей нужно давать перерывы, чтоб остыла. Я обычно формирую скриптом библиотеку на жесткий диск, а потом с него уже копирую на флешку пачками. Еще возможно есть смысл использовать какой-либо контейнер типа TrueCrypt, на который можно сформировать библиотеку как на флешку, потом содрать образ диска и развернуть на SD-карту. Это будет самый быстрый способ записи на флешку.
  • SD-карту, если у вас еще нет карты большого объема, лучше покупать классом повыше(например Class10), это выйдет немного дороже, но значительно ускорит процесс записи. Class10, кстати, уже вполне терпимо можно забить до упора за рабочий день. Это я еще раз напоминаю, что речь о сотнях тысяч мелких файлов. По скорости записи это кардинально отличается от «закатать фильмец на 4 Гига». Вот прям абсолютно не похоже.

Скрипт писался для машины на 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_LEAVE — оставить все как было в исходнике.
  • XMLP_UT_CONVERT — конвертировать в текст, отображаемвый в книге. То есть <tag>текст</tag> превратится в &lt;tag&gt; текст&lt;/tag&gt; и будет в результате в книге отображаться как <tag>текст</tag>.
  • XMLP_UT_CUT — будут вырезаны только сами теги. Их внутренний текст останется доступным читателю. <phpcode>$a=$b;</phpcode> превратится в $a=$b;
  • XMLP_UT_CUT_FULL — будут вырезаны неизвестные теги вместе с их содержимым. то есть полностью.
  • XMLP_UT_CUT_SMART — не реализовано. алиас для XMLP_UT_CUT

Очевидно для исправления ошибок наиболее подходят два варианта: 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>это> совершенно &lt;другая история.<:></p>
   <p>— Вот те на! — выразил <a>свое изумление Якуб.</p>
   <p>Зубами он оторвал уголок </a>конверта и достал плотно сложенную бумагу. Развернул.</p>

После(настройка XMLP_UT_CONVERT):
    <p>&lt;:&gt;В следующий раз &lt;tag&gt;Якуб&lt;/tag&gt; пошел на исповедь &lt;одиннадцать лет спустя&gt;, и &lt;/b&gt;это&gt; совершенно &lt;другая история.&lt;:&gt;</p>
   <p>— Вот те на! — выразил <a>свое изумление Якуб.</a></p>
   <p>Зубами он оторвал уголок конверта и достал плотно сложенную бумагу. Развернул.</p>

После(настройка XMLP_UT_CUT):
   <p>В следующий раз Якуб пошел на исповедь &lt;одиннадцать лет спустя&gt;, и это&gt; совершенно &lt;другая история.</p>
   <p>— Вот те на! — выразил <a>свое изумление Якуб.</a></p>
   <p>Зубами он оторвал уголок конверта и достал плотно сложенную бумагу. Развернул.</p>

Где взять это добро?


А вот здесь.

Еще на всяк пожарный тем, кто не хочет геморроиться с настройками PHP под Windows, вывалил свой пакет сюда. По большому счету, лучше скачайте свежую версию с php.net, но если в лом, и хочется по-быстрому безболезненно попробовать, и если что, сразу же снести, то разворачиваем данный архив в папку c:\php(в общем-то не важно в какую, желательно, чтобы путь был покороче и без пробелов), и прописываем в системную переменную окружения path эту директорию. Все сразу будет готово к работе.

P.S. Да, я знаю, что существуют десктопные программы, позволяющие хранить свою коллекцию. Но…
Во-первых, делалось это все, когда таких программ еще не было.
Во-вторых, они не позволят мне быстро сделать для себя то, что я могу сделать сам(допилить что-то в коде).
В третьих, вряд ли эти программы умеют править критические ошибки в книгах. Я в скрипте умышленно не использовал более быстрый парсинг XML с помощью встроенных в PHP Си-шных библиотек, ибо они падают на ошибках. Можно отключить вылет скрипта при ошибках, но нельзя нормально допарсить файл и автоматически его исправить.
Данный скрипт хоть и написан на «нативном» PHP, тем не менее оптимизирован по скорости обработки.
И много чего еще.
Кстати, никто не мешает же совместить и то и другое. Из десктопной программы отбираем нужные нам книги по условиям(там ведь в GIU гораздо удобнее условия всякие ставить и выливать все по фильтрам), а дальше скармливаем этому скрипту и он формирует каталог на карту памяти.

UPD: Обнаружилась засада с новыми Покетбуками и новыми прошивками. Убирают старый функционал книжной полки, и поддержку .flk файлов. Обсуждение проблемы вы можете видеть в комментах в моей переписке с пользователем Klu4nik. Функционал с .flk файлами работает на 912-м Покете на заводской прошивке E912.2.1.2 20110727_154845 из магазина. На более новых прошивках уже не работает.
Звонок в саппорт Покетбуков ничего полезного не дал, одна вода.
После этого с помощью Klu4nik я вышел на официальном форуме на пользователя с ником Antuan, с помощью которого нашел таки решение. Ну как решение, временный костыль по крайней мере у нас есть.
Заключается он в следующем:
Покетбуковская прошивка поддерживает родные и сторонние приложения, вы могли их видеть, зайдя из главного меню в одноименную папку.
Кроме прочего, эти приложения могут быть shell-скриптами, достаточно дать им расширение .app
После тестов оказалось, что можно книжку открыть простой шелл-коммандой вида "/ebrmain/bin/fbreader.app /mnt/ext2/_zipstorage_/00000000/00000000/00000001.zip".
Кроме того, если, находясь в папке приложений, на устройстве просто выйти на директорию вверх(директория "..", не выход в главное меню полки), вы будете видеть и память покетбука, и карту памяти, и можете ходить везде и запросто запускать .app файлы. Если войти из главного меню в библиотеку — .app файлы вы видеть не сможете. А через «Приложения» сможете.
Я обновил исходники, теперь там в файле «process.php» появилась еще одна опция настройки $app_instead_of_flk. Если ее включить(true), то вместо .flk файлов в каталогизаторе будут ложиться файлы .app вида «Война и мир.app», приблизительно с таким содержанием:

#!/bin/sh
/ebrmain/bin/fbreader.app /mnt/ext2/_zipstorage_/00000000/00000000/00000001.zip 

Работает на ура, книжки открывает. Проверял на своем PocketBook Pro 912, вроде все работает нормально.
Но есть и пара минусов.
При входе через раздел «Приложения» вместо книжной полки работает простенький файловый менеджер. Никаких обложек и красивой книжной полки он не отобразит. Так же нет возможности добавлять в «Избранное».

В общем, перекачайте скрипт по ссылке выше.

Так же посоветовали добавить на сайт idea.pocketbook-int.com запрос на возращение старого функционала.
Там уже есть топик: idea.pocketbook-int.com/pocketbook-pro/idea/161, нужно только голосов набрать побольше, тогда есть шанс, что сделают опцию старой полки в новых прошивках. Но там нужно регистрироваться, чтобы проголосовать за реализацию этой фичи.

UPD 2: Решение номер два. Тоже временное(ну кому как).
На текущие прошивки 2.1.2 и 2.1.3 можно установить старую полку от PocketBook 611. Будет интерфейс главного меню, как у старых прошивок. При этом работают файлы-ссылки .flk, «Избранное», мультизадачность, настройки новой прошивки.
Выглядит это на 9.7 дюймах вот так(вертикальная и горизонтальная ориентации):
       

      

    
(картинки кликабельны, на скриншоты и превьюхи я добавил только однопиксельную рамку, чтобы видно было края экрана на белом фоне)
Пока замечено только два небольших глючка:
1. главное меню не на полный экран, но работает как надо, не раздражает. Оно и понятно — шаблон менюхи делался под экран 6 дюймов. Остальные экраны нормально масштабируются сами. Так же не на полную ширину экрана рендерится «детальный» вид полки. На скриншотах видно.
2. кнопка «Домой»(возврат в главное меню) не работает. Т.к. переключение в главное меню из контекстного тоже не работает, видимо это связано с мультизадачной средой — не может переключить задачу на то немногозадачное меню, которое мы заменили. Немного раздражает долго выходить в главное меню из дерева каталогов.

Установка проста. Качаем здесь. Внутри зипа один файл «bookshelf.app». Нужно подключиться USB-шнурком и записать этот файл на внутреннюю память устройства(не на флешку) в директорию "/system/bin/".
Собственно все. После отключения кабеля заработает сразу, хотя лучше на всяк пожарный перегрузиться, чтобы основная полка выгрузилась из памяти.
Снос точно так же прост — цепляем шнурок, удаляем этот файл "/system/bin/bookshelf.app" и все вернется на круги своя.
Проверено на устройстве PocketBook Pro 912. Работает нормально.

UPD 3: Появилось дополнительное обсуждение здесь. Велкам с пожеланиями и предложениями.

UPD 4[15.05.2012 20:52 MSK]: Исправлена злостная ошибка в «genres.inc», функция genres_get(), приводившая к жору памяти в цикле. Тупая очепятка была. Во всем виноват копипаст. Исходники обновлены, перекачайте.

UPD 5[15.05.2012 22:03 MSK]: Как результат обсуждений по ссылке в UPD3, добавлены и изменены следующие настройки:

$dest_mount_point='/mnt/ext2/';
$storagename='.zipstorage';
$path_encoding=PATH_ENC_AUTO;

Переменная $path_encoding отвечает за то, в какой кодировке будут генерироваться пути в каталогизаторе.
Кроме дефолтного автоматического значения могут быть так же принудительные PATH_ENC_1251 или PATH_ENC_UTF8.
PATH_ENC_AUTO определяет по признаку preg_match('/win/i',PHP_OS)
Если в константе PHP_OS содержится «win», то будет PATH_ENC_1251, иначе PATH_ENC_UTF8.

UPD 6[20.05.2012 03:10 MSK]: Переехали на ГитХаб.