├── .env ├── .gitignore ├── Flibusta.Net └── .gitkeep ├── FlibustaSQL ├── .gitkeep └── psql │ └── .gitkeep ├── LICENSE ├── README.md ├── application ├── dbinit.php ├── djvu.php ├── epub.php ├── functions.php ├── init.php ├── modules │ ├── 404 │ │ ├── index.php │ │ └── module.conf │ ├── author │ │ ├── index.php │ │ └── module.conf │ ├── authors │ │ ├── index.php │ │ └── module.conf │ ├── book │ │ ├── djvu.php │ │ ├── docx.php │ │ ├── epub.php │ │ ├── fb.php │ │ ├── html.php │ │ ├── index.php │ │ ├── mobi.php │ │ ├── module.conf │ │ ├── pdf.php │ │ ├── rtf.php │ │ └── txt.php │ ├── fav │ │ ├── index.php │ │ └── module.conf │ ├── favlist │ │ ├── index.php │ │ └── module.conf │ ├── genres │ │ ├── index.php │ │ └── module.conf │ ├── opds │ │ ├── index.php │ │ └── module.conf │ ├── primary │ │ ├── index.php │ │ └── module.conf │ ├── series │ │ ├── index.php │ │ └── module.conf │ └── service │ │ ├── index.php │ │ └── module.conf ├── none.jpg ├── opds │ ├── author.php │ ├── authorsindex.php │ ├── fav.php │ ├── favs.php │ ├── genres.php │ ├── index.php │ ├── list.php │ ├── listgenres.php │ ├── main.php │ ├── search.php │ ├── search_author.php │ ├── search_book.php │ └── sequencesindex.php ├── public │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-grid.rtl.css │ │ │ ├── bootstrap-grid.rtl.css.map │ │ │ ├── bootstrap-grid.rtl.min.css │ │ │ ├── bootstrap-grid.rtl.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap-reboot.rtl.css │ │ │ ├── bootstrap-reboot.rtl.css.map │ │ │ ├── bootstrap-reboot.rtl.min.css │ │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ │ ├── bootstrap-utilities.css │ │ │ ├── bootstrap-utilities.css.map │ │ │ ├── bootstrap-utilities.min.css │ │ │ ├── bootstrap-utilities.min.css.map │ │ │ ├── bootstrap-utilities.rtl.css │ │ │ ├── bootstrap-utilities.rtl.css.map │ │ │ ├── bootstrap-utilities.rtl.min.css │ │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ ├── bootstrap.min.css.map │ │ │ ├── bootstrap.rtl.css │ │ │ ├── bootstrap.rtl.css.map │ │ │ ├── bootstrap.rtl.min.css │ │ │ └── bootstrap.rtl.min.css.map │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.esm.js │ │ │ ├── bootstrap.esm.js.map │ │ │ ├── bootstrap.esm.min.js │ │ │ ├── bootstrap.esm.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ ├── css │ │ ├── all.min.css │ │ ├── css.css │ │ └── style.css │ ├── extract_author.php │ ├── extract_cover.php │ ├── extract_usr.php │ ├── favicon.ico │ ├── favicon.svg │ ├── fb2.php │ ├── font │ │ ├── weathericons-regular-webfont.eot │ │ ├── weathericons-regular-webfont.svg │ │ ├── weathericons-regular-webfont.ttf │ │ ├── weathericons-regular-webfont.woff │ │ └── weathericons-regular-webfont.woff2 │ ├── fonts │ │ ├── KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 │ │ ├── KFOlCnqEu92Fr1MmWUlfBBc4.woff2 │ │ ├── Roboto-Bold.woff2 │ │ ├── Roboto-Medium.woff2 │ │ ├── Roboto-Regular.woff2 │ │ ├── flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ ├── materialicons.woff2 │ │ └── roboto │ │ │ ├── Roboto-Bold.eot │ │ │ ├── Roboto-Bold.ttf │ │ │ ├── Roboto-Bold.woff │ │ │ ├── Roboto-Bold.woff2 │ │ │ ├── Roboto-Light.eot │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-Light.woff │ │ │ ├── Roboto-Light.woff2 │ │ │ ├── Roboto-Medium.eot │ │ │ ├── Roboto-Medium.ttf │ │ │ ├── Roboto-Medium.woff │ │ │ ├── Roboto-Medium.woff2 │ │ │ ├── Roboto-Regular.eot │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── Roboto-Regular.woff │ │ │ ├── Roboto-Regular.woff2 │ │ │ ├── Roboto-Thin.eot │ │ │ ├── Roboto-Thin.ttf │ │ │ ├── Roboto-Thin.woff │ │ │ └── Roboto-Thin.woff2 │ ├── i │ │ ├── default_avatar.png │ │ └── ic_launcher.png │ ├── index.php │ ├── js │ │ ├── EMFJS.bundle.js │ │ ├── EMFJS.bundle.js.map │ │ ├── RTFJS.bundle.js │ │ ├── RTFJS.bundle.js.map │ │ ├── WMFJS.bundle.js │ │ ├── WMFJS.bundle.js.map │ │ ├── djvu.js │ │ ├── djvu_viewer.js │ │ ├── docx-preview.min.js │ │ ├── docx-preview.min.js.map │ │ ├── epub.min.js │ │ ├── jszip.min.js │ │ ├── mobi.min.js │ │ ├── pdf.js │ │ ├── pdf.js.map │ │ ├── pdf.worker.js │ │ └── pdf.worker.js.map │ ├── opds-opensearch.xml │ ├── opds-opensearch.xml.php │ ├── save_position.php │ ├── usr.php │ └── webfonts │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.svg │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ └── fa-solid-900.woff2 ├── renderer.php ├── tools │ ├── app_db_converter.py │ ├── app_import_sql.sh │ ├── app_reindex.sh │ ├── app_topg │ ├── app_update_zip_list.php │ ├── cleanup_db.sql │ ├── dbinit.sh │ ├── external_services_config │ │ ├── README.md │ │ ├── docker-compose.yml │ │ ├── external_postgres_init.sql │ │ ├── flibusta.conf │ │ └── flibusta_entrypoint.sh │ └── update_vectors.sql └── webroot.php ├── blob ├── 1 ├── x1.png ├── x2.png ├── x3.png └── x4.png ├── cache ├── .gitkeep ├── authors │ └── .gitkeep ├── covers │ └── .gitkeep └── tmp │ └── .gitkeep ├── docker-compose.yml ├── getcovers.sh ├── getsql.sh ├── phpdocker ├── nginx │ └── nginx.conf ├── pg │ ├── Dockerfile │ └── init_db.sql └── php-fpm │ ├── Dockerfile │ ├── php-fpm.conf │ └── php-ini-overrides.ini ├── secrets ├── flibusta_pwd.txt └── postgres_admin_pwd.txt └── update_daily.sh /.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=flibusta 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cache/* 2 | Flibusta.Net/* 3 | FlibustaSQL/* -------------------------------------------------------------------------------- /Flibusta.Net/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/Flibusta.Net/.gitkeep -------------------------------------------------------------------------------- /FlibustaSQL/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/FlibustaSQL/.gitkeep -------------------------------------------------------------------------------- /FlibustaSQL/psql/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/FlibustaSQL/psql/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker-контейнер для локальной копии Флибусты. 2 | 3 | Отображение книг, поиск по заголовкам, сборникам, авторам, жанрам. Открываются в браузере форматы fb2, docx, rtf, mobi, epub, txt, html. Для fb2 сохраняется позиция чтения. 4 | 5 | Возможность создания "книжных полок" для избранных книг, авторов и сборников. 6 | 7 | Встроенный сервис OPDS для читалок. 8 | 9 | |![x](blob/x1.png)|![x](blob/x2.png)|![x](blob/x3.png)|![x](blob/x4.png)| 10 | |---|---|---|---| 11 | ## Установка: 12 | 13 | 1. Установить сервисы docker для вашего сервера. 14 | 2. Разместить файлы проекта в каталог на сервере. 15 | 3. Для выполнения обновления необходимо разместить фалы дампа Флибусты (*.sql) в каталог FlibustaSQL. 16 | 4. Файлы архивов Флибусты (*.zip) необходимо размещать в каталоге Flibusta.Net. 17 | 5. Выполнить docker-compose build 18 | 6. Выполнить docker-compose up -d 19 | 7. Локальный портал будет доступен на порту 27100. Доступен также OPDS каталог: /opds/ 20 | 8. Выполнить "Обновление базы" через пункт меню "Сервис" 21 | 22 | Каталоги FlibustaSQL, cache и их подкаталоги должны иметь права на запись для контейнера. Скрипты в каталоге /application/tools/ должны иметь права на выполнение. 23 | 24 | Для обновления базы с новыми файлами выполнить пункты 3, 4, 5, 9 25 | 26 | ## Переменные окружения для docker-compose (docker-compose environmnet variables): 27 | 28 | Параметры базы данных можно определить в docker-compose.yml. 29 | 30 | Образ может использовать следующие переменные окружения 31 | 32 | 1. `FLIBUSTA_DBUSER` user базы данных. По умолчанию (если переменная не определена в файле) flibusta 33 | 2. `FLUBUSTA_DBNAME` имя схемы ( инстанса) базы данных. По умолчанию (если переменная не определена в файле) flibusta 34 | 3. `FLIBUSTA_DBTYPE` тип базы данных. В текущей версии поддерживается только postgres 35 | 4. `FLIBUSTA_DBHOST` имя хоста на которой установлена база данных. В докере по умолчанию совпадает с именем сервиса 36 | определяющего базу данных. По умолчанию postgres 37 | 5. `FLIBUSTA_DBPASSWORD` пароль user базы данных. См. обсуждение в следующем абзаце. По умолчанию flibusta 38 | 39 | 40 | Best practices docker compose не рекомендуют хранить пароль в переменной ( и тем более не рекомендуется задавать пароль в коде). 41 | Для хранения паролей в есть механизм secrets. Можно создать текстовой файл содержащий единственную 42 | строчку с паролем и указать полный путь к нему в глобальном разделе secrets. Обратите внимание на premissions файла - он должен быть 43 | доступен для чтения для php из образа, в идеале только ему. Юзер под которым бежит php-fpm в pool www-data ( user id 82). Это стандартный 44 | user из официального образа. Возможно он не существует на вашей системе. Можно дать числовой uid файлу если нет желания или возможности создать user. достаточно выполнить `sudo chown 82 /path/to/file` 45 | 46 | ## Конфигурация docker container для работы с reverse proxy и внешним сервером базы данных 47 | 48 | Docker compose файл, используемый по умолчанию, поднимает веб сервер и базу данных для локального зеркала Флибусты. Однако, часто вебсервер и база данных уже работают на NAS. 49 | В таком случае можно использовать их и сэкономить ресурсы сервера не поднимая несколько инстансов параллельно. Кроме того, использование централизованного reverse proxy может дать некотрые дополнительные преимущества. Папка application/tools/external_services_config 50 | содержит необходимые скрипты и конфигурационные файлы для работы в такой конфигурации, а [README](application/tools/external_services_config/README.md) описывает процесс конфигурации. 51 | 52 | 53 | -------------------------------------------------------------------------------- /application/dbinit.php: -------------------------------------------------------------------------------- 1 | "pgsql:host=".$dbhost.";dbname=".$dbname.";options='--client_encoding=UTF8'", 19 | // dsn for supported db types should be added here 20 | default => "pgsql:host=".$dbhost.";dbname=".$dbname.";options='--client_encoding=UTF8'" 21 | }; 22 | 23 | try { 24 | $dbh = new PDO($dsn, $dbuser, $dbpasswd); 25 | $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 26 | $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 27 | $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); 28 | } catch(Exception $e) { 29 | print_r($e); 30 | } 31 | 32 | ?> -------------------------------------------------------------------------------- /application/djvu.php: -------------------------------------------------------------------------------- 1 | filename = $filename; 21 | if (!file_exists($filename)) { die ("Djvu: file does not exist \"$filename\""); } 22 | } // end constructor 23 | 24 | // Method: NumberOfPages 25 | // 26 | // Returns: 27 | // 28 | // Number of pages in the current Djvu document. 29 | // 30 | function NumberOfPages ( ) { 31 | $filename = $this->filename; 32 | 33 | // Get basic info dump from file 34 | $info = `djvudump "$filename" | grep "Document directory"`; 35 | 36 | // If no info, one page 37 | if (empty($info)) { return 1; } 38 | 39 | // Split down to page number 40 | $_h1 = explode(", ", $info); 41 | $_h2 = explode(" ", trim($_h1[1])); 42 | 43 | // Return next to last segment 44 | return $_h2[count($_h2)-2]; 45 | } // end method NumberOfPages 46 | 47 | // Method: GetPage 48 | // 49 | // Get page image 50 | // 51 | // Parameters: 52 | // 53 | // $page - Page number to return 54 | // 55 | // $contents - (optional) Boolean, return the contents instead of the 56 | // filename. Defaults to false. 57 | // 58 | // $force_ps - (optional) Boolean, force no JPEG conversion. Defaults 59 | // to false. 60 | // 61 | // $force_rotate - (optional) Boolean, force 90 degree rotation 62 | // 63 | // Returns: 64 | // 65 | // Either JPEG image of file in string or name of temporary file. 66 | // 67 | function GetPage ( $page, $contents = false, $force_ps = false, $force_rotation = true ) { 68 | $filename = $this->filename; 69 | 70 | $s = $size."x".$size; 71 | 72 | $_t = tempnam('/tmp', 'fmdjvu'); 73 | $rotate = $force_rotation ? " -rotate 90 " : ""; 74 | if ($force_ps) { 75 | // No conversion ... 76 | $t = $_t.'.ps'; 77 | $temp = `djvups -page=$page "$filename" | /usr/bin/convert - ${rotate} "$t"`; 78 | } else { 79 | // Force convert to JPEG 80 | $t = $_t.'.jpg'; 81 | $temp = `djvups -page=$page "$filename" | /usr/bin/convert - ${rotate} "$t"`; 82 | } 83 | 84 | if ($contents) { 85 | ob_start(); 86 | readfile($t); 87 | $c = ob_get_contents(); 88 | ob_end_clean(); 89 | 90 | unlink ($_t); 91 | unlink ($t); 92 | 93 | return $c; 94 | } else { 95 | unlink ($_t); 96 | return $t; 97 | } 98 | } // end method GetPage 99 | 100 | // Method: GetPageThumbnail 101 | // 102 | // Get textual content of a page thumbnail. 103 | // 104 | // Parameters: 105 | // 106 | // $page - Page number 107 | // 108 | // $size - (optional) Maximum dimension of thumbnail. Defaults to 300 (px). 109 | // 110 | // Returns: 111 | // 112 | // String containing JPEG thumbnail of specified page. 113 | // 114 | function GetPageThumbnail ( $page, $size=300 ) { 115 | $filename = $this->filename; 116 | 117 | $s = $size."x".$size; 118 | 119 | $_t = tempnam('/tmp', 'fmdjvu'); 120 | $t = $_t.'.jpg'; 121 | $temp = `djvups -page=$page "$filename" | convert - -scale $s "$t"`; 122 | $temp = `djvups -page=$page "$filename" | convert - "$t"`; 123 | ob_start(); 124 | readfile($t); 125 | $contents = ob_get_contents(); 126 | ob_end_clean(); 127 | 128 | unlink ($_t); 129 | unlink ($t); 130 | 131 | return $contents; 132 | } // end method GetPageThumbnail 133 | 134 | // Method: StoredChunks 135 | // 136 | // Get list of chunks contained in the parent DjVu file. 137 | // 138 | // Returns: 139 | // 140 | // Array of chunk names. 141 | // 142 | function StoredChunks ( ) { 143 | $filename = $this->filename; 144 | 145 | $raw = `djvm -l "$filename"`; 146 | 147 | // deal by line 148 | $lines = explode("\n", $raw); 149 | 150 | foreach ($lines as $__garbage => $line) { 151 | if (eregi('PAGE #', $line)) { 152 | // Split out contents .... 153 | $_h1 = explode('#', $line); 154 | $_h2 = explode(' ', trim($_h1[1])); 155 | 156 | $pages[] = trim($_h2[count($_h2) - 1]); 157 | } 158 | } 159 | return $pages; 160 | } // end method StoredChunks 161 | 162 | } // end class Djvu 163 | 164 | -------------------------------------------------------------------------------- /application/init.php: -------------------------------------------------------------------------------- 1 | title = 'Страница не найдена'; 3 | #$url->keywords .= ''; 4 | header("HTTP/1.0 404 Not Found"); 5 | -------------------------------------------------------------------------------- /application/modules/author/index.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM libavtorname LEFT JOIN libapics USING(AvtorId) WHERE avtorid=:id"); 4 | $stmt->bindParam(":id", $url->var1); 5 | $stmt->execute(); 6 | $a = $stmt->fetch(); 7 | 8 | echo "
"; 9 | echo "

$a->lastname $a->firstname $a->middlename $a->nickname

"; 10 | echo "
"; 11 | 12 | echo "
"; 13 | if (isset($a->file)) { 14 | echo "
"; 15 | } 16 | echo "Книги автора"; 17 | 18 | try { 19 | $stmt = $dbh->prepare("SELECT COUNT(*) cnt FROM fav WHERE user_uuid=:uuid AND avtorid=:id"); 20 | $stmt->bindParam(":uuid", $user_uuid); 21 | $stmt->bindParam(":id", $a->avtorid); 22 | $stmt->execute(); 23 | $is_fav = ($stmt->fetch()->cnt > 0); 24 | if (!$is_fav) { 25 | echo "В избранное"; 26 | } else { 27 | echo "Из избранного"; 28 | } 29 | } catch (PDOException $e) { 30 | // 31 | } 32 | 33 | echo "
"; 34 | echo "
"; 35 | 36 | $stmt = $dbh->prepare("SELECT * FROM libaannotations WHERE AvtorId=:id"); 37 | $stmt->bindParam(":id", $url->var1); 38 | $stmt->execute(); 39 | while ($an = $stmt->fetch()) { 40 | echo "$an->title
"; 41 | echo "

", bbc2html(nl2br($an->body)), "

"; 42 | } 43 | echo '
'; 44 | 45 | 46 | 47 | echo "
"; 48 | 49 | -------------------------------------------------------------------------------- /application/modules/author/module.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/modules/author/module.conf -------------------------------------------------------------------------------- /application/modules/authors/index.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | "; 36 | foreach (range(chr(0xC0), chr(0xDF)) as $b) { 37 | $l = iconv('CP1251', 'UTF-8', $b); 38 | if ($l == mb_strtoupper($get)) { 39 | $cc = 'active'; 40 | } else { 41 | $cc = ''; 42 | } 43 | echo "
  • $l
  • "; 44 | } 45 | echo ""; 46 | echo ""; 58 | 59 | 60 | echo "
    \n"; 61 | ?> 62 |
    63 | 64 |
    65 | 66 | 67 |
    68 |
    69 |
    70 | 71 | prepare("SELECT COUNT(*) cnt FROM libavtorname WHERE lower(libavtorname.lastname) LIKE :letter"); 75 | $stmt->bindParam(":letter", $letter); 76 | $stmt->execute(); 77 | $cnt = $stmt->fetch()->cnt; 78 | 79 | $stmt = $dbh->prepare("SELECT *, 80 | (SELECT COUNT(*) FROM libavtor WHERE libavtor.avtorid=libavtorname.avtorid) cnt 81 | FROM libavtorname 82 | LEFT JOIN libapics USING(AvtorId) 83 | WHERE LOWER(libavtorname.lastname) LIKE :letter 84 | ORDER BY firstname LIMIT " . AUTHORS_PAGE . " OFFSET $start"); 85 | $stmt->bindParam(":letter", $letter); 86 | $stmt->execute(); 87 | 88 | echo '
    '; 89 | show_gpager(ceil($cnt / AUTHORS_PAGE), 5); 90 | while ($a = $stmt->fetch()) { 91 | if ($a->cnt > 0) { 92 | echo "
    "; 93 | echo ""; 94 | if ($a->file != '') { 95 | echo ""; 96 | } 97 | echo " $a->lastname $a->firstname $a->middlename $a->nickname "; 98 | echo "
    $a->cnt
    "; 99 | echo "
    "; 100 | 101 | } 102 | } 103 | echo "
    "; 104 | 105 | show_gpager(ceil($cnt / AUTHORS_PAGE), 5); 106 | -------------------------------------------------------------------------------- /application/modules/authors/module.conf: -------------------------------------------------------------------------------- 1 | title = 'Авторы'; 3 | -------------------------------------------------------------------------------- /application/modules/book/djvu.php: -------------------------------------------------------------------------------- 1 | \n"; 4 | echo "\n"; 5 | ?> 6 | 21 | 22 | -------------------------------------------------------------------------------- /application/modules/book/docx.php: -------------------------------------------------------------------------------- 1 | \n"; 4 | echo "\n"; 5 | ?> 6 | 12 | 13 | -------------------------------------------------------------------------------- /application/modules/book/epub.php: -------------------------------------------------------------------------------- 1 | "; 3 | echo ""; 4 | ?> 5 | 30 | 31 | -------------------------------------------------------------------------------- /application/modules/book/fb.php: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | prepare("SELECT pos FROM progress WHERE user_uuid=:uuid AND bookid=:id LIMIT 1"); 22 | $stmt->bindParam(":uuid", $user_uuid); 23 | $stmt->bindParam(":id", $url->var1); 24 | $stmt->execute(); 25 | if ($p = $stmt->fetch()) { 26 | echo ""; 31 | } 32 | } 33 | 34 | 35 | $content = ''; 36 | $data = $zip->getFromName("$url->var1.fb2"); 37 | 38 | $fb2 = simplexml_load_string($data); 39 | echo ($fb2 ? '' : 'FB2 Parse Error'), PHP_EOL; 40 | 41 | $images = array(); 42 | foreach ($fb2->binary as $binary) { 43 | $id = $binary->attributes()['id']; 44 | $images["$id"] = $binary; 45 | } 46 | 47 | if (isset($fb2->body->section)) { 48 | foreach ($fb2->body->section as $section) { 49 | $s = $section->asXML(); 50 | $s = str_replace("", "<subtitle>", $s); 51 | $s = str_replace("", "", $s); 52 | $s = str_replace('body->asXML(); 60 | $s = str_replace("", "<subtitle>", $s); 61 | $s = str_replace("", "", $s); 62 | $s = str_replace('***

    ", '
    ', str_replace("section>>", "section>", $content)); 69 | 70 | -------------------------------------------------------------------------------- /application/modules/book/html.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /application/modules/book/index.php: -------------------------------------------------------------------------------- 1 | var url = '$webroot/usr.php?id=$url->var1';"; 3 | 4 | function nl2p($string) { 5 | $paragraphs = ''; 6 | 7 | foreach (explode("\n", $string) as $line) { 8 | if (trim($line)) { 9 | $paragraphs .= '

    ' . $line . '

    '; 10 | } 11 | } 12 | 13 | return $paragraphs; 14 | } 15 | book_info_pg($book, $webroot, true); 16 | 17 | echo "
    "; 28 | 29 | 30 | function str_replace_first($from, $to, $content) { 31 | $from = '/'.preg_quote($from, '/').'/'; 32 | return preg_replace($from, $to, $content, 1); 33 | } 34 | 35 | 36 | $ext = strtolower(trim($book->filetype)); 37 | 38 | if ($ext == 'fb2') { 39 | $stmt = $dbh->prepare("SELECT * FROM book_zip WHERE $url->var1 BETWEEN start_id AND end_id AND usr=0"); 40 | } else { 41 | $stmt = $dbh->prepare("SELECT * FROM book_zip WHERE $url->var1 BETWEEN start_id AND end_id AND usr=1"); 42 | } 43 | $stmt->execute(); 44 | $zip_name = $stmt->fetch()->filename; 45 | $zip = new ZipArchive(); 46 | 47 | echo "
    "; 48 | if ($zip->open(ROOT_PATH . "flibusta/" . $zip_name)) { 49 | if ($ext == 'fb2') { 50 | include('fb.php'); 51 | } 52 | 53 | if ($ext == 'txt') { 54 | include('txt.php'); 55 | } 56 | 57 | if ($ext == 'epub') { 58 | include('epub.php'); 59 | } 60 | 61 | if ($ext == 'pdf') { 62 | include('pdf.php'); 63 | } 64 | 65 | if ($ext == 'mobi') { 66 | include('mobi.php'); 67 | } 68 | 69 | if (($ext == 'djvu') || ($ext == 'djv')) { 70 | include('djvu.php'); 71 | } 72 | 73 | if ($ext == 'rtf') { 74 | include('rtf.php'); 75 | } 76 | 77 | if ($ext == 'docx') { 78 | include('docx.php'); 79 | } 80 | 81 | if (($ext == 'html') || ($ext == 'htm')) { 82 | include('html.php'); 83 | } 84 | 85 | $zip->close(); 86 | } 87 | 88 | 89 | ?> 90 |
    91 | -------------------------------------------------------------------------------- /application/modules/book/mobi.php: -------------------------------------------------------------------------------- 1 | \n"; ?> 3 | 10 | 11 | -------------------------------------------------------------------------------- /application/modules/book/module.conf: -------------------------------------------------------------------------------- 1 | prepare("SELECT b.*, 6 | (SELECT Body FROM libbannotations WHERE BookId=b.BookId LIMIT 1) Body 7 | FROM libbook b WHERE bookid=:id LIMIT 1"); 8 | 9 | $stmt->bindParam(":id", $url->var1); 10 | 11 | try { 12 | $stmt->execute(); 13 | $book = $stmt->fetch(PDO::FETCH_OBJ); 14 | } catch (Exception $e) { 15 | $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); 16 | header($protocol . ' 504 Gateway Time-out'); 17 | 18 | echo "
    База данных
    "; 19 | echo "

    " . $e->getMessage() . "

    "; 20 | echo "

    Попробуйте упростить параметры поиска, убрать часть тэгов, направленность. Сервер маленький ^^.

    "; 21 | echo "
    "; 22 | } 23 | 24 | 25 | if (isset($book->bookid)) { 26 | $url->title = $book->title; 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /application/modules/book/pdf.php: -------------------------------------------------------------------------------- 1 | \n"; ?> 3 | 4 | 42 | 43 | -------------------------------------------------------------------------------- /application/modules/book/rtf.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 24 | -------------------------------------------------------------------------------- /application/modules/book/txt.php: -------------------------------------------------------------------------------- 1 | getFromName("$url->var1.txt"); 4 | if (!mb_detect_encoding($content, 'UTF-8', true)) { 5 | $content =iconv('windows-1251//IGNORE', 'UTF-8//IGNORE', $content); 6 | } 7 | $content = nl2p($content); 8 | echo "
    "; 9 | echo str_replace("

    ***

    ", '
    ', str_replace("section>>", "section>", $content)); 10 | echo "
    "; 11 | 12 | -------------------------------------------------------------------------------- /application/modules/fav/index.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT COUNT(*) cnt FROM fav_users"); 3 | $stmt->execute(); 4 | $fav_count = $stmt->fetch()->cnt; 5 | 6 | if ($fav_count == 0) { 7 | die("Книжные полки не определены"); 8 | } 9 | 10 | $stmt = $dbh->prepare("SELECT * 11 | FROM fav 12 | LEFT JOIN libavtorname USING(AvtorId) 13 | LEFT JOIN libapics USING(AvtorId) 14 | WHERE user_uuid=:uuid AND avtorid IS NOT NULL"); 15 | $stmt->bindParam(":uuid", $user_uuid); 16 | 17 | try { 18 | $stmt->execute(); 19 | } catch (PDOException $e) { 20 | // 21 | } 22 | 23 | echo '
    '; 24 | while ($a = $stmt->fetch()) { 25 | echo "
    "; 26 | echo ""; 27 | if ($a->file != '') { 28 | echo ""; 29 | } 30 | echo " $a->lastname $a->firstname $a->middlename $a->nickname "; 31 | echo "
    "; 32 | } 33 | echo "
    "; 34 | 35 | 36 | $stmt = $dbh->prepare("SELECT * 37 | FROM fav 38 | LEFT JOIN libseqname USING(seqid) 39 | WHERE user_uuid=:uuid AND seqid IS NOT NULL"); 40 | $stmt->bindParam(":uuid", $user_uuid); 41 | 42 | echo '
    '; 43 | echo '
    '; 44 | echo '
    '; 45 | try { 46 | $stmt->execute(); 47 | while ($s = $stmt->fetch()) { 48 | echo ""; 49 | echo " $s->seqname  "; 50 | } 51 | 52 | } catch (PDOException $e) { 53 | print_r($e); 54 | } 55 | echo "
    "; 56 | echo "
    "; 57 | echo "
    "; 58 | 59 | $stmt = $dbh->prepare("SELECT DISTINCT b.* 60 | FROM fav f 61 | LEFT JOIN libbook b USING(bookid) 62 | WHERE user_uuid=:uuid AND f.bookid IS NOT NULL"); 63 | $stmt->bindParam(":uuid", $user_uuid); 64 | $stmt->execute(); 65 | 66 | $cnt = $stmt->rowCount(); 67 | 68 | //show_gpager(ceil($cnt / RECORDS_PAGE), 5); 69 | 70 | echo "
    "; 71 | echo "
    "; 72 | 73 | $c = 0; 74 | while ($book = $stmt->fetch()) { 75 | $c++; 76 | if ($c > 10) { 77 | // break; 78 | } 79 | book_small_pg($book,$webroot); 80 | } 81 | echo "
    "; 82 | echo "
    "; 83 | 84 | //show_gpager(ceil($cnt / RECORDS_PAGE), 5); 85 | 86 | -------------------------------------------------------------------------------- /application/modules/fav/module.conf: -------------------------------------------------------------------------------- 1 | title = 'Книжная полка - Избранное'; 3 | -------------------------------------------------------------------------------- /application/modules/favlist/index.php: -------------------------------------------------------------------------------- 1 |
    '; ?> 3 |
    4 | 5 |
    6 | 7 |
    8 |
    9 |
    10 | 11 | 12 | query("SELECT * FROM fav_users"); 14 | 15 | echo '
    '; 16 | while ($a = $stmt->fetch()) { 17 | echo "
    "; 18 | echo "
    "; 19 | 20 | echo "
    "; 21 | echo "$a->name"; 22 | echo "
    "; 23 | 24 | echo "
    "; 25 | $bs = $dbh->prepare("SELECT (SELECT COUNT(*) cnt FROM fav WHERE user_uuid=:uuid AND bookid is not null) bcnt, (SELECT COUNT(*) cnt FROM fav WHERE user_uuid=:uuid AND avtorid is not null) acnt, (SELECT COUNT(*) cnt FROM fav WHERE user_uuid=:uuid AND seqid is not null) scnt"); 26 | $bs->bindParam(":uuid", $a->user_uuid); 27 | $bs->execute(); 28 | $sta = $bs->fetch(); 29 | echo '
      '; 30 | echo "
    • Книг: $sta->bcnt
    • "; 31 | echo "
    • Авторов: $sta->acnt
    • "; 32 | echo "
    • Серий: $sta->scnt
    • "; 33 | echo "
    "; 34 | echo "
    "; 35 | 36 | echo ""; 39 | 40 | echo "
    "; 41 | echo "
    "; 42 | } 43 | echo "
    "; 44 | 45 | -------------------------------------------------------------------------------- /application/modules/favlist/module.conf: -------------------------------------------------------------------------------- 1 | title = 'Выбор книжной полки'; 3 | -------------------------------------------------------------------------------- /application/modules/genres/index.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | prepare("SELECT g.GenreMeta 11 | FROM libgenrelist g 12 | GROUP BY g.GenreMeta 13 | ORDER BY (SELECT COUNT(*) FROM libgenrelist a WHERE g.GenreMeta=a.GenreMeta) DESC"); 14 | $stmt->execute(); 15 | $cn = 10; 16 | 17 | 18 | echo "
    "; 19 | 20 | while ($bg = $stmt->fetch()) { 21 | $cn++; 22 | if ($cn > 0) { 23 | $cn = 0; 24 | } 25 | 26 | echo "
    "; 27 | echo "
    "; 28 | 29 | echo "

    $bg->genremeta

    "; 30 | 31 | echo "
    "; 32 | 33 | $st2 = $dbh->prepare("SELECT libgenrelist.genreid, libgenrelist.genremeta, libgenrelist.genredesc, 34 | (SELECT COUNT(*) FROM libgenre WHERE libgenre.GenreId=libgenrelist.GenreId) cnt 35 | FROM libgenrelist 36 | WHERE GenreMeta=:meta 37 | ORDER BY genredesc"); 38 | $st2->bindParam(":meta", $bg->genremeta); 39 | $st2->execute(); 40 | while ($g = $st2->fetch()) { 41 | echo "
    "; 42 | echo "$g->genredesc "; 43 | echo "Исключить"; 44 | echo "$g->cnt
    "; 45 | } 46 | 47 | echo "
    "; 48 | echo "
    "; 49 | echo "
    "; 50 | 51 | } 52 | echo "
    "; 53 | -------------------------------------------------------------------------------- /application/modules/genres/module.conf: -------------------------------------------------------------------------------- 1 | title = 'Жанры'; 3 | -------------------------------------------------------------------------------- /application/modules/opds/index.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/modules/opds/index.php -------------------------------------------------------------------------------- /application/modules/opds/module.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/modules/opds/module.conf -------------------------------------------------------------------------------- /application/modules/primary/index.php: -------------------------------------------------------------------------------- 1 | '; 72 | if (isset($_SESSION['fb2'])) { 73 | $filter .= "AND filetype='fb2' "; 74 | $fcontent .= "Только FB2 "; 75 | } else { 76 | $fcontent .= "Все форматы "; 77 | } 78 | 79 | if (isset($_SESSION['ru'])) { 80 | $filter .= "AND lang='ru' "; 81 | $fcontent .= "На русском "; 82 | } else { 83 | $fcontent .= "Все языки "; 84 | } 85 | $fcontent .= ''; 86 | 87 | if (isset($_SESSION['filter_author'])) { 88 | $do_cnt = true; 89 | $filter .= 'AND avtorid=:aid '; 90 | $join .= 'LEFT JOIN libavtor a USING(BookId) '; 91 | $stmt = $dbh->prepare("SELECT * FROM libavtorname 92 | LEFT JOIN libapics USING(AvtorId) 93 | WHERE AvtorId=:id"); 94 | $stmt->bindParam(":id", $_SESSION['filter_author']); 95 | $stmt->execute(); 96 | $a = $stmt->fetch(); 97 | 98 | $fcontent .= "
    "; 99 | if ($a->file != '') { 100 | $fcontent .= ""; 101 | } 102 | $fcontent .= "$a->lastname $a->firstname $a->middlename $a->nickname
    "; 103 | } 104 | 105 | if (isset($_SESSION['filter_genre'])) { 106 | $filter .= 'AND g.genreid=:gid '; 107 | $join .= 'LEFT JOIN libgenre g USING(BookId) '; 108 | $stmt = $dbh->prepare("SELECT * FROM libgenrelist 109 | WHERE genreid=:id"); 110 | $stmt->bindParam(":id", $_SESSION['filter_genre']); 111 | $stmt->execute(); 112 | $g = $stmt->fetch(); 113 | 114 | $fcontent .= "
    "; 115 | $fcontent .= "$g->genremeta: $g->genredesc
    "; 116 | } 117 | 118 | if (isset($_SESSION['filter_xgenre'])) { 119 | $filter .= 'AND (SELECT COUNT(*) FROM libgenre xg WHERE xg.BookId=B.BookId AND xg.genreid=:xgid) = 0'; 120 | $stmt = $dbh->prepare("SELECT * FROM libgenrelist 121 | WHERE genreid=:id"); 122 | $stmt->bindParam(":id", $_SESSION['filter_xgenre']); 123 | $stmt->execute(); 124 | $xg = $stmt->fetch(); 125 | 126 | $fcontent .= "
    "; 127 | $fcontent .= "$xg->genremeta: $xg->genredesc
    "; 128 | } 129 | 130 | 131 | if (isset($_SESSION['filter_series'])) { 132 | $do_cnt = true; 133 | $cols = 's.seqnumb,'; 134 | $filter .= 'AND seqid=:sid '; 135 | $join .= 'LEFT JOIN libseq s USING(BookId) '; 136 | $stmt = $dbh->prepare("SELECT * FROM libseqname 137 | WHERE seqid=:id"); 138 | $stmt->bindParam(":id", $_SESSION['filter_series']); 139 | $stmt->execute(); 140 | $s = $stmt->fetch(); 141 | 142 | $fcontent .= "
    "; 143 | $fcontent .= "$s->seqname
    "; 144 | $order = "s.seqnumb, $order"; 145 | $seqname = $s->seqname; 146 | $seqid = $_SESSION['filter_series']; 147 | } 148 | 149 | if (isset($_SESSION['search'])) { 150 | $filter .= "AND vector @@ to_tsquery('russian', :search) "; 151 | $join .= 'LEFT JOIN libbook_ts USING(bookid) '; 152 | 153 | $fcontent .= "
    "; 154 | $fcontent .= "" . $_SESSION['search'] . "
    "; 155 | } 156 | 157 | if (isset($_SESSION['filter_series'])) { 158 | $fcontent .= "$seqname в Избранное "; 159 | } 160 | 161 | echo "
    "; 162 | echo "
    "; 163 | ?> 164 | 165 |
    166 | 167 |
    168 | 169 | 170 |
    171 |
    172 |
    173 | "; 177 | 178 | 179 | $sql = "SELECT *, $cols 180 | (SELECT Body FROM libbannotations WHERE BookId=b.BookId LIMIT 1) Body 181 | FROM libbook b 182 | $join 183 | WHERE deleted='0' 184 | $filter 185 | ORDER BY $order LIMIT " . RECORDS_PAGE . " OFFSET $start"; 186 | 187 | //echo "$sql"; 188 | 189 | $stmt = $dbh->prepare($sql); 190 | 191 | if (isset($_SESSION['filter_author'])) { 192 | $stmt->bindParam(":aid", $_SESSION['filter_author']); 193 | } 194 | if (isset($_SESSION['filter_genre'])) { 195 | $stmt->bindParam(":gid", $_SESSION['filter_genre']); 196 | } 197 | if (isset($_SESSION['filter_xgenre'])) { 198 | $stmt->bindParam(":xgid", $_SESSION['filter_xgenre']); 199 | } 200 | if (isset($_SESSION['filter_series'])) { 201 | $stmt->bindParam(":sid", $_SESSION['filter_series']); 202 | } 203 | if (isset($_SESSION['search'])) { 204 | $stmt->bindParam(":search", $_SESSION['search']); 205 | } 206 | 207 | 208 | try { 209 | $stmt->execute(); 210 | } catch (Exception $e) { 211 | $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); 212 | header($protocol . ' 504 Gateway Time-out'); 213 | 214 | echo "
    База данных
    "; 215 | echo "

    " . $e->getMessage() . "

    "; 216 | echo "

    Попробуйте упростить параметры поиска, убрать часть тэгов, направленность. Сервер маленький ^^.

    "; 217 | echo "
    "; 218 | } 219 | 220 | if (COUNT_BOOKS) { 221 | $sql = "SELECT COUNT(*) cnt 222 | FROM libbook b 223 | $join 224 | WHERE deleted='0' 225 | $filter"; 226 | $stt = $dbh->prepare($sql); 227 | 228 | if (isset($_SESSION['filter_author'])) { 229 | $stt->bindParam(":aid", $_SESSION['filter_author']); 230 | } 231 | if (isset($_SESSION['filter_genre'])) { 232 | $stt->bindParam(":gid", $_SESSION['filter_genre']); 233 | } 234 | if (isset($_SESSION['filter_xgenre'])) { 235 | $stt->bindParam(":xgid", $_SESSION['filter_xgenre']); 236 | } 237 | if (isset($_SESSION['filter_series'])) { 238 | $stt->bindParam(":sid", $_SESSION['filter_series']); 239 | } 240 | if (isset($_SESSION['search'])) { 241 | $stt->bindParam(":search", $_SESSION['search']); 242 | } 243 | 244 | $stt->execute(); 245 | $cnt = $stt->fetch()->cnt; 246 | echo "Найдено: $cnt "; 247 | } else { 248 | $cnt = 2000; 249 | } 250 | 251 | $rcnt = $stmt->rowCount(); 252 | if ($rcnt < RECORDS_PAGE) { 253 | $cnt = $page * RECORDS_PAGE + $rcnt; 254 | } 255 | 256 | show_gpager(ceil($cnt / RECORDS_PAGE), 5); 257 | 258 | $c = 0; 259 | while ($book = $stmt->fetch()) { 260 | $c++; 261 | if ($c > 10) { 262 | break; 263 | } 264 | book_info_pg($book, $webroot); 265 | } 266 | 267 | show_gpager(ceil($cnt / RECORDS_PAGE), 5); 268 | 269 | -------------------------------------------------------------------------------- /application/modules/primary/module.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/modules/primary/module.conf -------------------------------------------------------------------------------- /application/modules/series/index.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | "; 35 | foreach (range(chr(0xC0), chr(0xDF)) as $b) { 36 | $l = iconv('CP1251', 'UTF-8', $b); 37 | if ($l == mb_strtoupper($get)) { 38 | $cc = 'active'; 39 | } else { 40 | $cc = ''; 41 | } 42 | echo "
  • $l
  • "; 43 | } 44 | echo ""; 45 | echo ""; 57 | 58 | 59 | echo "
    \n"; 60 | ?> 61 |
    62 | 63 |
    64 | 65 | 66 |
    67 |
    68 |
    69 | 70 | prepare("SELECT COUNT(*) cnt FROM libseqname WHERE lower(libseqname.SeqName) LIKE :letter"); 74 | $stmt->bindParam(":letter", $letter); 75 | $stmt->execute(); 76 | $cnt = $stmt->fetch()->cnt; 77 | 78 | $stmt = $dbh->prepare("SELECT SeqName, SeqId, 79 | (SELECT COUNT(*) FROM libseq WHERE libseq.SeqId=libseqname.SeqId) cnt 80 | FROM libseqname 81 | WHERE LOWER(libseqname.SeqName) LIKE :letter 82 | ORDER BY seqname LIMIT " . SERIES_PAGE . " OFFSET $start"); 83 | $stmt->bindParam(":letter", $letter); 84 | $stmt->execute(); 85 | 86 | echo '
    '; 87 | show_gpager(ceil($cnt / SERIES_PAGE), 5); 88 | while ($bs = $stmt->fetch()) { 89 | if ($bs->cnt > 0) { 90 | echo "
    $bs->seqname$bs->cnt
    "; 91 | } 92 | } 93 | echo "
    "; 94 | 95 | show_gpager(ceil($cnt / SERIES_PAGE), 5); 96 | 97 | -------------------------------------------------------------------------------- /application/modules/series/module.conf: -------------------------------------------------------------------------------- 1 | title = 'Жанры'; 3 | -------------------------------------------------------------------------------- /application/modules/service/index.php: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |
    5 |

    Статистика

    6 |
    7 | query("SELECT (SELECT MAX(time) FROM libbook) mmod, (SELECT COUNT(*) FROM libbook) bcnt, (SELECT COUNT(*) FROM libbook WHERE deleted='0') bdcnt"); 22 | $qtotal->execute(); 23 | $total = $qtotal->fetch(); 24 | echo ""; 25 | echo ""; 26 | echo ""; 27 | echo ""; 28 | echo ""; 29 | echo "
    Актуальность базы:$total->mmod
    Всего произведений:$total->bcnt
    Размер архива:$books_size Gb
    Размер кэша:$cache_size Mb
    "; 30 | } else { 31 | echo "Идёт процесс импорта..."; 32 | } 33 | ?> 34 |
    35 |
    36 |
    37 | 38 |
    39 |
    40 |

    Операции

    41 |
    42 | /dev/null >/dev/null &'); 54 | $status_fetch = true; 55 | header("location:$webroot/service/"); 56 | } 57 | if (isset($_GET['reindex'])) { 58 | shell_exec('stdbuf -o0 /application/tools/app_reindex.sh 2>/dev/null >/dev/null &'); 59 | $status_fetch = true; 60 | header("location:$webroot/service/"); 61 | } 62 | } 63 | 64 | if ($status_import) { 65 | $status = 'disabled'; 66 | } else { 67 | $status = ''; 68 | } 69 | echo "
    "; 70 | echo "Обновить базу "; 71 | echo "Очистить кэш "; 72 | echo "Сканирование ZIP "; 73 | echo "
    "; 74 | 75 | if ($status_import) { 76 | $op = file_get_contents('/application/sql/status');; 77 | echo "
    "; 78 | echo nl2br($op); 79 | echo "
    "; 80 | header("Refresh:10"); 81 | } 82 | 83 | ?> 84 |
    85 |
    86 |
    87 | 88 |
    89 | 90 |
    91 |
    92 |
    93 |
    94 |

    95 | Для выполнения обновления необходимо разместить фалы дампа Флибусты (*.sql) в каталог FlibustaSQL. Процесс занимает до 30 минут, в зависимости от быстродействия сервера (SSD значительно увеличивает скорость импорта) 96 |

    97 |

    98 | Чтобы отображались фото авторов и обложек для форматов, отличных от FB2, необходимо разместить в каталоге cache файлы архивов lib.a.attached.zip и lib.b.attached.zip соответственно. 99 | В кэше хранятся распакованные фото авторов и обложек для FB2, а также их уменьшенные версии.

    100 |

    Файлы архивов Флибусты (*.zip) необходимо размещать в каталоге Flibusta.Net. Обрабатываются также файлы ежедневных обновлений, но обязательно необходимо подгружать свежие SQL файлы.

    101 | Доступен также OPDS-каталог для читалок: /opds/

    "; ?> 102 |

    Каталоги FlibustaSQL, cache и их подкаталоги должны иметь права на запись для контейнера. Скрипты в каталоге /application/tools/ должны иметь права на выполнение.

    103 |
    104 | 105 | -------------------------------------------------------------------------------- /application/modules/service/module.conf: -------------------------------------------------------------------------------- 1 | title = 'Сервис и настройки'; 5 | -------------------------------------------------------------------------------- /application/none.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/none.jpg -------------------------------------------------------------------------------- /application/opds/author.php: -------------------------------------------------------------------------------- 1 | ';echo "\n"; 4 | echo ''; 5 | 6 | $author_id = $_GET['author_id']; 7 | if ($author_id == '') 8 | die('author.php called without specifying id'); 9 | 10 | $seq_mode = isset($_GET['seq']); 11 | if (! $seq_mode) { 12 | $stmt = $dbh->prepare("SELECT a.LastName as LastName, a.MiddleName as MiddleName, a.FirstName as FirstName, a.NickName as NickName, 13 | aa.Body as Body, p.File as picFile 14 | from libavtorname a 15 | LEFT JOIN libaannotations aa on a.avtorid = aa.avtorid 16 | LEFT JOIN libapics p on a.avtorid=p.avtorid 17 | where a.avtorID=:authorid "); 18 | } else { 19 | $stmt = $dbh->prepare("SELECT LastName, MiddleName, FirstName, NickName from libavtorname where avtorID=:authorid "); 20 | } 21 | 22 | $stmt->bindParam(':authorid', $author_id); 23 | $stmt->execute(); 24 | if ($a = $stmt->fetchObject()){ 25 | $author_name = ($a->nickname !='')?"$a->firstname $a->middlename $a->lastname ($a->nickname)" 26 | :"$a->firstname $a->middlename $a->lastname"; 27 | 28 | if ($seq_mode) { // show list of sequences with current author's works 29 | 30 | echo <<<_XML 31 | tag:author:$author_id:sequences 32 | $author_name : Книги по сериям 33 | $cdt 34 | /favicon.ico 35 | 36 | 37 | 38 | _XML; 39 | $sequences = $dbh->prepare("SELECT distinct sn.seqid seqid, sn.seqname seqname 40 | from libseqname sn, libseq s, libavtor a 41 | where sn.seqid = s.seqid and s.bookId= a.bookId and a.avtorId= :aid"); 42 | $sequences->bindParam(":aid", $author_id); 43 | $sequences->execute(); 44 | while($seq = $sequences->fetchObject()){ 45 | echo "\n"; 46 | echo "$cdt\n"; 47 | echo "" . htmlspecialchars($seq->seqname ?? '') . "\n"; 48 | echo "\n"; 49 | echo "\n"; 50 | } 51 | $sequences = null; 52 | } else { 53 | echo <<<_XML 54 | tag:author:$author_id 55 | $author_name 56 | $cdt 57 | /favicon.ico 58 | 59 | 60 | 61 | 62 | _XML; 63 | if ($a->body != '') { 64 | echo <<<_XML 65 | 66 | $cdt 67 | tag:author:bio:$author_id 68 | Об авторе 69 | _XML; 70 | if (!is_null($a->picfile)){ 71 | echo <<<_XML 72 | 73 | 74 | 75 | 76 | 77 | _XML; 78 | } 79 | //echo ''; 80 | echo ''; 81 | /* if (!is_null($a->picfile)){ 82 | echo "<img src="/extract_author.php?id=$author_id" align=left style="border:5px solid #ededed; margin: 12px;"><p>\n"; 83 | }*/ 84 | echo $a->body; 85 | echo "\n"; 86 | echo <<< _XML 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | _XML; 95 | } 96 | echo <<< _XML 97 | 98 | $cdt 99 | Все книги автора (без сортировки) 100 | tag:author:$author_id:list 101 | 102 | 103 | 104 | $cdt 105 | Книги автора по алфавиту 106 | tag:author:$author_id:alphabet 107 | 108 | 109 | 110 | $cdt 111 | Книги автора по году издания 112 | tag:author:$author_id:year 113 | 114 | 115 | 116 | $cdt 117 | Книжные серии с произведениями автора 118 | tag:author:$author_id:sequences 119 | 120 | 121 | 122 | $cdt 123 | Произведения вне серий 124 | tag:author:$author_id:sequenceless 125 | 126 | 127 | _XML; 128 | } 129 | } 130 | else 131 | die("author with id $author_id not found in the data base"); 132 | $stmt = null; 133 | ?> 134 | 135 | -------------------------------------------------------------------------------- /application/opds/authorsindex.php: -------------------------------------------------------------------------------- 1 | '; 13 | echo <<< _XML 14 | tag:root:authors 15 | Книги по авторам 16 | $cdt 17 | /favicon.ico 18 | 19 | 20 | \n 21 | _XML; 22 | 23 | $query=" 24 | SELECT UPPER(SUBSTR(LastName, 1, ".($length_letters + 1).")) as alpha, COUNT(*) as cnt 25 | FROM libavtorname 26 | WHERE UPPER(SUBSTR(LastName, 1, ".($length_letters + 1).")) SIMILAR TO '".$letters."[A-ZА-Я]' 27 | GROUP BY UPPER(SUBSTR(LastName, 1, ".($length_letters + 1).")) 28 | ORDER BY alpha"; 29 | $ai = $dbh->query($query); 30 | while ($ach = $ai->fetchObject()) { 31 | echo "\n $cdt"; 32 | echo "tag:authors:$ach->alpha"; 33 | echo "$ach->alpha"; 34 | echo "$ach->cnt авторов на $ach->alpha"; 35 | if ($ach->cnt>500) { 36 | $url="$webroot/opds/authorsindex?letters=$ach->alpha"; 37 | } else { 38 | $url="$webroot/opds/search?by=author&q=$ach->alpha"; 39 | } 40 | echo ""; 41 | echo ""; 42 | } 43 | echo ''; 44 | ?> -------------------------------------------------------------------------------- /application/opds/fav.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo <<< _XML 5 | 6 | tag:root:home 7 | Книги по авторам 8 | _XML; 9 | echo "$cdt"; 10 | echo <<< _XML 11 | /favicon.ico 12 | 13 | 14 | 15 | _XML; 16 | echo "'; 17 | 18 | $uuid = $_GET['uuid']; 19 | $books = $dbh->prepare("SELECT DISTINCT b.* 20 | FROM fav f 21 | LEFT JOIN libbook b USING(bookid) 22 | WHERE user_uuid=:uuid AND f.bookid IS NOT NULL"); 23 | $books->bindParam(":uuid", $uuid); 24 | $books->execute(); 25 | 26 | while ($b = $books->fetch()) { 27 | opds_book($b, $webroot); 28 | } 29 | 30 | echo ""; 31 | ?> 32 | -------------------------------------------------------------------------------- /application/opds/favs.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo <<< _XML 5 | tag:root 6 | Книжные полки 7 | _XML; 8 | echo "$cdt"; 9 | echo <<< _XML 10 | /favicon.ico 11 | 12 | 13 | 14 | 15 | _XML; 16 | 17 | $favs = $dbh->prepare("SELECT * FROM fav_users"); 18 | $favs->execute(); 19 | 20 | while ($fav = $favs->fetch()) { 21 | echo " $cdt"; 22 | echo " tag:fav:$fav->user_uuid"; 23 | echo " $fav->name"; 24 | echo " "; 25 | echo " "; 26 | echo ""; 27 | } 28 | 29 | echo ''; 30 | ?> 31 | -------------------------------------------------------------------------------- /application/opds/genres.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo <<< _XML 5 | tag:root 6 | Категории жанров 7 | _XML; 8 | echo "$cdt"; 9 | echo <<< _XML 10 | /favicon.ico 11 | 12 | 13 | 14 | 15 | _XML; 16 | $gs = $dbh->prepare("SELECT DISTINCT(genremeta) genre FROM libgenrelist ORDER BY genre"); 17 | $gs->execute(); 18 | 19 | while ($g = $gs->fetch()) { 20 | echo " $cdt"; 21 | echo " tag:genre:" . urlencode($g->genre) . ""; 22 | echo " $g->genre"; 23 | echo " "; 24 | echo " "; 25 | echo "\n"; 26 | } 27 | echo ""; 28 | ?> 29 | 30 | -------------------------------------------------------------------------------- /application/opds/index.php: -------------------------------------------------------------------------------- 1 | action) { 3 | case 'list': 4 | include('list.php'); 5 | break; 6 | case 'authorsindex': 7 | include('authorsindex.php'); 8 | break; 9 | case 'author': 10 | include('author.php'); 11 | break; 12 | case 'sequencesindex': 13 | include('sequencesindex.php'); 14 | break; 15 | case 'genres': 16 | include('genres.php'); 17 | break; 18 | case 'listgenres': 19 | include('listgenres.php'); 20 | break; 21 | case 'fav': 22 | include('fav.php'); 23 | break; 24 | case 'favs': 25 | include('favs.php'); 26 | break; 27 | case 'search': 28 | include('search.php'); 29 | break; 30 | 31 | default: 32 | include('main.php'); 33 | } 34 | -------------------------------------------------------------------------------- /application/opds/list.php: -------------------------------------------------------------------------------- 1 | '; 4 | 5 | $filter = "deleted='0' "; 6 | $join = ''; 7 | 8 | $orderby = ' time DESC '; 9 | 10 | $title = 'в новинках'; 11 | 12 | if (isset($_GET['genre_id'])) { 13 | $gid = intval($_GET['genre_id']); 14 | $filter .= 'AND genreid=:gid '; 15 | $join .= 'LEFT JOIN libgenre g USING(BookId) '; 16 | $orderby = ' time DESC '; 17 | $stmt = $dbh->prepare("SELECT * FROM libgenrelist 18 | WHERE genreid=:gid"); 19 | $stmt->bindParam(":gid", $gid); 20 | $stmt->execute(); 21 | $g = $stmt->fetch(); 22 | $title = "в $g->genremeta: $g->genredesc"; 23 | } 24 | 25 | if (isset($_GET['seq_id'])) { 26 | $sid = intval($_GET['seq_id']); 27 | $filter .= 'AND seqid=:sid'; 28 | $join .= 'LEFT JOIN libseq s USING(BookId) '; 29 | $orderby = " s.seqnumb "; 30 | $stmt = $dbh->prepare("SELECT * FROM libseqname 31 | WHERE seqid=:sid"); 32 | $stmt->bindParam(":sid", $sid); 33 | $stmt->execute(); 34 | $s = $stmt->fetch(); 35 | $title = "в сборнике $s->seqname"; 36 | } 37 | 38 | if (isset($_GET['author_id'])) { 39 | $aid = intval($_GET['author_id']); 40 | $filter .= 'AND avtorid=:aid '; 41 | $join .= 'JOIN libavtor USING (bookid) JOIN libavtorname USING (avtorid) '; 42 | 43 | $display_type = (isset($_GET['display_type']))? ($_GET['display_type'] ?? '') : ''; 44 | if ($display_type == 'sequenceless') { 45 | $filter .= 'AND s.seqid is null '; 46 | $join .= ' LEFT JOIN libseq s ON s.bookId= b.bookId '; 47 | $orderby = ' time DESC '; 48 | } else if ($display_type == 'year'){ 49 | $orderby = ' year '; 50 | } else if ($display_type == 'alphabet') { 51 | $orderby = ' title '; 52 | } else { 53 | $orderby = ' time DESC '; 54 | } 55 | $stmt = $dbh->prepare("SELECT * FROM libavtorname WHERE avtorid=:aid"); 56 | $stmt->bindParam(":aid", $aid); 57 | $stmt->execute(); 58 | $a = $stmt->fetch(); 59 | $title = ($a->nickname !='')?"$a->firstname $a->middlename $a->lastname ($a->nickname)" 60 | :"$a->firstname $a->middlename $a->lastname"; 61 | } 62 | 63 | echo <<< _XML 64 | 65 | tag:root:home 66 | _XML; 67 | echo "Книги $title"; 68 | echo "$cdt"; 69 | echo <<< _XML 70 | /favicon.ico 71 | 72 | 73 | \n 74 | _XML; 75 | 76 | $books = $dbh->prepare("SELECT b.* 77 | FROM libbook b 78 | $join 79 | WHERE 80 | $filter 81 | ORDER BY $orderby 82 | LIMIT ". OPDS_FEED_COUNT); 83 | 84 | if (isset($_GET['genre_id'])) { 85 | $books->bindParam(":gid", $gid); 86 | } 87 | 88 | if (isset($_GET['seq_id'])) { 89 | $books->bindParam(":sid", $sid); 90 | } 91 | 92 | if (isset($_GET['author_id'])) { 93 | $books->bindParam(":aid", $aid); 94 | } 95 | 96 | $books->execute(); 97 | 98 | while ($b = $books->fetch()) { 99 | opds_book($b, $webroot); 100 | } 101 | 102 | echo ""; 103 | ?> -------------------------------------------------------------------------------- /application/opds/listgenres.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo ' tag:root'; 5 | echo "Жанры в ".$_GET['id'].""; 6 | echo "$cdt"; 7 | echo <<< _XML 8 | /favicon.ico 9 | 10 | 11 | 12 | 13 | _XML; 14 | 15 | $gs = $dbh->prepare("SELECT *, 16 | (SELECT COUNT(*) FROM libgenre WHERE libgenre.genreid=g.genreid) cnt 17 | FROM libgenrelist g 18 | WHERE g.genremeta=:id"); 19 | $gs->bindParam(":id", $_GET['id']); 20 | $gs->execute(); 21 | 22 | while ($g = $gs->fetch()) { 23 | echo " $cdt"; 24 | echo " tag:genre:$g->genrecode"; 25 | echo " $g->genredesc"; 26 | echo " Книг: $g->cnt"; 27 | echo " "; 28 | echo "\n"; 29 | } 30 | echo ''; 31 | ?> 32 | 33 | -------------------------------------------------------------------------------- /application/opds/main.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo <<< _XML 5 | 6 | 7 | tag:root 8 | Домашняя библиотека 9 | /favicon.ico 10 | 11 | 12 | 13 | 14 | 15 | $cdt 16 | tag:root:new 17 | Новинки 18 | Последние поступления в библиотеку 19 | 20 | 21 | 22 | 23 | $cdt 24 | tag:root:shelf 25 | Книжные полки 26 | Избранное 27 | 28 | 29 | $cdt 30 | tag:root:genre 31 | По жанрам 32 | Поиск книг по жанрам 33 | 34 | 35 | $cdt 36 | tag:root:authors 37 | По авторам 38 | Поиск книг по авторам 39 | 40 | 41 | $cdt 42 | tag:root:sequences 43 | По сериям 44 | Поиск книг по сериям 45 | 46 | 47 | _XML 48 | ?> 49 | -------------------------------------------------------------------------------- /application/opds/search.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/opds/search_author.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo <<< _XML 5 | tag:root:authors 6 | Поиск по авторам 7 | $cdt 8 | /favicon.ico 9 | 10 | 11 | 12 | 13 | 14 | $cdt 15 | tag:search:author 16 | Поиск авторов 17 | Поиск авторов по фамилии 18 | 19 | 20 | _XML; 21 | 22 | $q = $_GET['q']; 23 | 24 | if ($q == '') { 25 | die(':('); 26 | } 27 | $queryParam = $q . '%'; 28 | $authors = $dbh->prepare("SELECT *, 29 | (SELECT COUNT(*) FROM libavtor, libbook WHERE 30 | libbook.deleted='0' AND 31 | libbook.bookid=libavtor.bookid AND 32 | libavtor.avtorid=libavtorname.avtorid) cnt 33 | FROM libavtorname 34 | WHERE lastname ILIKE :q ORDER BY lastname, firstname"); 35 | $authors->bindParam(":q", $queryParam); 36 | $authors->execute(); 37 | while ($a = $authors->fetch()) { 38 | if ($a->cnt > 0) { 39 | echo "\n $cdt"; 40 | echo " tag:author:$a->avtorid"; 41 | echo " $a->lastname $a->firstname $a->middlename $a->nickname"; 42 | 43 | $stmt = $dbh->query("SELECT COUNT(*) as cnt FROM libbook,libavtor WHERE deleted='0' AND libavtor.bookid=libbook.bookid AND libavtor.avtorid=$a->avtorid"); 44 | $stmt->execute(); 45 | $books_cnt = $stmt->fetch()->cnt; 46 | $stmt = null; 47 | echo " $books_cnt книг"; 48 | echo " "; 49 | echo ''; 50 | } 51 | } 52 | 53 | 54 | /* 55 | echo "
    "; 56 | echo '
    '; 57 | $seqs = DB::query("SELECT *, 58 | (SELECT COUNT(*) FROM libseq WHERE libseq.seqid=libseqname.SeqId) cnt 59 | FROM libseqname $filter4 ORDER BY seqname"); 60 | 61 | while ($s = $seqs->fetch_object()) { 62 | if ($s->cnt > 0) { 63 | echo "
    "; 64 | 65 | echo "SeqId'\">"; 66 | 67 | echo "$s->SeqName"; 68 | 69 | 70 | 71 | echo "$s->cnt"; 72 | 73 | echo ""; 74 | 75 | echo "
    "; 76 | } 77 | } 78 | */ 79 | ?> 80 | -------------------------------------------------------------------------------- /application/opds/search_book.php: -------------------------------------------------------------------------------- 1 | '; 4 | echo <<< _XML 5 | tag:root:authors 6 | Поиск по книгам 7 | $cdt 8 | /favicon.ico 9 | 10 | 11 | 12 | _XML; 13 | 14 | $q = $_GET['q']; 15 | $get = "?q=$q"; 16 | 17 | if ($q == '') { 18 | die(':('); 19 | } 20 | 21 | //$filter2 = "AND libbook.Title LIKE " . DB::es('%' . $q . '%'); 22 | 23 | $books = $dbh->prepare("SELECT DISTINCT BookId, libbook.Title as BookTitle, 24 | (SELECT Body FROM libbannotations WHERE BookId=libbook.BookId LIMIT 1) as Body 25 | FROM libbook 26 | JOIN libgenre USING(BookId) 27 | WHERE deleted='0' AND libbook.Title LIKE :q 28 | GROUP BY BookId, BookTitle, Body 29 | LIMIT 100"); 30 | $param = '%'.$q.'%'; 31 | $books->bindParam(":q", $param); 32 | $books->execute(); 33 | 34 | while ($b = $books->fetchObject()) { 35 | echo " $cdt"; 36 | echo " tag:book:$b->bookid"; 37 | echo " " . htmlspecialchars($b->booktitle) . ""; 38 | 39 | $as = ''; 40 | $authors = $dbh->query("SELECT lastname, firstname, middlename FROM libavtorname, libavtor WHERE libavtor.BookId=$b->bookid AND libavtor.AvtorId=libavtorname.AvtorId ORDER BY LastName"); 41 | while ($a = $authors->fetchObject()) { 42 | $as .= $a->lastname . " " . $a->firstname . " " . $a->middlename . ", "; 43 | } 44 | $authors = null; 45 | 46 | echo " $as"; 47 | echo " /a/id"; 48 | echo ""; 49 | echo " " . htmlspecialchars($b->body ?? '') . ""; 50 | 51 | echo ""; 52 | echo ""; 53 | echo " "; 54 | 55 | echo "\n"; 56 | } 57 | $books = null; 58 | ?> 59 | -------------------------------------------------------------------------------- /application/opds/sequencesindex.php: -------------------------------------------------------------------------------- 1 | '; 13 | echo <<< _XML 14 | tag:root:authors 15 | Книги по сериям 16 | $cdt 17 | /favicon.ico 18 | 19 | 20 | \n 21 | _XML; 22 | 23 | $query=" 24 | SELECT UPPER(SUBSTR(SeqName, 1, ".($length_letters + 1).")) as alpha, COUNT(*) as cnt 25 | FROM libseqname 26 | WHERE UPPER(SUBSTR(SeqName, 1, ".($length_letters + 1).")) SIMILAR TO :pattern 27 | GROUP BY UPPER(SUBSTR(SeqName, 1, ".($length_letters + 1).")) 28 | ORDER BY alpha"; 29 | $ai = $dbh->prepare($query); 30 | $bindparam1 = $letters."_"; 31 | $ai->bindParam(":pattern",$bindparam1); 32 | $ai->execute(); 33 | while ($ach = $ai->fetchObject()) { 34 | if ($ach->cnt>30) { 35 | echo "\n\n $cdt"; 36 | echo "tag:sequences:".urlencode($ach->alpha)."\n"; 37 | echo "".htmlspecialchars($ach->alpha)."\n"; 38 | echo "$ach->cnt книжных серий на ".htmlspecialchars($ach->alpha)."\n"; 39 | echo "\n"; 40 | echo ""; 41 | } else { 42 | // list individual serie 43 | $sq = $dbh->prepare("SELECT SeqName, SeqId 44 | from libseqname 45 | where UPPER(SUBSTR(SeqName, 1, ".($length_letters + 1).")) = :pattern 46 | ORDER BY UPPER(SeqName)"); 47 | $sq->bindParam(":pattern",$ach->alpha); 48 | $sq->execute(); 49 | while($s = $sq->fetchObject()){ 50 | echo "\n\n $cdt"; 51 | echo "tag:sequence:$s->seqid\n"; 52 | echo "". htmlspecialchars($s->seqname)."\n"; 53 | echo " "; 54 | echo " "; 55 | echo ""; 56 | } 57 | $sq = null; 58 | } 59 | } 60 | echo ''; 61 | ?> -------------------------------------------------------------------------------- /application/public/bootstrap/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | :root { 9 | --bs-blue: #0d6efd; 10 | --bs-indigo: #6610f2; 11 | --bs-purple: #6f42c1; 12 | --bs-pink: #d63384; 13 | --bs-red: #dc3545; 14 | --bs-orange: #fd7e14; 15 | --bs-yellow: #ffc107; 16 | --bs-green: #198754; 17 | --bs-teal: #20c997; 18 | --bs-cyan: #0dcaf0; 19 | --bs-white: #fff; 20 | --bs-gray: #6c757d; 21 | --bs-gray-dark: #343a40; 22 | --bs-gray-100: #f8f9fa; 23 | --bs-gray-200: #e9ecef; 24 | --bs-gray-300: #dee2e6; 25 | --bs-gray-400: #ced4da; 26 | --bs-gray-500: #adb5bd; 27 | --bs-gray-600: #6c757d; 28 | --bs-gray-700: #495057; 29 | --bs-gray-800: #343a40; 30 | --bs-gray-900: #212529; 31 | --bs-primary: #0d6efd; 32 | --bs-secondary: #6c757d; 33 | --bs-success: #198754; 34 | --bs-info: #0dcaf0; 35 | --bs-warning: #ffc107; 36 | --bs-danger: #dc3545; 37 | --bs-light: #f8f9fa; 38 | --bs-dark: #212529; 39 | --bs-primary-rgb: 13, 110, 253; 40 | --bs-secondary-rgb: 108, 117, 125; 41 | --bs-success-rgb: 25, 135, 84; 42 | --bs-info-rgb: 13, 202, 240; 43 | --bs-warning-rgb: 255, 193, 7; 44 | --bs-danger-rgb: 220, 53, 69; 45 | --bs-light-rgb: 248, 249, 250; 46 | --bs-dark-rgb: 33, 37, 41; 47 | --bs-white-rgb: 255, 255, 255; 48 | --bs-black-rgb: 0, 0, 0; 49 | --bs-body-color-rgb: 33, 37, 41; 50 | --bs-body-bg-rgb: 255, 255, 255; 51 | --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 52 | --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 53 | --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); 54 | --bs-body-font-family: var(--bs-font-sans-serif); 55 | --bs-body-font-size: 1rem; 56 | --bs-body-font-weight: 400; 57 | --bs-body-line-height: 1.5; 58 | --bs-body-color: #212529; 59 | --bs-body-bg: #fff; 60 | } 61 | 62 | *, 63 | *::before, 64 | *::after { 65 | box-sizing: border-box; 66 | } 67 | 68 | @media (prefers-reduced-motion: no-preference) { 69 | :root { 70 | scroll-behavior: smooth; 71 | } 72 | } 73 | 74 | body { 75 | margin: 0; 76 | font-family: var(--bs-body-font-family); 77 | font-size: var(--bs-body-font-size); 78 | font-weight: var(--bs-body-font-weight); 79 | line-height: var(--bs-body-line-height); 80 | color: var(--bs-body-color); 81 | text-align: var(--bs-body-text-align); 82 | background-color: var(--bs-body-bg); 83 | -webkit-text-size-adjust: 100%; 84 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 85 | } 86 | 87 | hr { 88 | margin: 1rem 0; 89 | color: inherit; 90 | background-color: currentColor; 91 | border: 0; 92 | opacity: 0.25; 93 | } 94 | 95 | hr:not([size]) { 96 | height: 1px; 97 | } 98 | 99 | h6, h5, h4, h3, h2, h1 { 100 | margin-top: 0; 101 | margin-bottom: 0.5rem; 102 | font-weight: 500; 103 | line-height: 1.2; 104 | } 105 | 106 | h1 { 107 | font-size: calc(1.375rem + 1.5vw); 108 | } 109 | @media (min-width: 1200px) { 110 | h1 { 111 | font-size: 2.5rem; 112 | } 113 | } 114 | 115 | h2 { 116 | font-size: calc(1.325rem + 0.9vw); 117 | } 118 | @media (min-width: 1200px) { 119 | h2 { 120 | font-size: 2rem; 121 | } 122 | } 123 | 124 | h3 { 125 | font-size: calc(1.3rem + 0.6vw); 126 | } 127 | @media (min-width: 1200px) { 128 | h3 { 129 | font-size: 1.75rem; 130 | } 131 | } 132 | 133 | h4 { 134 | font-size: calc(1.275rem + 0.3vw); 135 | } 136 | @media (min-width: 1200px) { 137 | h4 { 138 | font-size: 1.5rem; 139 | } 140 | } 141 | 142 | h5 { 143 | font-size: 1.25rem; 144 | } 145 | 146 | h6 { 147 | font-size: 1rem; 148 | } 149 | 150 | p { 151 | margin-top: 0; 152 | margin-bottom: 1rem; 153 | } 154 | 155 | abbr[title], 156 | abbr[data-bs-original-title] { 157 | -webkit-text-decoration: underline dotted; 158 | text-decoration: underline dotted; 159 | cursor: help; 160 | -webkit-text-decoration-skip-ink: none; 161 | text-decoration-skip-ink: none; 162 | } 163 | 164 | address { 165 | margin-bottom: 1rem; 166 | font-style: normal; 167 | line-height: inherit; 168 | } 169 | 170 | ol, 171 | ul { 172 | padding-left: 2rem; 173 | } 174 | 175 | ol, 176 | ul, 177 | dl { 178 | margin-top: 0; 179 | margin-bottom: 1rem; 180 | } 181 | 182 | ol ol, 183 | ul ul, 184 | ol ul, 185 | ul ol { 186 | margin-bottom: 0; 187 | } 188 | 189 | dt { 190 | font-weight: 700; 191 | } 192 | 193 | dd { 194 | margin-bottom: 0.5rem; 195 | margin-left: 0; 196 | } 197 | 198 | blockquote { 199 | margin: 0 0 1rem; 200 | } 201 | 202 | b, 203 | strong { 204 | font-weight: bolder; 205 | } 206 | 207 | small { 208 | font-size: 0.875em; 209 | } 210 | 211 | mark { 212 | padding: 0.2em; 213 | background-color: #fcf8e3; 214 | } 215 | 216 | sub, 217 | sup { 218 | position: relative; 219 | font-size: 0.75em; 220 | line-height: 0; 221 | vertical-align: baseline; 222 | } 223 | 224 | sub { 225 | bottom: -0.25em; 226 | } 227 | 228 | sup { 229 | top: -0.5em; 230 | } 231 | 232 | a { 233 | color: #0d6efd; 234 | text-decoration: underline; 235 | } 236 | a:hover { 237 | color: #0a58ca; 238 | } 239 | 240 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 241 | color: inherit; 242 | text-decoration: none; 243 | } 244 | 245 | pre, 246 | code, 247 | kbd, 248 | samp { 249 | font-family: var(--bs-font-monospace); 250 | font-size: 1em; 251 | direction: ltr /* rtl:ignore */; 252 | unicode-bidi: bidi-override; 253 | } 254 | 255 | pre { 256 | display: block; 257 | margin-top: 0; 258 | margin-bottom: 1rem; 259 | overflow: auto; 260 | font-size: 0.875em; 261 | } 262 | pre code { 263 | font-size: inherit; 264 | color: inherit; 265 | word-break: normal; 266 | } 267 | 268 | code { 269 | font-size: 0.875em; 270 | color: #d63384; 271 | word-wrap: break-word; 272 | } 273 | a > code { 274 | color: inherit; 275 | } 276 | 277 | kbd { 278 | padding: 0.2rem 0.4rem; 279 | font-size: 0.875em; 280 | color: #fff; 281 | background-color: #212529; 282 | border-radius: 0.2rem; 283 | } 284 | kbd kbd { 285 | padding: 0; 286 | font-size: 1em; 287 | font-weight: 700; 288 | } 289 | 290 | figure { 291 | margin: 0 0 1rem; 292 | } 293 | 294 | img, 295 | svg { 296 | vertical-align: middle; 297 | } 298 | 299 | table { 300 | caption-side: bottom; 301 | border-collapse: collapse; 302 | } 303 | 304 | caption { 305 | padding-top: 0.5rem; 306 | padding-bottom: 0.5rem; 307 | color: #6c757d; 308 | text-align: left; 309 | } 310 | 311 | th { 312 | text-align: inherit; 313 | text-align: -webkit-match-parent; 314 | } 315 | 316 | thead, 317 | tbody, 318 | tfoot, 319 | tr, 320 | td, 321 | th { 322 | border-color: inherit; 323 | border-style: solid; 324 | border-width: 0; 325 | } 326 | 327 | label { 328 | display: inline-block; 329 | } 330 | 331 | button { 332 | border-radius: 0; 333 | } 334 | 335 | button:focus:not(:focus-visible) { 336 | outline: 0; 337 | } 338 | 339 | input, 340 | button, 341 | select, 342 | optgroup, 343 | textarea { 344 | margin: 0; 345 | font-family: inherit; 346 | font-size: inherit; 347 | line-height: inherit; 348 | } 349 | 350 | button, 351 | select { 352 | text-transform: none; 353 | } 354 | 355 | [role=button] { 356 | cursor: pointer; 357 | } 358 | 359 | select { 360 | word-wrap: normal; 361 | } 362 | select:disabled { 363 | opacity: 1; 364 | } 365 | 366 | [list]::-webkit-calendar-picker-indicator { 367 | display: none; 368 | } 369 | 370 | button, 371 | [type=button], 372 | [type=reset], 373 | [type=submit] { 374 | -webkit-appearance: button; 375 | } 376 | button:not(:disabled), 377 | [type=button]:not(:disabled), 378 | [type=reset]:not(:disabled), 379 | [type=submit]:not(:disabled) { 380 | cursor: pointer; 381 | } 382 | 383 | ::-moz-focus-inner { 384 | padding: 0; 385 | border-style: none; 386 | } 387 | 388 | textarea { 389 | resize: vertical; 390 | } 391 | 392 | fieldset { 393 | min-width: 0; 394 | padding: 0; 395 | margin: 0; 396 | border: 0; 397 | } 398 | 399 | legend { 400 | float: left; 401 | width: 100%; 402 | padding: 0; 403 | margin-bottom: 0.5rem; 404 | font-size: calc(1.275rem + 0.3vw); 405 | line-height: inherit; 406 | } 407 | @media (min-width: 1200px) { 408 | legend { 409 | font-size: 1.5rem; 410 | } 411 | } 412 | legend + * { 413 | clear: left; 414 | } 415 | 416 | ::-webkit-datetime-edit-fields-wrapper, 417 | ::-webkit-datetime-edit-text, 418 | ::-webkit-datetime-edit-minute, 419 | ::-webkit-datetime-edit-hour-field, 420 | ::-webkit-datetime-edit-day-field, 421 | ::-webkit-datetime-edit-month-field, 422 | ::-webkit-datetime-edit-year-field { 423 | padding: 0; 424 | } 425 | 426 | ::-webkit-inner-spin-button { 427 | height: auto; 428 | } 429 | 430 | [type=search] { 431 | outline-offset: -2px; 432 | -webkit-appearance: textfield; 433 | } 434 | 435 | /* rtl:raw: 436 | [type="tel"], 437 | [type="url"], 438 | [type="email"], 439 | [type="number"] { 440 | direction: ltr; 441 | } 442 | */ 443 | ::-webkit-search-decoration { 444 | -webkit-appearance: none; 445 | } 446 | 447 | ::-webkit-color-swatch-wrapper { 448 | padding: 0; 449 | } 450 | 451 | ::-webkit-file-upload-button { 452 | font: inherit; 453 | } 454 | 455 | ::file-selector-button { 456 | font: inherit; 457 | } 458 | 459 | ::-webkit-file-upload-button { 460 | font: inherit; 461 | -webkit-appearance: button; 462 | } 463 | 464 | output { 465 | display: inline-block; 466 | } 467 | 468 | iframe { 469 | border: 0; 470 | } 471 | 472 | summary { 473 | display: list-item; 474 | cursor: pointer; 475 | } 476 | 477 | progress { 478 | vertical-align: baseline; 479 | } 480 | 481 | [hidden] { 482 | display: none !important; 483 | } 484 | 485 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /application/public/bootstrap/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /application/public/bootstrap/css/bootstrap-reboot.rtl.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | :root { 9 | --bs-blue: #0d6efd; 10 | --bs-indigo: #6610f2; 11 | --bs-purple: #6f42c1; 12 | --bs-pink: #d63384; 13 | --bs-red: #dc3545; 14 | --bs-orange: #fd7e14; 15 | --bs-yellow: #ffc107; 16 | --bs-green: #198754; 17 | --bs-teal: #20c997; 18 | --bs-cyan: #0dcaf0; 19 | --bs-white: #fff; 20 | --bs-gray: #6c757d; 21 | --bs-gray-dark: #343a40; 22 | --bs-gray-100: #f8f9fa; 23 | --bs-gray-200: #e9ecef; 24 | --bs-gray-300: #dee2e6; 25 | --bs-gray-400: #ced4da; 26 | --bs-gray-500: #adb5bd; 27 | --bs-gray-600: #6c757d; 28 | --bs-gray-700: #495057; 29 | --bs-gray-800: #343a40; 30 | --bs-gray-900: #212529; 31 | --bs-primary: #0d6efd; 32 | --bs-secondary: #6c757d; 33 | --bs-success: #198754; 34 | --bs-info: #0dcaf0; 35 | --bs-warning: #ffc107; 36 | --bs-danger: #dc3545; 37 | --bs-light: #f8f9fa; 38 | --bs-dark: #212529; 39 | --bs-primary-rgb: 13, 110, 253; 40 | --bs-secondary-rgb: 108, 117, 125; 41 | --bs-success-rgb: 25, 135, 84; 42 | --bs-info-rgb: 13, 202, 240; 43 | --bs-warning-rgb: 255, 193, 7; 44 | --bs-danger-rgb: 220, 53, 69; 45 | --bs-light-rgb: 248, 249, 250; 46 | --bs-dark-rgb: 33, 37, 41; 47 | --bs-white-rgb: 255, 255, 255; 48 | --bs-black-rgb: 0, 0, 0; 49 | --bs-body-color-rgb: 33, 37, 41; 50 | --bs-body-bg-rgb: 255, 255, 255; 51 | --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 52 | --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 53 | --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); 54 | --bs-body-font-family: var(--bs-font-sans-serif); 55 | --bs-body-font-size: 1rem; 56 | --bs-body-font-weight: 400; 57 | --bs-body-line-height: 1.5; 58 | --bs-body-color: #212529; 59 | --bs-body-bg: #fff; 60 | } 61 | 62 | *, 63 | *::before, 64 | *::after { 65 | box-sizing: border-box; 66 | } 67 | 68 | @media (prefers-reduced-motion: no-preference) { 69 | :root { 70 | scroll-behavior: smooth; 71 | } 72 | } 73 | 74 | body { 75 | margin: 0; 76 | font-family: var(--bs-body-font-family); 77 | font-size: var(--bs-body-font-size); 78 | font-weight: var(--bs-body-font-weight); 79 | line-height: var(--bs-body-line-height); 80 | color: var(--bs-body-color); 81 | text-align: var(--bs-body-text-align); 82 | background-color: var(--bs-body-bg); 83 | -webkit-text-size-adjust: 100%; 84 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 85 | } 86 | 87 | hr { 88 | margin: 1rem 0; 89 | color: inherit; 90 | background-color: currentColor; 91 | border: 0; 92 | opacity: 0.25; 93 | } 94 | 95 | hr:not([size]) { 96 | height: 1px; 97 | } 98 | 99 | h6, h5, h4, h3, h2, h1 { 100 | margin-top: 0; 101 | margin-bottom: 0.5rem; 102 | font-weight: 500; 103 | line-height: 1.2; 104 | } 105 | 106 | h1 { 107 | font-size: calc(1.375rem + 1.5vw); 108 | } 109 | @media (min-width: 1200px) { 110 | h1 { 111 | font-size: 2.5rem; 112 | } 113 | } 114 | 115 | h2 { 116 | font-size: calc(1.325rem + 0.9vw); 117 | } 118 | @media (min-width: 1200px) { 119 | h2 { 120 | font-size: 2rem; 121 | } 122 | } 123 | 124 | h3 { 125 | font-size: calc(1.3rem + 0.6vw); 126 | } 127 | @media (min-width: 1200px) { 128 | h3 { 129 | font-size: 1.75rem; 130 | } 131 | } 132 | 133 | h4 { 134 | font-size: calc(1.275rem + 0.3vw); 135 | } 136 | @media (min-width: 1200px) { 137 | h4 { 138 | font-size: 1.5rem; 139 | } 140 | } 141 | 142 | h5 { 143 | font-size: 1.25rem; 144 | } 145 | 146 | h6 { 147 | font-size: 1rem; 148 | } 149 | 150 | p { 151 | margin-top: 0; 152 | margin-bottom: 1rem; 153 | } 154 | 155 | abbr[title], 156 | abbr[data-bs-original-title] { 157 | -webkit-text-decoration: underline dotted; 158 | text-decoration: underline dotted; 159 | cursor: help; 160 | -webkit-text-decoration-skip-ink: none; 161 | text-decoration-skip-ink: none; 162 | } 163 | 164 | address { 165 | margin-bottom: 1rem; 166 | font-style: normal; 167 | line-height: inherit; 168 | } 169 | 170 | ol, 171 | ul { 172 | padding-right: 2rem; 173 | } 174 | 175 | ol, 176 | ul, 177 | dl { 178 | margin-top: 0; 179 | margin-bottom: 1rem; 180 | } 181 | 182 | ol ol, 183 | ul ul, 184 | ol ul, 185 | ul ol { 186 | margin-bottom: 0; 187 | } 188 | 189 | dt { 190 | font-weight: 700; 191 | } 192 | 193 | dd { 194 | margin-bottom: 0.5rem; 195 | margin-right: 0; 196 | } 197 | 198 | blockquote { 199 | margin: 0 0 1rem; 200 | } 201 | 202 | b, 203 | strong { 204 | font-weight: bolder; 205 | } 206 | 207 | small { 208 | font-size: 0.875em; 209 | } 210 | 211 | mark { 212 | padding: 0.2em; 213 | background-color: #fcf8e3; 214 | } 215 | 216 | sub, 217 | sup { 218 | position: relative; 219 | font-size: 0.75em; 220 | line-height: 0; 221 | vertical-align: baseline; 222 | } 223 | 224 | sub { 225 | bottom: -0.25em; 226 | } 227 | 228 | sup { 229 | top: -0.5em; 230 | } 231 | 232 | a { 233 | color: #0d6efd; 234 | text-decoration: underline; 235 | } 236 | a:hover { 237 | color: #0a58ca; 238 | } 239 | 240 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 241 | color: inherit; 242 | text-decoration: none; 243 | } 244 | 245 | pre, 246 | code, 247 | kbd, 248 | samp { 249 | font-family: var(--bs-font-monospace); 250 | font-size: 1em; 251 | direction: ltr ; 252 | unicode-bidi: bidi-override; 253 | } 254 | 255 | pre { 256 | display: block; 257 | margin-top: 0; 258 | margin-bottom: 1rem; 259 | overflow: auto; 260 | font-size: 0.875em; 261 | } 262 | pre code { 263 | font-size: inherit; 264 | color: inherit; 265 | word-break: normal; 266 | } 267 | 268 | code { 269 | font-size: 0.875em; 270 | color: #d63384; 271 | word-wrap: break-word; 272 | } 273 | a > code { 274 | color: inherit; 275 | } 276 | 277 | kbd { 278 | padding: 0.2rem 0.4rem; 279 | font-size: 0.875em; 280 | color: #fff; 281 | background-color: #212529; 282 | border-radius: 0.2rem; 283 | } 284 | kbd kbd { 285 | padding: 0; 286 | font-size: 1em; 287 | font-weight: 700; 288 | } 289 | 290 | figure { 291 | margin: 0 0 1rem; 292 | } 293 | 294 | img, 295 | svg { 296 | vertical-align: middle; 297 | } 298 | 299 | table { 300 | caption-side: bottom; 301 | border-collapse: collapse; 302 | } 303 | 304 | caption { 305 | padding-top: 0.5rem; 306 | padding-bottom: 0.5rem; 307 | color: #6c757d; 308 | text-align: right; 309 | } 310 | 311 | th { 312 | text-align: inherit; 313 | text-align: -webkit-match-parent; 314 | } 315 | 316 | thead, 317 | tbody, 318 | tfoot, 319 | tr, 320 | td, 321 | th { 322 | border-color: inherit; 323 | border-style: solid; 324 | border-width: 0; 325 | } 326 | 327 | label { 328 | display: inline-block; 329 | } 330 | 331 | button { 332 | border-radius: 0; 333 | } 334 | 335 | button:focus:not(:focus-visible) { 336 | outline: 0; 337 | } 338 | 339 | input, 340 | button, 341 | select, 342 | optgroup, 343 | textarea { 344 | margin: 0; 345 | font-family: inherit; 346 | font-size: inherit; 347 | line-height: inherit; 348 | } 349 | 350 | button, 351 | select { 352 | text-transform: none; 353 | } 354 | 355 | [role=button] { 356 | cursor: pointer; 357 | } 358 | 359 | select { 360 | word-wrap: normal; 361 | } 362 | select:disabled { 363 | opacity: 1; 364 | } 365 | 366 | [list]::-webkit-calendar-picker-indicator { 367 | display: none; 368 | } 369 | 370 | button, 371 | [type=button], 372 | [type=reset], 373 | [type=submit] { 374 | -webkit-appearance: button; 375 | } 376 | button:not(:disabled), 377 | [type=button]:not(:disabled), 378 | [type=reset]:not(:disabled), 379 | [type=submit]:not(:disabled) { 380 | cursor: pointer; 381 | } 382 | 383 | ::-moz-focus-inner { 384 | padding: 0; 385 | border-style: none; 386 | } 387 | 388 | textarea { 389 | resize: vertical; 390 | } 391 | 392 | fieldset { 393 | min-width: 0; 394 | padding: 0; 395 | margin: 0; 396 | border: 0; 397 | } 398 | 399 | legend { 400 | float: right; 401 | width: 100%; 402 | padding: 0; 403 | margin-bottom: 0.5rem; 404 | font-size: calc(1.275rem + 0.3vw); 405 | line-height: inherit; 406 | } 407 | @media (min-width: 1200px) { 408 | legend { 409 | font-size: 1.5rem; 410 | } 411 | } 412 | legend + * { 413 | clear: right; 414 | } 415 | 416 | ::-webkit-datetime-edit-fields-wrapper, 417 | ::-webkit-datetime-edit-text, 418 | ::-webkit-datetime-edit-minute, 419 | ::-webkit-datetime-edit-hour-field, 420 | ::-webkit-datetime-edit-day-field, 421 | ::-webkit-datetime-edit-month-field, 422 | ::-webkit-datetime-edit-year-field { 423 | padding: 0; 424 | } 425 | 426 | ::-webkit-inner-spin-button { 427 | height: auto; 428 | } 429 | 430 | [type=search] { 431 | outline-offset: -2px; 432 | -webkit-appearance: textfield; 433 | } 434 | 435 | [type="tel"], 436 | [type="url"], 437 | [type="email"], 438 | [type="number"] { 439 | direction: ltr; 440 | } 441 | ::-webkit-search-decoration { 442 | -webkit-appearance: none; 443 | } 444 | 445 | ::-webkit-color-swatch-wrapper { 446 | padding: 0; 447 | } 448 | 449 | ::-webkit-file-upload-button { 450 | font: inherit; 451 | } 452 | 453 | ::file-selector-button { 454 | font: inherit; 455 | } 456 | 457 | ::-webkit-file-upload-button { 458 | font: inherit; 459 | -webkit-appearance: button; 460 | } 461 | 462 | output { 463 | display: inline-block; 464 | } 465 | 466 | iframe { 467 | border: 0; 468 | } 469 | 470 | summary { 471 | display: list-item; 472 | cursor: pointer; 473 | } 474 | 475 | progress { 476 | vertical-align: baseline; 477 | } 478 | 479 | [hidden] { 480 | display: none !important; 481 | } 482 | /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ -------------------------------------------------------------------------------- /application/public/bootstrap/css/bootstrap-reboot.rtl.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */ -------------------------------------------------------------------------------- /application/public/css/css.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/css/css.css -------------------------------------------------------------------------------- /application/public/css/style.css: -------------------------------------------------------------------------------- 1 | .whb { 2 | background-color: #fff; 3 | } 4 | 5 | section title { 6 | display: block; 7 | 8 | } 9 | 10 | .block { 11 | width: 100%; 12 | background: #faf4ea; 13 | padding: 0.3rem 0.3rem; 14 | border: 1px solid #e4dcd8; 15 | position: relative; 16 | } 17 | 18 | .block h3 { 19 | margin: 0 0 10px; 20 | } 21 | 22 | 23 | .authors-list { 24 | margin-bottom: 10px; 25 | } 26 | 27 | .block .info { 28 | color: #542a00; 29 | margin-bottom: 5px; 30 | font-size: 14px; 31 | line-height: 20px; 32 | } 33 | 34 | 35 | .info dt { 36 | float: left; 37 | clear: left; 38 | color: #000; 39 | margin: 0 3px 0 0; 40 | } 41 | 42 | .bottom-line { 43 | border-top: 1px dashed #e4dcd8; 44 | margin-top: 5px; 45 | margin-bottom: 5px; 46 | } 47 | 48 | .author { 49 | line-height: 1.3em; 50 | padding-right: 4px; 51 | padding-left: 4px; 52 | border-radius: 2px; 53 | text-decoration: none; 54 | margin: 2px 4px 2px 0; 55 | color: #542a00; 56 | padding: 2px; 57 | } 58 | 59 | section { 60 | text-align: justify; 61 | } 62 | 63 | section p { 64 | text-indent: 2em; 65 | } 66 | 67 | section title { 68 | text-align: center; 69 | font-weight: bold; 70 | font-size: 1.5 em; 71 | } 72 | 73 | a:hover, a:visited, a:link, a:active { 74 | text-decoration: none; 75 | } 76 | h3 { 77 | color: #334455; 78 | text-decoration: none; 79 | } 80 | 81 | .book-link { 82 | color: #542a00; 83 | } 84 | .card-body { 85 | padding: 0.3rem 0.3rem; 86 | } 87 | 88 | h4 { 89 | margin-bottom: .1rem; 90 | } 91 | 92 | .cover { 93 | box-shadow: 0 .125rem .25rem rgba(0,0,0,.9)!important; 94 | max-height: 400px; 95 | } 96 | 97 | .book { 98 | text-align: justify; 99 | } 100 | 101 | 102 | canvas { 103 | width: 100%; 104 | } 105 | 106 | .epub-view { 107 | width: 100% !important; 108 | } 109 | 110 | .epub-view iframe { 111 | width: 100% !important; 112 | 113 | } 114 | 115 | .divider { 116 | position: relative; 117 | margin-top: 1em; 118 | margin-bottom: 1em; 119 | height: 1px; 120 | } 121 | 122 | .div-transparent:before { 123 | content: ""; 124 | position: absolute; 125 | top: 0; 126 | left: 5%; 127 | right: 5%; 128 | width: 90%; 129 | height: 1px; 130 | background-image: linear-gradient(to right, transparent, rgb(48,49,51), transparent); 131 | } 132 | 133 | .div-arrow-down:after { 134 | content: ""; 135 | position: absolute; 136 | z-index: 1; 137 | top: -7px; 138 | left: calc(50% - 7px); 139 | width: 14px; 140 | height: 14px; 141 | transform: rotate(45deg); 142 | background-color: white; 143 | border-bottom: 1px solid rgb(48,49,51); 144 | border-right: 1px solid rgb(48,49,51); 145 | } 146 | 147 | .div-tab-down:after { 148 | content: ""; 149 | position: absolute; 150 | z-index: 1; 151 | top: 0; 152 | left: calc(50% - 10px); 153 | width: 20px; 154 | height: 14px; 155 | background-color: white; 156 | border-bottom: 1px solid rgb(48,49,51); 157 | border-left: 1px solid rgb(48,49,51); 158 | border-right: 1px solid rgb(48,49,51); 159 | border-radius: 0 0 8px 8px; 160 | } 161 | 162 | .div-stopper:after { 163 | content: ""; 164 | position: absolute; 165 | z-index: 1; 166 | top: -6px; 167 | left: calc(50% - 7px); 168 | width: 14px; 169 | height: 12px; 170 | background-color: white; 171 | border-left: 1px solid rgb(48,49,51); 172 | border-right: 1px solid rgb(48,49,51); 173 | } 174 | 175 | .div-dot:after { 176 | content: ""; 177 | position: absolute; 178 | z-index: 1; 179 | top: -9px; 180 | left: calc(50% - 9px); 181 | width: 18px; 182 | height: 18px; 183 | background-color: #aaa ; 184 | border: 1px solid rgb(48,49,51); 185 | border-radius: 50%; 186 | box-shadow: inset 0 0 0 2px white, 187 | 0 0 0 4px white; 188 | } 189 | 190 | title { 191 | text-shadow: 0 2px white, 0 3px #777; 192 | margin: 1em 0 0.5em 0; 193 | color: #343434; 194 | font-family: 'Open Sans Condensed', sans-serif; 195 | font-size: 1.5em; 196 | line-height: 1.8em; 197 | text-align: center; 198 | text-transform: uppercase; 199 | } 200 | 201 | section section title { 202 | font-size: 18px; 203 | line-height: 20px; 204 | margin: 0; 205 | text-shadow: none; 206 | text-transform: none; 207 | text-align: left; 208 | border-top-left-radius: .25rem!important; 209 | border-top-right-radius: .25rem!important; 210 | background: #ddd; 211 | display: inline-block; 212 | padding: 0.25em; 213 | border-top: 1px solid #ddd; 214 | border-right: 1px solid #ddd; 215 | border-left: 1px solid #ddd; 216 | } 217 | 218 | section1 section p { 219 | padding: 0.5em; 220 | border: 1px solid #ddd; 221 | border-bottom-left-radius: .25rem!important; 222 | border-bottom-right-radius: .25rem!important; 223 | border-top-right-radius: .25rem!important; 224 | background: #eee; 225 | } 226 | 227 | .reader p { 228 | font-family: Sentinel SSm A,Sentinel SSm B,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; 229 | font-size: 1.2rem; 230 | font-weight: 400; 231 | line-height: 1.7; 232 | color: #333; 233 | font-family: sans-serif; 234 | margin-bottom: 15px; 235 | } 236 | 237 | .fx { 238 | display: block; 239 | margin-bottom: 0.5em; 240 | 241 | } 242 | 243 | .epub-container { 244 | min-width: 320px; 245 | margin: 0 auto; 246 | position: relative; 247 | } 248 | 249 | .epub-container .epub-view > iframe { 250 | background: white; 251 | box-shadow: 0 0 4px #ccc; 252 | /*margin: 10px; 253 | padding: 20px;*/ 254 | } 255 | 256 | -------------------------------------------------------------------------------- /application/public/extract_author.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT file FROM libapics WHERE AvtorId=$id"); 34 | $stmt->execute(); 35 | $f = $stmt->fetch(); 36 | 37 | if (isset($f->file)) { 38 | $zip = new ZipArchive(); 39 | if ($zip->open(ROOT_PATH . "cache/lib.a.attached.zip")) { 40 | $f = $zip->getFromName($f->file); 41 | if (strlen($f) > 0) { 42 | file_put_contents(ROOT_PATH . "cache/authors/$id.jpg", $f); 43 | echo $f; 44 | die(); 45 | } 46 | } 47 | $zip->close(); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /application/public/extract_cover.php: -------------------------------------------------------------------------------- 1 | $height && $newheight < $height){ 12 | $newheight = (int)round($height / ($width / $newwidth)); 13 | } else if ($width < $height && $newwidth < $width) { 14 | $newwidth = (int)round($width / ($height / $newheight)); 15 | } else { 16 | $newwidth = (int)round($width); 17 | $newheight = (int)round($height); 18 | } 19 | $thumb = imagecreatetruecolor($newwidth, $newheight); 20 | imagecopyresized($thumb, $i, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); 21 | return $thumb; 22 | } 23 | 24 | function lastm($path) { 25 | $fmtimestamp = filemtime($path); 26 | if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $fmtimestamp <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { 27 | header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified'); 28 | die(); 29 | } else { 30 | header("Expires: " . gmdate("D, d M Y H:i:s", filemtime($path) + 60*60*24) . " GMT"); 31 | header("Last-Modified: " . gmdate("D, d M Y H:i:s", filemtime($path)) . " GMT"); 32 | 33 | echo file_get_contents($path); 34 | } 35 | } 36 | 37 | $small = isset($_GET['small']); 38 | 39 | if (isset($_GET['id'])) { 40 | $id = $_GET['id']; 41 | } else { 42 | if (isset($_GET['sid'])) { 43 | $id = $_GET['sid']; 44 | $small = true; 45 | } 46 | } 47 | $iid = $id; 48 | 49 | header("Content-type: image/jpeg"); 50 | 51 | if ($small) { 52 | if (file_exists(ROOT_PATH . "cache/covers/$id-small.jpg")) { 53 | lastm(ROOT_PATH . "cache/covers/$id-small.jpg"); 54 | die(); 55 | } 56 | } else { 57 | if (file_exists(ROOT_PATH . "cache/covers/$id.jpg")) { 58 | lastm(ROOT_PATH . "cache/covers/$id.jpg"); 59 | die(); 60 | } 61 | } 62 | 63 | $stmt = $dbh->prepare("SELECT file FROM libbpics WHERE BookId=$id"); 64 | $stmt->execute(); 65 | $f = $stmt->fetch(); 66 | 67 | if (isset($f->file)) { 68 | $zip = new ZipArchive(); 69 | if ($zip->open(ROOT_PATH . "cache/lib.b.attached.zip")) { 70 | $f = $zip->getFromName($f->file); 71 | if (strlen($f) > 0) { 72 | file_put_contents(ROOT_PATH . "cache/covers/$id.jpg", $f); 73 | $thm = resizeCover($f, 300, 400); 74 | imagejpeg($thm, ROOT_PATH . "cache/covers/$id-small.jpg", 75); 75 | imagedestroy($thm); 76 | if ($small) { 77 | if (file_exists(ROOT_PATH . "cache/covers/$id-small.jpg")) { 78 | lastm(ROOT_PATH . "cache/covers/$id-small.jpg"); 79 | die(); 80 | } 81 | } else { 82 | echo $f; 83 | die(); 84 | } 85 | } 86 | } 87 | $zip->close(); 88 | } 89 | 90 | 91 | $stmt = $dbh->prepare("SELECT filetype FROM libbook WHERE bookid=$id LIMIT 1"); 92 | $stmt->execute(); 93 | $type = trim($stmt->fetch()->filetype); 94 | if ($type == 'fb2') { 95 | $u = '0'; 96 | } else { 97 | $u = '1'; 98 | } 99 | 100 | $stmt = $dbh->prepare("SELECT * FROM book_zip WHERE $id BETWEEN start_id AND end_id AND usr=$u"); 101 | $stmt->execute(); 102 | $zip_name = $stmt->fetch()->filename; 103 | $zip = new ZipArchive(); 104 | 105 | $result = $dbh->query("SELECT filename FROM libfilename where BookId=$id")->fetch(); 106 | 107 | if ($result) { 108 | $filename = $result->filename; 109 | } else { 110 | $filename = null; 111 | } 112 | if ($filename == '') { 113 | $filename = trim("$id.$type"); 114 | } 115 | 116 | if ($zip->open(ROOT_PATH . "flibusta/" . $zip_name)) { 117 | $f = $zip->getFromName("$filename"); 118 | } 119 | 120 | 121 | if ($type == 'fb2') { 122 | $fb2 = simplexml_load_string($f); 123 | $images = array(); 124 | if (isset($fb2->binary)) { 125 | foreach ($fb2->binary as $binary) { 126 | $id = $binary->attributes()['id']; 127 | if ( 128 | (strpos($id, "cover") !== false) || 129 | (strpos($id, "jpg") !== false) || 130 | (strpos($id, "obloj") !== false) 131 | ) { 132 | $cover = base64_decode($binary); 133 | } 134 | $images["$id"] = $binary; 135 | } 136 | } 137 | $zip->close(); 138 | } 139 | 140 | if ($type == 'epub') { 141 | file_put_contents(ROOT_PATH . "cache/tmp/$iid.tmp", $f); 142 | include('/application/epub.php'); 143 | $d = new EPub(ROOT_PATH . "cache/tmp/$iid.tmp"); 144 | $im = $d->Cover(); 145 | if ($im['found'] != '') { 146 | $cover = $im['data']; 147 | unlink(ROOT_PATH . "cache/tmp/$iid.tmp"); 148 | } else { 149 | echo file_get_contents('/application/none.jpg'); 150 | } 151 | } 152 | 153 | if (strlen($cover) < 100) { 154 | $cover = file_get_contents('/application/none.jpg'); 155 | echo $cover; 156 | die(); 157 | } else { 158 | file_put_contents(ROOT_PATH . "cache/covers/$iid.jpg", $cover); 159 | $thm = resizeCover($cover, 300, 400); 160 | imagejpeg($thm, ROOT_PATH . "cache/covers/$iid-small.jpg", 75); 161 | imagedestroy($thm); 162 | } 163 | 164 | if ($small) { 165 | if (file_exists(ROOT_PATH . "cache/covers/$iid-small.jpg")) { 166 | lastm(ROOT_PATH . "cache/covers/$iid-small.jpg"); 167 | die(); 168 | } 169 | } else { 170 | echo $cover; 171 | } 172 | 173 | -------------------------------------------------------------------------------- /application/public/extract_usr.php: -------------------------------------------------------------------------------- 1 | fetchObject(); 17 | 18 | $usr_filename = DBO::query("SELECT * FROM libfilename where BookId=$id")->fetchObject(); 19 | 20 | 21 | if ($usr_filename == '') { 22 | $usr_filename = trim("$id.$book->filetype"); 23 | } 24 | 25 | $zip_name = DBO::query("SELECT * FROM book_zip WHERE ($id BETWEEN start_id AND end_id) AND usr=1")->fetchObject()->filename; 26 | $zip = new ZipArchive(); 27 | 28 | if ($zip->open("/work/fb/Flibusta.Net/" . $zip_name)) { 29 | $filename = $book->author_name . " - " . $book->booktitle . " " . $book->filename . "." . trim($book->filetype); 30 | 31 | header('Content-Description: File Transfer'); 32 | header('Content-Type: application/octet-stream'); 33 | header('Content-Transfer-Encoding: binary'); 34 | header('Expires: 0'); 35 | header('Cache-Control: must-revalidate'); 36 | header('Pragma: public'); 37 | header('Content-Disposition: attachment; filename=' . basename(rawurlencode($filename))); 38 | 39 | $data = $zip->getFromName($usr_filename); 40 | if ($data == '') { 41 | $data = $zip->getFromName($usr_filename . ".zip"); 42 | } 43 | 44 | echo $data; 45 | $zip->close(); 46 | } else { 47 | echo "NO ZIP"; 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /application/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/favicon.ico -------------------------------------------------------------------------------- /application/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /application/public/fb2.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT libbook.Title BookTitle, 11 | CONCAT(libavtorname.LastName, ' ', libavtorname.FirstName) author_name 12 | FROM libbook 13 | LEFT JOIN libbannotations USING(BookId) 14 | LEFT JOIN libgenre USING(BookId) 15 | LEFT JOIN libgenrelist USING(GenreId) 16 | LEFT JOIN libseq USING(BookId) 17 | LEFT JOIN libavtor USING(BookId) 18 | LEFT JOIN libavtorname USING(AvtorId) 19 | LEFT JOIN libseqname USING(SeqId) WHERE libbook.BookId=:id"); 20 | $stmt->bindParam(":id", $id); 21 | $stmt->execute(); 22 | $book = $stmt->fetch(); 23 | 24 | 25 | $stmt = $dbh->prepare("SELECT * FROM book_zip WHERE $id BETWEEN start_id AND end_id AND usr=0"); 26 | $stmt->execute(); 27 | $zip_name = $stmt->fetch()->filename; 28 | $zip = new ZipArchive(); 29 | 30 | if ($zip->open(ROOT_PATH . "flibusta/" . $zip_name)) { 31 | $filename = $book->author_name . " - " . $book->booktitle . " " . $id . ".fb2"; 32 | header('Content-Description: File Transfer'); 33 | header('Content-Type: application/octet-stream'); 34 | header('Content-Disposition: attachment; filename=' . basename(rawurlencode($filename))); 35 | header('Content-Transfer-Encoding: binary'); 36 | header('Expires: 0'); 37 | header('Cache-Control: must-revalidate'); 38 | header('Pragma: public'); 39 | echo $zip->getFromName("$id.fb2"); 40 | $zip->close(); 41 | } else { 42 | echo "NO ZIP"; 43 | } 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /application/public/font/weathericons-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/font/weathericons-regular-webfont.eot -------------------------------------------------------------------------------- /application/public/font/weathericons-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/font/weathericons-regular-webfont.ttf -------------------------------------------------------------------------------- /application/public/font/weathericons-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/font/weathericons-regular-webfont.woff -------------------------------------------------------------------------------- /application/public/font/weathericons-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/font/weathericons-regular-webfont.woff2 -------------------------------------------------------------------------------- /application/public/fonts/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 -------------------------------------------------------------------------------- /application/public/fonts/KFOlCnqEu92Fr1MmWUlfBBc4.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 -------------------------------------------------------------------------------- /application/public/fonts/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /application/public/fonts/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /application/public/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /application/public/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 -------------------------------------------------------------------------------- /application/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /application/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /application/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /application/public/fonts/materialicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/materialicons.woff2 -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Bold.eot -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Bold.ttf -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Light.eot -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Light.ttf -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Medium.eot -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Regular.eot -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Thin.eot -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Thin.ttf -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /application/public/fonts/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/fonts/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /application/public/i/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/i/default_avatar.png -------------------------------------------------------------------------------- /application/public/i/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/i/ic_launcher.png -------------------------------------------------------------------------------- /application/public/index.php: -------------------------------------------------------------------------------- 1 | prepare("DELETE FROM fav_users WHERE user_uuid=:uuid"); 16 | $stmt->bindParam(":uuid", $uu); 17 | $stmt->execute(); 18 | $st = $dbh->prepare("DELETE FROM fav WHERE user_uuid=:uuid"); 19 | $st->bindParam(":uuid", $uu); 20 | $st->execute(); 21 | } 22 | 23 | if (isset($_GET['new_uuid'])) { 24 | $nname = trim($_GET['new_uuid']); 25 | if ($nname !== '') { 26 | $stmt = $dbh->prepare("INSERT INTO fav_users (user_uuid, name) VALUES (uuid_generate_v1(), :name)"); 27 | $stmt->bindParam(":name", $nname); 28 | $stmt->execute(); 29 | 30 | $stmt = $dbh->prepare("SELECT user_uuid FROM fav_users WHERE name=:name LIMIT 1"); 31 | $stmt->bindParam(":name", $nname); 32 | $stmt->execute(); 33 | $r = $stmt->fetch(); 34 | $user_uuid = $r->user_uuid; 35 | $user_name = $nname; 36 | $_SESSION['user_uuid'] = $user_uuid; 37 | } 38 | } 39 | 40 | if (isset($_SESSION['user_uuid'])) { 41 | $user_uuid = $_SESSION['user_uuid']; 42 | $stmt = $dbh->prepare("SELECT * FROM fav_users WHERE user_uuid=:uuid"); 43 | $stmt->bindParam(":uuid", $user_uuid); 44 | try { 45 | $stmt->execute(); 46 | $user = $stmt->fetch(); 47 | } catch (PDOException $e) { 48 | // 49 | } 50 | 51 | if (isset($user->name)) { 52 | $user_name = $user->name; 53 | 54 | if (isset($_GET['fav_book'])) { 55 | $id = intval($_GET['fav_book']); 56 | $st = $dbh->prepare("INSERT INTO fav (user_uuid, bookid) VALUES(:uuid, :id) ON CONFLICT DO NOTHING"); 57 | $st->bindParam(":uuid", $user_uuid); 58 | $st->bindParam(":id", $id); 59 | $st->execute(); 60 | } 61 | if (isset($_GET['fav_author'])) { 62 | $id = intval($_GET['fav_author']); 63 | $st = $dbh->prepare("INSERT INTO fav (user_uuid, avtorid) VALUES(:uuid, :id) ON CONFLICT DO NOTHING"); 64 | $st->bindParam(":uuid", $user_uuid); 65 | $st->bindParam(":id", $id); 66 | $st->execute(); 67 | } 68 | if (isset($_GET['fav_seq'])) { 69 | $id = intval($_GET['fav_seq']); 70 | $st = $dbh->prepare("DELETE FROM fav WHERE user_uuid=:uuid AND seqid=:id"); 71 | $st->bindParam(":uuid", $user_uuid); 72 | $st->bindParam(":id", $id); 73 | $st->execute(); 74 | $st = $dbh->prepare("INSERT INTO fav (user_uuid, seqid) VALUES(:uuid, :id) ON CONFLICT DO NOTHING"); 75 | $st->bindParam(":uuid", $user_uuid); 76 | $st->bindParam(":id", $id); 77 | $st->execute(); 78 | } 79 | 80 | if (isset($_GET['unfav_book'])) { 81 | $id = intval($_GET['unfav_book']); 82 | $st = $dbh->prepare("DELETE FROM fav WHERE user_uuid=:uuid AND bookid=:id"); 83 | $st->bindParam(":uuid", $user_uuid); 84 | $st->bindParam(":id", $id); 85 | $st->execute(); 86 | } 87 | if (isset($_GET['unfav_author'])) { 88 | $id = intval($_GET['unfav_author']); 89 | $st = $dbh->prepare("DELETE FROM fav WHERE user_uuid=:uuid AND avtorid=:id"); 90 | $st->bindParam(":uuid", $user_uuid); 91 | $st->bindParam(":id", $id); 92 | $st->execute(); 93 | } 94 | if (isset($_GET['unfav_seq'])) { 95 | $id = intval($_GET['unfav_seq']); 96 | $st = $dbh->prepare("DELETE FROM fav WHERE user_uuid=:uuid AND seqid=:id"); 97 | $st->bindParam(":uuid", $user_uuid); 98 | $st->bindParam(":id", $id); 99 | $st->execute(); 100 | } 101 | } else { 102 | unset($_SESSION['user_uuid']); 103 | $user_name = 'Книжные полки'; 104 | } 105 | } else { 106 | $user_uuid = ''; 107 | } 108 | 109 | if (isset($_GET['sort'])) { 110 | $sort_mode = $_GET['sort']; 111 | } else { 112 | $sort_mode = 'abc'; 113 | if ($url->action == '') { 114 | $sort_mode = 'date'; 115 | } 116 | } 117 | 118 | if (isset($_GET['page'])) { 119 | $page = intval($_GET['page']); 120 | } else { 121 | $page = 0; 122 | } 123 | 124 | $start = $page * RECORDS_PAGE; 125 | $lang = 'ru'; 126 | $filter = ""; 127 | 128 | switch ($sort_mode) { 129 | case 'abc': 130 | $order = 'b.Title'; 131 | break; 132 | 133 | case 'author': 134 | $order = 'b.Title'; 135 | break; 136 | 137 | case 'date': 138 | $order = 'b.Time DESC'; 139 | break; 140 | 141 | case 'rating': 142 | $order = 'b.Title'; 143 | break; 144 | } 145 | 146 | if ($url->mod == 'opds') { 147 | include(ROOT_PATH . "/opds/index.php"); 148 | } else { 149 | include(ROOT_PATH . "renderer.php"); 150 | } 151 | 152 | -------------------------------------------------------------------------------- /application/public/js/mobi.min.js: -------------------------------------------------------------------------------- 1 | var _0x3d0b=["\x6C\x65\x6E\x67\x74\x68","\x65\x6E\x75\x6D\x65\x72\x61\x62\x6C\x65","\x63\x6F\x6E\x66\x69\x67\x75\x72\x61\x62\x6C\x65","\x76\x61\x6C\x75\x65","\x77\x72\x69\x74\x61\x62\x6C\x65","\x6B\x65\x79","\x64\x65\x66\x69\x6E\x65\x50\x72\x6F\x70\x65\x72\x74\x79","\x70\x72\x6F\x74\x6F\x74\x79\x70\x65","\x43\x61\x6E\x6E\x6F\x74\x20\x63\x61\x6C\x6C\x20\x61\x20\x63\x6C\x61\x73\x73\x20\x61\x73\x20\x61\x20\x66\x75\x6E\x63\x74\x69\x6F\x6E","\x64\x65\x63\x6F\x64\x65","\x75\x74\x66\x2D\x38","\x63\x61\x70\x61\x63\x69\x74\x79","\x66\x72\x61\x67\x6D\x65\x6E\x74\x5F\x6C\x69\x73\x74","\x63\x75\x72\x5F\x66\x72\x61\x67\x6D\x65\x6E\x74","\x70\x75\x73\x68","\x77\x72\x69\x74\x65","\x67\x65\x74","\x73\x69\x7A\x65","\x73\x68\x72\x69\x6E\x6B","\x66\x75\x6C\x6C","\x62\x75\x66\x66\x65\x72","\x73\x65\x74","\x73\x6C\x69\x63\x65","\x76\x69\x65\x77","\x6F\x66\x66\x73\x65\x74","\x68\x65\x61\x64\x65\x72","\x70\x61\x72\x73\x65","\x67\x65\x74\x55\x69\x6E\x74\x38","\x67\x65\x74\x55\x69\x6E\x74\x31\x36","\x67\x65\x74\x55\x69\x6E\x74\x33\x32","\x67\x65\x74\x53\x74\x72","\x73\x6B\x69\x70","\x73\x65\x74\x6F\x66\x66\x73\x65\x74","\x67\x65\x74\x5F\x72\x65\x63\x6F\x72\x64\x5F\x65\x78\x74\x72\x61\x73\x69\x7A\x65","\x62\x75\x66\x66\x65\x72\x5F\x67\x65\x74\x5F\x76\x61\x72\x6C\x65\x6E","\x72\x65\x61\x64\x5F\x74\x65\x78\x74","\x72\x65\x63\x6F\x72\x64\x5F\x63\x6F\x75\x6E\x74","\x70\x61\x6C\x6D\x5F\x68\x65\x61\x64\x65\x72","\x72\x65\x61\x64\x5F\x74\x65\x78\x74\x5F\x72\x65\x63\x6F\x72\x64","\x65\x78\x74\x72\x61\x5F\x66\x6C\x61\x67\x73","\x6D\x6F\x62\x69\x5F\x68\x65\x61\x64\x65\x72","\x72\x65\x63\x6C\x69\x73\x74","\x63\x6F\x6D\x70\x72\x65\x73\x73\x69\x6F\x6E","\x72\x65\x61\x64\x5F\x69\x6D\x61\x67\x65","\x66\x69\x72\x73\x74\x5F\x69\x6D\x61\x67\x65\x5F\x69\x64\x78","\x6C\x6F\x61\x64","\x6C\x6F\x61\x64\x5F\x70\x64\x62\x68\x65\x61\x64\x65\x72","\x6C\x6F\x61\x64\x5F\x72\x65\x63\x6C\x69\x73\x74","\x6C\x6F\x61\x64\x5F\x72\x65\x63\x6F\x72\x64\x30","\x6E\x61\x6D\x65","\x61\x74\x74\x72","\x76\x65\x72\x73\x69\x6F\x6E","\x63\x74\x69\x6D\x65","\x6D\x74\x69\x6D\x65","\x62\x74\x69\x6D\x65","\x6D\x6F\x64\x5F\x6E\x75\x6D","\x61\x70\x70\x69\x6E\x66\x6F\x5F\x6F\x66\x66\x73\x65\x74","\x73\x6F\x72\x74\x69\x6E\x66\x6F\x5F\x6F\x66\x66\x73\x65\x74","\x74\x79\x70\x65","\x63\x72\x65\x61\x74\x6F\x72","\x75\x69\x64","\x6E\x65\x78\x74\x5F\x72\x65\x63","\x72\x65\x63\x6F\x72\x64\x5F\x6E\x75\x6D","\x6C\x6F\x61\x64\x5F\x72\x65\x63\x6F\x72\x64\x30\x5F\x68\x65\x61\x64\x65\x72","\x6C\x6F\x61\x64\x5F\x6D\x6F\x62\x69\x5F\x68\x65\x61\x64\x65\x72","\x74\x65\x78\x74\x5F\x6C\x65\x6E\x67\x74\x68","\x72\x65\x63\x6F\x72\x64\x5F\x73\x69\x7A\x65","\x65\x6E\x63\x72\x79\x70\x74\x69\x6F\x6E\x5F\x74\x79\x70\x65","\x69\x64\x65\x6E\x74\x69\x66\x69\x65\x72","\x68\x65\x61\x64\x65\x72\x5F\x6C\x65\x6E\x67\x74\x68","\x6D\x6F\x62\x69\x5F\x74\x79\x70\x65","\x74\x65\x78\x74\x5F\x65\x6E\x63\x6F\x64\x69\x6E\x67","\x67\x65\x6E\x65\x72\x61\x74\x6F\x72\x5F\x76\x65\x72\x73\x69\x6F\x6E","\x66\x69\x72\x73\x74\x5F\x6E\x6F\x6E\x62\x6F\x6F\x6B\x5F\x69\x6E\x64\x65\x78","\x66\x75\x6C\x6C\x5F\x6E\x61\x6D\x65\x5F\x6F\x66\x66\x73\x65\x74","\x66\x75\x6C\x6C\x5F\x6E\x61\x6D\x65\x5F\x6C\x65\x6E\x67\x74\x68","\x6C\x61\x6E\x67\x75\x61\x67\x65","\x69\x6E\x70\x75\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65","\x6F\x75\x74\x70\x75\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65","\x6D\x69\x6E\x5F\x76\x65\x72\x73\x69\x6F\x6E","\x68\x75\x66\x66\x5F\x72\x65\x63\x5F\x69\x6E\x64\x65\x78","\x68\x75\x66\x66\x5F\x72\x65\x63\x5F\x63\x6F\x75\x6E\x74","\x64\x61\x74\x70\x5F\x72\x65\x63\x5F\x69\x6E\x64\x65\x78","\x64\x61\x74\x70\x5F\x72\x65\x63\x5F\x63\x6F\x75\x6E\x74","\x65\x78\x74\x68\x5F\x66\x6C\x61\x67\x73","\x64\x72\x6D\x5F\x6F\x66\x66\x73\x65\x74","\x64\x72\x6D\x5F\x63\x6F\x75\x6E\x74","\x64\x72\x6D\x5F\x73\x69\x7A\x65","\x64\x72\x6D\x5F\x66\x6C\x61\x67\x73","\x6C\x6F\x61\x64\x5F\x65\x78\x74\x68\x5F\x68\x65\x61\x64\x65\x72","\x72\x65\x6E\x64\x65\x72\x5F\x74\x6F","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x66\x69\x72\x73\x74\x43\x68\x69\x6C\x64","\x72\x65\x6D\x6F\x76\x65\x43\x68\x69\x6C\x64","\x74\x65\x78\x74\x2F\x68\x74\x6D\x6C","\x70\x61\x72\x73\x65\x46\x72\x6F\x6D\x53\x74\x72\x69\x6E\x67","\x61\x70\x70\x65\x6E\x64\x43\x68\x69\x6C\x64","\x66\x6F\x72\x45\x61\x63\x68","\x63\x68\x69\x6C\x64\x4E\x6F\x64\x65\x73","\x62\x6F\x64\x79","\x69\x6D\x67","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x73\x42\x79\x54\x61\x67\x4E\x61\x6D\x65","\x72\x65\x6E\x64\x65\x72\x5F\x69\x6D\x61\x67\x65","\x72\x65\x63\x69\x6E\x64\x65\x78","\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65","\x6F\x6E\x6C\x6F\x61\x64","\x73\x72\x63","\x72\x65\x73\x75\x6C\x74","\x74\x61\x72\x67\x65\x74","\x72\x65\x61\x64\x41\x73\x44\x61\x74\x61\x55\x52\x4C"];var _createClass=function(){function _0x8e3ex2(_0x8e3ex3,_0x8e3ex4){for(var _0x8e3ex5=0;_0x8e3ex5< _0x8e3ex4[_0x3d0b[0]];_0x8e3ex5++){var _0x8e3ex6=_0x8e3ex4[_0x8e3ex5];_0x8e3ex6[_0x3d0b[1]]= _0x8e3ex6[_0x3d0b[1]]|| false;_0x8e3ex6[_0x3d0b[2]]= true;if(_0x3d0b[3] in _0x8e3ex6){_0x8e3ex6[_0x3d0b[4]]= true};Object[_0x3d0b[6]](_0x8e3ex3,_0x8e3ex6[_0x3d0b[5]],_0x8e3ex6)}}return function(_0x8e3ex7,_0x8e3ex8,_0x8e3ex9){if(_0x8e3ex8){_0x8e3ex2(_0x8e3ex7[_0x3d0b[7]],_0x8e3ex8)};if(_0x8e3ex9){_0x8e3ex2(_0x8e3ex7,_0x8e3ex9)};return _0x8e3ex7}}();function _classCallCheck(_0x8e3exb,_0x8e3ex7){if(!(_0x8e3exb instanceof _0x8e3ex7)){throw new TypeError(_0x3d0b[8])}}function ab2str(_0x8e3exd){if(_0x8e3exd instanceof ArrayBuffer){_0x8e3exd= new Uint8Array(_0x8e3exd)};return new TextDecoder(_0x3d0b[10])[_0x3d0b[9]](_0x8e3exd)}var domParser= new DOMParser();var Buffer=function(){function Buffer(_0x8e3ex10){_classCallCheck(this,Buffer);this[_0x3d0b[11]]= _0x8e3ex10;this[_0x3d0b[12]]= [];this[_0x3d0b[13]]= new Fragment(_0x8e3ex10);this[_0x3d0b[12]][_0x3d0b[14]](this[_0x3d0b[13]])}_createClass(Buffer,[{key:_0x3d0b[15],value:function _0x8e3ex11(_0x8e3ex12){var _0x8e3ex13=this[_0x3d0b[13]][_0x3d0b[15]](_0x8e3ex12);if(!_0x8e3ex13){this[_0x3d0b[13]]= new Fragment(this[_0x3d0b[11]]);this[_0x3d0b[12]][_0x3d0b[14]](this[_0x3d0b[13]]);this[_0x3d0b[13]][_0x3d0b[15]](_0x8e3ex12)}}},{key:_0x3d0b[16],value:function _0x8e3ex14(_0x8e3ex15){var _0x8e3ex16=0;while(_0x8e3ex16< this[_0x3d0b[12]][_0x3d0b[0]]){var _0x8e3ex17=this[_0x3d0b[12]][_0x8e3ex16];if(_0x8e3ex15< _0x8e3ex17[_0x3d0b[17]]){return _0x8e3ex17[_0x3d0b[16]](_0x8e3ex15)};_0x8e3ex15-= _0x8e3ex17[_0x3d0b[17]];_0x8e3ex16+= 1};return null}},{key:_0x3d0b[17],value:function _0x8e3ex18(){var _0x8e3ex19=0;for(var _0x8e3ex5=0;_0x8e3ex5< this[_0x3d0b[12]][_0x3d0b[0]];_0x8e3ex5++){_0x8e3ex19+= this[_0x3d0b[12]][_0x8e3ex5][_0x3d0b[17]]};return _0x8e3ex19}},{key:_0x3d0b[18],value:function _0x8e3ex1a(){var _0x8e3ex1b= new Uint8Array(this[_0x3d0b[17]]());var _0x8e3ex1c=0;for(var _0x8e3ex5=0;_0x8e3ex5< this[_0x3d0b[12]][_0x3d0b[0]];_0x8e3ex5++){var _0x8e3ex17=this[_0x3d0b[12]][_0x8e3ex5];if(_0x8e3ex17[_0x3d0b[19]]()){_0x8e3ex1b[_0x3d0b[21]](_0x8e3ex17[_0x3d0b[20]],_0x8e3ex1c)}else {_0x8e3ex1b[_0x3d0b[21]](_0x8e3ex17[_0x3d0b[20]][_0x3d0b[22]](0,_0x8e3ex17[_0x3d0b[17]]),_0x8e3ex1c)};_0x8e3ex1c+= _0x8e3ex17[_0x3d0b[17]]};return _0x8e3ex1b}}]);return Buffer}();var combine_uint8array=function combine_uint8array(_0x8e3ex1e){var _0x8e3ex1f=0;for(var _0x8e3ex5=0;_0x8e3ex5< _0x8e3ex1e[_0x3d0b[0]];_0x8e3ex5++){var _0x8e3ex20=_0x8e3ex1e[_0x8e3ex5];_0x8e3ex1f+= _0x8e3ex20[_0x3d0b[0]]};var _0x8e3ex1b= new Uint8Array(_0x8e3ex1f);var _0x8e3ex1c=0;for(var _0x8e3ex5=0;_0x8e3ex5< _0x8e3ex1e[_0x3d0b[0]];_0x8e3ex5++){var _0x8e3ex20=_0x8e3ex1e[_0x8e3ex5];_0x8e3ex1b[_0x3d0b[21]](_0x8e3ex20,_0x8e3ex1c);_0x8e3ex1c+= _0x8e3ex20[_0x3d0b[0]]};return _0x8e3ex1b};var Fragment=function(){function Fragment(_0x8e3ex10){_classCallCheck(this,Fragment);this[_0x3d0b[20]]= new Uint8Array(_0x8e3ex10);this[_0x3d0b[11]]= _0x8e3ex10;this[_0x3d0b[17]]= 0}_createClass(Fragment,[{key:_0x3d0b[15],value:function _0x8e3ex11(_0x8e3ex12){if(this[_0x3d0b[17]]>= this[_0x3d0b[11]]){return false};this[_0x3d0b[20]][this[_0x3d0b[17]]]= _0x8e3ex12;this[_0x3d0b[17]]+= 1;return true}},{key:_0x3d0b[19],value:function _0x8e3ex22(){return this[_0x3d0b[17]]=== this[_0x3d0b[11]]}},{key:_0x3d0b[16],value:function _0x8e3ex14(_0x8e3ex15){return this[_0x3d0b[20]][_0x8e3ex15]}}]);return Fragment}();var uncompression_lz77=function uncompression_lz77(_0x8e3ex24){var _0x8e3ex25=_0x8e3ex24[_0x3d0b[0]];var _0x8e3ex1c=0;var _0x8e3ex20= new Buffer(_0x8e3ex24[_0x3d0b[0]]);while(_0x8e3ex1c< _0x8e3ex25){var _0x8e3ex26=_0x8e3ex24[_0x8e3ex1c];_0x8e3ex1c+= 1;if(_0x8e3ex26== 0){_0x8e3ex20[_0x3d0b[15]](_0x8e3ex26)}else {if(_0x8e3ex26<= 8){for(var _0x8e3ex5=_0x8e3ex1c;_0x8e3ex5< _0x8e3ex1c+ _0x8e3ex26;_0x8e3ex5++){_0x8e3ex20[_0x3d0b[15]](_0x8e3ex24[_0x8e3ex5])};_0x8e3ex1c+= _0x8e3ex26}else {if(_0x8e3ex26<= 0x7f){_0x8e3ex20[_0x3d0b[15]](_0x8e3ex26)}else {if(_0x8e3ex26<= 0xbf){var _0x8e3ex27=_0x8e3ex24[_0x8e3ex1c];_0x8e3ex1c+= 1;var _0x8e3ex28=(_0x8e3ex26<< 8| _0x8e3ex27)>> 3& 0x7ff;var _0x8e3ex29=(_0x8e3ex27& 0x7)+ 3;var _0x8e3ex2a=_0x8e3ex20[_0x3d0b[17]]();for(var _0x8e3ex5=0;_0x8e3ex5< _0x8e3ex29;_0x8e3ex5++){_0x8e3ex20[_0x3d0b[15]](_0x8e3ex20[_0x3d0b[16]](_0x8e3ex2a- _0x8e3ex28));_0x8e3ex2a+= 1}}else {_0x8e3ex20[_0x3d0b[15]](32);_0x8e3ex20[_0x3d0b[15]](_0x8e3ex26^ 0x80)}}}}};return _0x8e3ex20};var MobiFile=function(){function MobiFile(_0x8e3ex24){_classCallCheck(this,MobiFile);this[_0x3d0b[23]]= new DataView(_0x8e3ex24);this[_0x3d0b[20]]= this[_0x3d0b[23]][_0x3d0b[20]];this[_0x3d0b[24]]= 0;this[_0x3d0b[25]]= null}_createClass(MobiFile,[{key:_0x3d0b[26],value:function _0x8e3ex2c(){}},{key:_0x3d0b[27],value:function _0x8e3ex2d(){var _0x8e3ex2e=this[_0x3d0b[23]][_0x3d0b[27]](this[_0x3d0b[24]]);this[_0x3d0b[24]]+= 1;return _0x8e3ex2e}},{key:_0x3d0b[28],value:function _0x8e3ex2f(){var _0x8e3ex2e=this[_0x3d0b[23]][_0x3d0b[28]](this[_0x3d0b[24]]);this[_0x3d0b[24]]+= 2;return _0x8e3ex2e}},{key:_0x3d0b[29],value:function _0x8e3ex30(){var _0x8e3ex2e=this[_0x3d0b[23]][_0x3d0b[29]](this[_0x3d0b[24]]);this[_0x3d0b[24]]+= 4;return _0x8e3ex2e}},{key:_0x3d0b[30],value:function _0x8e3ex31(_0x8e3ex18){var _0x8e3ex2e=ab2str(this[_0x3d0b[20]][_0x3d0b[22]](this[_0x3d0b[24]],this[_0x3d0b[24]]+ _0x8e3ex18));this[_0x3d0b[24]]+= _0x8e3ex18;return _0x8e3ex2e}},{key:_0x3d0b[31],value:function _0x8e3ex32(_0x8e3ex18){this[_0x3d0b[24]]+= _0x8e3ex18}},{key:_0x3d0b[32],value:function _0x8e3ex33(_0x8e3ex34){this[_0x3d0b[24]]= _0x8e3ex34}},{key:_0x3d0b[33],value:function _0x8e3ex35(_0x8e3ex24,_0x8e3ex36){var _0x8e3ex37=_0x8e3ex24[_0x3d0b[0]]- 1;var _0x8e3ex38=0;for(var _0x8e3ex5=15;_0x8e3ex5> 0;_0x8e3ex5--){if(_0x8e3ex36& 1<< _0x8e3ex5){var _0x8e3ex39=this[_0x3d0b[34]](_0x8e3ex24,_0x8e3ex37);var _0x8e3ex18=_0x8e3ex39[0];var _0x8e3ex3a=_0x8e3ex39[1];_0x8e3ex37= _0x8e3ex39[2];_0x8e3ex37-= _0x8e3ex18- _0x8e3ex3a;_0x8e3ex38+= _0x8e3ex18}};if(_0x8e3ex36& 1){var _0x8e3ex3b=_0x8e3ex24[_0x8e3ex37];_0x8e3ex38+= (_0x8e3ex3b& 0x3)+ 1};return _0x8e3ex38}},{key:_0x3d0b[34],value:function _0x8e3ex3c(_0x8e3ex24,_0x8e3ex37){var _0x8e3ex3a=0;var _0x8e3ex18=0;var _0x8e3ex3d=0;var _0x8e3ex3e=0x7f;var _0x8e3ex3f=0x80;var _0x8e3ex40=0;for(var _0x8e3ex5=0;;_0x8e3ex5++){var _0x8e3ex12=_0x8e3ex24[_0x8e3ex37];_0x8e3ex18|= (_0x8e3ex12& _0x8e3ex3e)<< _0x8e3ex40;_0x8e3ex40+= 7;_0x8e3ex3a+= 1;_0x8e3ex3d+= 1;_0x8e3ex37-= 1;var _0x8e3ex41=_0x8e3ex12& _0x8e3ex3f;if(_0x8e3ex3d>= 4|| _0x8e3ex41> 0){break}};return [_0x8e3ex18,_0x8e3ex3a,_0x8e3ex37]}},{key:_0x3d0b[35],value:function _0x8e3ex42(){var _0x8e3ex43=this[_0x3d0b[37]][_0x3d0b[36]];var _0x8e3ex1e=[];for(var _0x8e3ex5=1;_0x8e3ex5<= _0x8e3ex43;_0x8e3ex5++){_0x8e3ex1e[_0x3d0b[14]](this[_0x3d0b[38]](_0x8e3ex5))};var _0x8e3ex44=combine_uint8array(_0x8e3ex1e);return ab2str(_0x8e3ex44)}},{key:_0x3d0b[38],value:function _0x8e3ex45(_0x8e3ex5){var _0x8e3ex36=this[_0x3d0b[40]][_0x3d0b[39]];var _0x8e3ex46=this[_0x3d0b[41]][_0x8e3ex5][_0x3d0b[24]];var _0x8e3ex47=this[_0x3d0b[41]][_0x8e3ex5+ 1][_0x3d0b[24]];var _0x8e3ex24= new Uint8Array(this[_0x3d0b[20]][_0x3d0b[22]](_0x8e3ex46,_0x8e3ex47));var _0x8e3ex48=this[_0x3d0b[33]](_0x8e3ex24,_0x8e3ex36);_0x8e3ex24= new Uint8Array(this[_0x3d0b[20]][_0x3d0b[22]](_0x8e3ex46,_0x8e3ex47- _0x8e3ex48));if(this[_0x3d0b[37]][_0x3d0b[42]]=== 2){var _0x8e3ex20=uncompression_lz77(_0x8e3ex24);return _0x8e3ex20[_0x3d0b[18]]()}else {return _0x8e3ex24}}},{key:_0x3d0b[43],value:function _0x8e3ex49(_0x8e3ex15){var _0x8e3ex4a=this[_0x3d0b[40]][_0x3d0b[44]];var _0x8e3ex46=this[_0x3d0b[41]][_0x8e3ex4a+ _0x8e3ex15][_0x3d0b[24]];var _0x8e3ex47=this[_0x3d0b[41]][_0x8e3ex4a+ _0x8e3ex15+ 1][_0x3d0b[24]];var _0x8e3ex24= new Uint8Array(this[_0x3d0b[20]][_0x3d0b[22]](_0x8e3ex46,_0x8e3ex47));return new Blob([_0x8e3ex24[_0x3d0b[20]]])}},{key:_0x3d0b[45],value:function _0x8e3ex4b(){this[_0x3d0b[25]]= this[_0x3d0b[46]]();this[_0x3d0b[41]]= this[_0x3d0b[47]]();this[_0x3d0b[48]]()}},{key:_0x3d0b[46],value:function _0x8e3ex4c(){var _0x8e3ex4d={};_0x8e3ex4d[_0x3d0b[49]]= this[_0x3d0b[30]](32);_0x8e3ex4d[_0x3d0b[50]]= this[_0x3d0b[28]]();_0x8e3ex4d[_0x3d0b[51]]= this[_0x3d0b[28]]();_0x8e3ex4d[_0x3d0b[52]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[53]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[54]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[55]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[56]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[57]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[58]]= this[_0x3d0b[30]](4);_0x8e3ex4d[_0x3d0b[59]]= this[_0x3d0b[30]](4);_0x8e3ex4d[_0x3d0b[60]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[61]]= this[_0x3d0b[29]]();_0x8e3ex4d[_0x3d0b[62]]= this[_0x3d0b[28]]();return _0x8e3ex4d}},{key:_0x3d0b[47],value:function _0x8e3ex4e(){var _0x8e3ex4f=[];for(var _0x8e3ex5=0;_0x8e3ex5< this[_0x3d0b[25]][_0x3d0b[62]];_0x8e3ex5++){var _0x8e3ex50={};_0x8e3ex50[_0x3d0b[24]]= this[_0x3d0b[29]]();_0x8e3ex50[_0x3d0b[50]]= this[_0x3d0b[29]]();_0x8e3ex4f[_0x3d0b[14]](_0x8e3ex50)};return _0x8e3ex4f}},{key:_0x3d0b[48],value:function _0x8e3ex51(){this[_0x3d0b[37]]= this[_0x3d0b[63]]();this[_0x3d0b[40]]= this[_0x3d0b[64]]()}},{key:_0x3d0b[63],value:function _0x8e3ex52(){var _0x8e3ex53={};var _0x8e3ex54=this[_0x3d0b[41]][0];this[_0x3d0b[32]](_0x8e3ex54[_0x3d0b[24]]);_0x8e3ex53[_0x3d0b[42]]= this[_0x3d0b[28]]();this[_0x3d0b[31]](2);_0x8e3ex53[_0x3d0b[65]]= this[_0x3d0b[29]]();_0x8e3ex53[_0x3d0b[36]]= this[_0x3d0b[28]]();_0x8e3ex53[_0x3d0b[66]]= this[_0x3d0b[28]]();_0x8e3ex53[_0x3d0b[67]]= this[_0x3d0b[28]]();this[_0x3d0b[31]](2);return _0x8e3ex53}},{key:_0x3d0b[64],value:function _0x8e3ex55(){var _0x8e3ex56={};var _0x8e3ex57=this[_0x3d0b[24]];_0x8e3ex56[_0x3d0b[68]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[69]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[70]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[71]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[60]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[72]]= this[_0x3d0b[29]]();this[_0x3d0b[31]](40);_0x8e3ex56[_0x3d0b[73]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[74]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[75]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[76]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[77]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[78]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[79]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[44]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[80]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[81]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[82]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[83]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[84]]= this[_0x3d0b[29]]();this[_0x3d0b[31]](36);_0x8e3ex56[_0x3d0b[85]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[86]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[87]]= this[_0x3d0b[29]]();_0x8e3ex56[_0x3d0b[88]]= this[_0x3d0b[29]]();this[_0x3d0b[31]](8);this[_0x3d0b[31]](4);this[_0x3d0b[31]](46);_0x8e3ex56[_0x3d0b[39]]= this[_0x3d0b[28]]();this[_0x3d0b[32]](_0x8e3ex57+ _0x8e3ex56[_0x3d0b[69]]);return _0x8e3ex56}},{key:_0x3d0b[89],value:function _0x8e3ex58(){return {}}},{key:_0x3d0b[90],value:function _0x8e3ex59(_0x8e3ex5a){this[_0x3d0b[45]]();var _0x8e3ex5b=this[_0x3d0b[35]]();var _0x8e3ex5c=document[_0x3d0b[91]](_0x8e3ex5a);while(_0x8e3ex5c[_0x3d0b[92]]){_0x8e3ex5c[_0x3d0b[93]](_0x8e3ex5c[_0x3d0b[92]])};var _0x8e3ex5d=domParser[_0x3d0b[95]](_0x8e3ex5b,_0x3d0b[94]);_0x8e3ex5d[_0x3d0b[99]][_0x3d0b[98]][_0x3d0b[97]](function(_0x8e3ex5e){if(_0x8e3ex5e instanceof Element){_0x8e3ex5c[_0x3d0b[96]](_0x8e3ex5e)}});var _0x8e3ex5f=_0x8e3ex5c[_0x3d0b[101]](_0x3d0b[100]);for(var _0x8e3ex5=0;_0x8e3ex5< _0x8e3ex5f[_0x3d0b[0]];_0x8e3ex5++){this[_0x3d0b[102]](_0x8e3ex5f,_0x8e3ex5)}}},{key:_0x3d0b[102],value:function _0x8e3ex60(_0x8e3ex5f,_0x8e3ex5){var _0x8e3ex61=_0x8e3ex5f[_0x8e3ex5];var _0x8e3ex15=+_0x8e3ex61[_0x3d0b[104]](_0x3d0b[103]);var _0x8e3ex62=this[_0x3d0b[43]](_0x8e3ex15- 1);var _0x8e3ex63= new FileReader();_0x8e3ex63[_0x3d0b[105]]= function(_0x8e3ex64){_0x8e3ex61[_0x3d0b[106]]= _0x8e3ex64[_0x3d0b[108]][_0x3d0b[107]]};_0x8e3ex63[_0x3d0b[109]](_0x8e3ex62)}}]);return MobiFile}() -------------------------------------------------------------------------------- /application/public/opds-opensearch.xml: -------------------------------------------------------------------------------- 1 | 2 | Библиотека 3 | Библиотека 4 | 5 | 6 | 7 | /opds/search 8 | Библиотека 9 | /favicon.ico 10 | 11 | 12 | open 13 | false 14 | * 15 | UTF-8 16 | UTF-8 17 | 18 | -------------------------------------------------------------------------------- /application/public/opds-opensearch.xml.php: -------------------------------------------------------------------------------- 1 | 5 | Библиотека 6 | Библиотека 7 | 8 | 9 | 10 | $webroot/opds/search 11 | Библиотека 12 | /favicon.ico 13 | 14 | 15 | open 16 | false 17 | * 18 | UTF-8 19 | UTF-8 20 | 21 | __HTML 22 | ?> -------------------------------------------------------------------------------- /application/public/save_position.php: -------------------------------------------------------------------------------- 1 | prepare("DELETE FROM progress WHERE user_uuid=:uuid AND bookid=:id"); 18 | $stmt->bindParam(":uuid", $user_uuid); 19 | $stmt->bindParam(":id", $bookid); 20 | $stmt->execute(); 21 | die(); 22 | } 23 | 24 | $stmt = $dbh->prepare("INSERT INTO progress (user_uuid, bookid, pos) VALUES (:uuid, :id, :pos) ON CONFLICT(user_uuid, bookid) DO UPDATE set pos=:pos2"); 25 | $stmt->bindParam(":uuid", $user_uuid); 26 | $stmt->bindParam(":id", $bookid); 27 | $stmt->bindParam(":pos", $pos); 28 | $stmt->bindParam(":pos2", $pos); 29 | $stmt->execute(); 30 | -------------------------------------------------------------------------------- /application/public/usr.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT libbook.Title BookTitle, libfilename.filename, libbook.filetype, 11 | CONCAT(libavtorname.LastName, ' ', libavtorname.FirstName) author_name 12 | FROM libbook 13 | LEFT JOIN libavtor USING(BookId) 14 | LEFT JOIN libfilename USING(BookId) 15 | LEFT JOIN libavtorname USING(AvtorId) 16 | WHERE libbook.BookId=:id"); 17 | $stmt->bindParam(":id", $id); 18 | $stmt->execute(); 19 | $book = $stmt->fetch(); 20 | 21 | 22 | $stmt = $dbh->prepare("SELECT * FROM book_zip WHERE $id BETWEEN start_id AND end_id AND usr=1"); 23 | $stmt->execute(); 24 | $zip_name = $stmt->fetch()->filename; 25 | $zip = new ZipArchive(); 26 | 27 | if ($zip->open(ROOT_PATH . "flibusta/" . $zip_name)) { 28 | $filename = $book->author_name . " - " . $book->booktitle . " " . $id . "." . $book->filename . "." . trim($book->filetype); 29 | header('Content-Description: File Transfer'); 30 | header('Content-Type: application/octet-stream'); 31 | header('Content-Disposition: attachment; filename=' . basename(rawurlencode($filename))); 32 | header('Content-Transfer-Encoding: binary'); 33 | header('Expires: 0'); 34 | header('Cache-Control: must-revalidate'); 35 | header('Pragma: public'); 36 | 37 | if (isset($book->filename)) { 38 | echo $zip->getFromName($book->filename); 39 | } else { 40 | echo $zip->getFromName("$id." . trim($book->filetype)); 41 | } 42 | $zip->close(); 43 | } else { 44 | echo "NO ZIP"; 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /application/public/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /application/public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /application/public/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /application/public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /application/public/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /application/public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /application/public/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /application/public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /application/public/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /application/public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /application/public/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /application/public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/application/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /application/renderer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | description != '') { 11 | echo ""; 12 | } 13 | 14 | if ($url->title != '') { 15 | $title = $url->title; 16 | } else { 17 | $title = 'Библиотека'; 18 | } 19 | echo "$title"; 20 | include_once(ROOT_PATH . 'webroot.php'); 21 | echo <<< __HTML 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | __HTML 31 | ?> 32 | 58 | 59 | 60 | mod) { 69 | case '': 70 | $c1 = 'active'; 71 | break; 72 | case 'genres': 73 | $c2 = 'active'; 74 | break; 75 | case 'genres': 76 | $c3 = 'active'; 77 | break; 78 | case 'authors': 79 | $c4 = 'active'; 80 | break; 81 | case 'fav': 82 | $c5 = 'active'; 83 | break; 84 | case 'service': 85 | $c6 = 'active'; 86 | break; 87 | default: 88 | $c1 = 'active'; 89 | } 90 | 91 | 92 | 93 | echo <<< __HTML 94 | 95 | 96 | 97 |
    98 | 121 |
    122 |
    123 |
    124 | module)) { 126 | // echo "

    $url->title

    "; 127 | include($url->module); 128 | } else { 129 | echo 'Раздел не найден', 'Вы ввели неверный адрес, либо раздел находится в разработке.'; 130 | header("HTTP/1.0 404 Not Found"); 131 | } 132 | ?> 133 |
     
    134 |
    135 | 136 | 137 | 138 |
    139 | 143 |
    144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /application/tools/app_db_converter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Fixes a MySQL dump made with the right format so it can be directly 5 | imported to a new PostgreSQL database. 6 | 7 | Dump using: 8 | mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root databasename 9 | """ 10 | 11 | import re 12 | import sys 13 | import os 14 | import time 15 | import subprocess 16 | 17 | 18 | def parse(input_filename, output_filename): 19 | "Feed it a file, and it'll output a fixed one" 20 | 21 | # State storage 22 | if input_filename == "-": 23 | num_lines = -1 24 | else: 25 | num_lines = int(subprocess.check_output(["wc", "-l", input_filename]).strip().split()[0]) 26 | tables = {} 27 | current_table = None 28 | creation_lines = [] 29 | enum_types = [] 30 | foreign_key_lines = [] 31 | fulltext_key_lines = [] 32 | sequence_lines = [] 33 | cast_lines = [] 34 | num_inserts = 0 35 | started = time.time() 36 | 37 | # Open output file and write header. Logging file handle will be stdout 38 | # unless we're writing output to stdout, in which case NO PROGRESS FOR YOU. 39 | if output_filename == "-": 40 | output = sys.stdout 41 | logging = open(os.devnull, "w") 42 | else: 43 | output = open(output_filename, "w") 44 | logging = sys.stdout 45 | 46 | if input_filename == "-": 47 | input_fh = sys.stdin 48 | else: 49 | input_fh = open(input_filename) 50 | 51 | 52 | output.write("-- Converted by db_converter\n") 53 | output.write("START TRANSACTION;\n") 54 | output.write("SET standard_conforming_strings=off;\n") 55 | output.write("SET escape_string_warning=off;\n") 56 | output.write("SET CONSTRAINTS ALL DEFERRED;\n\n") 57 | 58 | for i, line in enumerate(input_fh): 59 | time_taken = time.time() - started 60 | percentage_done = (i+1) / float(num_lines) 61 | secs_left = (time_taken / percentage_done) - time_taken 62 | logging.write("\rLine %i (of %s: %.2f%%) [%s tables] [%s inserts] [ETA: %i min %i sec]" % ( 63 | i + 1, 64 | num_lines, 65 | ((i+1)/float(num_lines))*100, 66 | len(tables), 67 | num_inserts, 68 | secs_left // 60, 69 | secs_left % 60, 70 | )) 71 | logging.flush() 72 | line = line.strip().replace(r"\\", "WUBWUBREALSLASHWUB").replace(r"\'", "''").replace("WUBWUBREALSLASHWUB", r"\\") 73 | # Ignore comment lines 74 | if line.startswith("--") or line.startswith("/*") or line.startswith("LOCK TABLES") or line.startswith("DROP TABLE") or line.startswith("UNLOCK TABLES") or not line: 75 | continue 76 | 77 | # Outside of anything handling 78 | if current_table is None: 79 | # Start of a table creation statement? 80 | if line.startswith("CREATE TABLE"): 81 | current_table = line.split('"')[1] 82 | tables[current_table] = {"columns": []} 83 | creation_lines = [] 84 | # Inserting data into a table? 85 | elif line.startswith("INSERT INTO"): 86 | output.write(line.replace("'0000-00-00 00:00:00'", "NULL") + "\n") 87 | num_inserts += 1 88 | # ??? 89 | elif line.startswith("TRUNCATE"): 90 | output.write(line.replace("'0000-00-00 00:00:00'", "NULL") + "\n") 91 | num_inserts += 1 92 | else: 93 | print("\n ! Unknown line in main body: {}".format(line)) 94 | 95 | # Inside-create-statement handling 96 | else: 97 | # Is it a column? 98 | if line.startswith('"'): 99 | useless, name, definition = line.strip(",").split('"',2) 100 | try: 101 | type, extra = definition.strip().split(" ", 1) 102 | 103 | # This must be a tricky enum 104 | if ')' in extra: 105 | type, extra = definition.strip().split(")") 106 | 107 | except ValueError: 108 | type = definition.strip() 109 | extra = "" 110 | extra = re.sub(r"CHARACTER SET [\w\d]+\s*", "", extra.replace("unsigned", "")) 111 | extra = re.sub(r"COLLATE [\w\d]+\s*", "", extra.replace("unsigned", "")) 112 | 113 | # See if it needs type conversion 114 | final_type = None 115 | set_sequence = None 116 | if type.startswith("tinyint("): 117 | type = "int4" 118 | set_sequence = True 119 | final_type = "boolean" 120 | elif type.startswith("int("): 121 | type = "integer" 122 | set_sequence = True 123 | elif type.startswith("bigint("): 124 | type = "bigint" 125 | set_sequence = True 126 | elif type == "longtext": 127 | type = "text" 128 | elif type == "mediumtext": 129 | type = "text" 130 | elif type == "tinytext": 131 | type = "text" 132 | elif type.startswith("varchar("): 133 | size = int(type.split("(")[1].rstrip(")")) 134 | type = "varchar(%s)" % (size * 2) 135 | elif type.startswith("smallint("): 136 | type = "int2" 137 | set_sequence = True 138 | elif type == "datetime": 139 | type = "timestamp with time zone" 140 | elif type == "double": 141 | type = "double precision" 142 | elif type.endswith("blob"): 143 | type = "bytea" 144 | elif type.startswith("enum(") or type.startswith("set("): 145 | 146 | types_str = type.split("(")[1].rstrip(")").rstrip('"') 147 | types_arr = [type_str.strip('\'') for type_str in types_str.split(",")] 148 | 149 | # Considered using values to make a name, but its dodgy 150 | # enum_name = '_'.join(types_arr) 151 | enum_name = "{0}_{1}".format(current_table, name) 152 | 153 | if enum_name not in enum_types: 154 | output.write("CREATE TYPE {0} AS ENUM ({1}); \n".format(enum_name, types_str)); 155 | enum_types.append(enum_name) 156 | 157 | type = enum_name 158 | 159 | if final_type: 160 | cast_lines.append("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP DEFAULT, ALTER COLUMN \"%s\" TYPE %s USING CAST(\"%s\" as %s)" % (current_table, name, name, final_type, name, final_type)) 161 | # ID fields need sequences [if they are integers?] 162 | if name == "id" and set_sequence is True: 163 | sequence_lines.append("CREATE SEQUENCE %s_id_seq" % (current_table)) 164 | sequence_lines.append("SELECT setval('%s_id_seq', max(id)) FROM %s" % (current_table, current_table)) 165 | sequence_lines.append("ALTER TABLE \"%s\" ALTER COLUMN \"id\" SET DEFAULT nextval('%s_id_seq')" % (current_table, current_table)) 166 | # Record it 167 | creation_lines.append('"%s" %s %s' % (name, type, extra)) 168 | tables[current_table]['columns'].append((name, type, extra)) 169 | # Is it a constraint or something? 170 | elif line.startswith("PRIMARY KEY"): 171 | creation_lines.append(line.rstrip(",")) 172 | elif line.startswith("CONSTRAINT"): 173 | foreign_key_lines.append("ALTER TABLE \"%s\" ADD CONSTRAINT %s DEFERRABLE INITIALLY DEFERRED" % (current_table, line.split("CONSTRAINT")[1].strip().rstrip(","))) 174 | foreign_key_lines.append("CREATE INDEX ON \"%s\" %s" % (current_table, line.split("FOREIGN KEY")[1].split("REFERENCES")[0].strip().rstrip(","))) 175 | elif line.startswith("UNIQUE KEY"): 176 | creation_lines.append("UNIQUE (%s)" % line.split("(")[1].split(")")[0]) 177 | elif line.startswith("FULLTEXT KEY"): 178 | 179 | fulltext_keys = " || ' ' || ".join( line.split('(')[-1].split(')')[0].replace('"', '').split(',') ) 180 | fulltext_key_lines.append("CREATE INDEX ON %s USING gin(to_tsvector('english', %s))" % (current_table, fulltext_keys)) 181 | 182 | elif line.startswith("KEY"): 183 | pass 184 | # Is it the end of the table? 185 | elif line == ");": 186 | output.write("CREATE TABLE \"%s\" (\n" % current_table) 187 | for i, line in enumerate(creation_lines): 188 | output.write(" %s%s\n" % (line, "," if i != (len(creation_lines) - 1) else "")) 189 | output.write(');\n\n') 190 | current_table = None 191 | # ??? 192 | else: 193 | print("\n ! Unknown line inside table creation: {}".format(line)) 194 | 195 | 196 | # Finish file 197 | output.write("\n-- Post-data save --\n") 198 | output.write("COMMIT;\n") 199 | output.write("START TRANSACTION;\n") 200 | 201 | # Write typecasts out 202 | output.write("\n-- Typecasts --\n") 203 | for line in cast_lines: 204 | output.write("%s;\n" % line) 205 | 206 | # Write FK constraints out 207 | output.write("\n-- Foreign keys --\n") 208 | for line in foreign_key_lines: 209 | output.write("%s;\n" % line) 210 | 211 | # Write sequences out 212 | output.write("\n-- Sequences --\n") 213 | for line in sequence_lines: 214 | output.write("%s;\n" % line) 215 | 216 | # Write full-text indexkeyses out 217 | output.write("\n-- Full Text keys --\n") 218 | for line in fulltext_key_lines: 219 | output.write("%s;\n" % line) 220 | 221 | # Finish file 222 | output.write("\n") 223 | output.write("COMMIT;\n") 224 | print("") 225 | 226 | 227 | if __name__ == "__main__": 228 | parse(sys.argv[1], sys.argv[2]) 229 | -------------------------------------------------------------------------------- /application/tools/app_import_sql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | source /application/tools/dbinit.sh 3 | 4 | mkdir -p /application/sql/psql 5 | mkdir -p /application/cache/authors 6 | mkdir -p /application/cache/covers 7 | mkdir -p /application/cache/tmp 8 | 9 | echo "Распаковка sql.gz">/application/sql/status 10 | gzip -f -d /application/sql/*.gz 11 | 12 | /application/tools/app_topg lib.a.annotations_pics.sql 13 | /application/tools/app_topg lib.b.annotations_pics.sql 14 | /application/tools/app_topg lib.a.annotations.sql 15 | /application/tools/app_topg lib.b.annotations.sql 16 | /application/tools/app_topg lib.libavtorname.sql 17 | /application/tools/app_topg lib.libavtor.sql 18 | /application/tools/app_topg lib.libbook.sql 19 | /application/tools/app_topg lib.libfilename.sql 20 | /application/tools/app_topg lib.libgenrelist.sql 21 | /application/tools/app_topg lib.libgenre.sql 22 | /application/tools/app_topg lib.libjoinedbooks.sql 23 | /application/tools/app_topg lib.librate.sql 24 | /application/tools/app_topg lib.librecs.sql 25 | /application/tools/app_topg lib.libseqname.sql 26 | /application/tools/app_topg lib.libseq.sql 27 | /application/tools/app_topg lib.libtranslator.sql 28 | /application/tools/app_topg lib.reviews.sql 29 | 30 | echo "Подчистка БД. Стираем авторов, серии и жанры у которых нет ни одной книги" >>/application/sql/status 31 | $SQL_CMD -f /application/tools/cleanup_db.sql 32 | 33 | echo "Обновление полнотекстовых индексов">>/application/sql/status 34 | $SQL_CMD -f /application/tools/update_vectors.sql 35 | 36 | echo "Создание индекса zip-файлов">>/application/sql/status 37 | php /application/tools/app_update_zip_list.php 38 | 39 | echo "">/application/sql/status 40 | 41 | -------------------------------------------------------------------------------- /application/tools/app_reindex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Создание индекса zip-файлов">>/application/sql/status 3 | php /application/tools/app_update_zip_list.php 4 | 5 | echo "">/application/sql/status 6 | 7 | 8 | -------------------------------------------------------------------------------- /application/tools/app_topg: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | echo "Конвертация $1">>/application/sql/status 3 | if ! grep -q INSERT "/application/sql/$1"; then 4 | echo "Empty table">>/application/sql/status; 5 | exit 1 6 | fi 7 | 8 | 9 | 10 | xtable="cat /application/sql/$1|grep 'CREATE TABLE'|awk '{print \$3}'| sed 's/^.//;s/.$//'" 11 | table=$(eval "$xtable") 12 | 13 | echo $1 14 | echo "TRUNCATE $table;">/application/sql/tmp.sql 15 | echo $table 16 | cat /application/sql/$1|grep INSERT|tr -d \`|sed "s/$table/$table/g">>/application/sql/tmp.sql 17 | /application/tools/app_db_converter.py /application/sql/tmp.sql /application/sql/psql/$1 18 | rm /application/sql/tmp.sql 19 | 20 | echo "Импорт $1">>/application/sql/status 21 | $SQL_CMD -f /application/sql/psql/$1 22 | 23 | -------------------------------------------------------------------------------- /application/tools/app_update_zip_list.php: -------------------------------------------------------------------------------- 1 | prepare("TRUNCATE book_zip;"); 7 | $stmt->execute(); 8 | 9 | $dbh->beginTransaction(); 10 | 11 | while (false !== ($entry = readdir($handle))) { 12 | if (strpos($entry, "-") !== false && strpos($entry, ".zip") !== false && substr($entry, -9) !== ".zip.part") { 13 | $dt = str_replace(".zip", "", $entry); 14 | $dt = str_replace("f.n.", "f.n-", $dt); 15 | $dt = str_replace("f.fb2.", "f.n-", $dt); 16 | echo "[$dt]"; 17 | $fn = explode("-", $dt); 18 | $u = 1; 19 | if (strpos($entry, "fb2") !== false) { 20 | $u = 0; 21 | } 22 | if (strpos($entry, "d.fb2-009") !== false) { 23 | } else { 24 | $stmt = $dbh->prepare("INSERT INTO book_zip (filename, start_id, end_id, usr) VALUES (:fn, :start, :end, :usr)"); 25 | $stmt->bindParam(":fn", $entry); 26 | $stmt->bindParam(":start", $fn[1]); 27 | $stmt->bindParam(":end", $fn[2]); 28 | $stmt->bindParam(":usr", $u); 29 | $stmt->execute(); 30 | } 31 | } 32 | echo "\n"; 33 | } 34 | $dbh->commit(); 35 | closedir($handle); 36 | } 37 | -------------------------------------------------------------------------------- /application/tools/cleanup_db.sql: -------------------------------------------------------------------------------- 1 | delete from libgenrelist where not exists (select 1 from libgenre where libgenrelist.genreid = libgenre.genreid); 2 | delete from libseqname where not exists ( select 1 from libseq where libseqname.seqid = libseq.seqid); 3 | delete from libavtorname where not exists (select 1 from libavtor where libavtorname.avtorid = libavtor.avtorid); 4 | -------------------------------------------------------------------------------- /application/tools/dbinit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z "$FLIBUSTA_DBNAME" ]; then 4 | export FLIBUSTA_DBNAME=flibusta 5 | fi 6 | 7 | if [ -z "$FLIBUSTA_DBHOST" ]; then 8 | export FLIBUSTA_DBHOST=postgres 9 | fi 10 | 11 | if [ -z "$FLIBUSTA_DBUSER" ]; then 12 | export FLIBUSTA_DBUSER=flibusta 13 | fi 14 | 15 | if [ -z "$FLIBUSTA_DBTYPE" ] || [ "$FLIBUSTA_DBTYPE" == 'postgres' ]; then 16 | export SQL_CMD="psql -h $FLIBUSTA_DBHOST -d $FLIBUSTA_DBNAME -U $FLIBUSTA_DBUSER" 17 | export SQL_PASSWORD_VAR=PGPASSWORD 18 | 19 | if [ ! -z "$FLIBUSTA_DBPASSWORD_FILE" ] && [ -e $FLIBUSTA_DBPASSWORD_FILE ]; then 20 | FPGPASSWORD=`cat $FLIBUSTA_DBPASSWORD_FILE` 21 | fi 22 | 23 | if [ ! -z "$FLIBUSTA_DBPASSWORD" ] && [ -z "$FPGPASSWORD" ]; then 24 | FPGPASSWORD=$FLIBUSTA_DBPASSWORD 25 | fi 26 | 27 | if [ ! -z "$FPGPASSWORD" ]; then 28 | export PGPASSWORD=$FPGPASSWORD 29 | fi 30 | 31 | if [ -z "$PGPASSWORD" ]; then 32 | export PGPASSWORD=flibusta 33 | fi 34 | fi 35 | -------------------------------------------------------------------------------- /application/tools/external_services_config/README.md: -------------------------------------------------------------------------------- 1 | # Конфигурация docker container для работы с reverse proxy и внешним сервером базы данных 2 | 3 | ## Почему Вы можете предпочесть такую конфигурацию 4 | 5 | Главное преимущество конфигурации с reverse proxy и внешним сервером базы данных- это экономия ресурсов. Как правило ресурсы NAS довольно ограничены, а дополнительные процессы потребляют память и CPU которому вероятно есть более полезное применение. 6 | 7 | "Обычный" docker-compose.yaml определяет 3 сервиса, бекенд на php-fpm , база данных Postgres и nginx в качестве front end. Вместо 2 последних можно использовать существующие сервисы, если они есть. Сервисы не зависят друг от друга, так что можмо "экстернализаровать" оба или только один. 8 | 9 | ## Работа с внешней Postgres базой данных. 10 | Есть 2 главных отличия в конфигурации внешней и "внутренней" базы данных. 11 | 1. При использовании внутренней базы данных для траффика между бекендом о БД используется default netwrok автоматически определяемая Docker engine для каждого docker-compose file. Если БД определена в другом файле, нужно явным образом разрешить коммуникацию межды сервисами. Это делается с помощью explicite network, в [docker-compose.yaml](docker-compose.yaml) из этой директории такой сетью служит postgres_db_net. Сеть с таким же именем дожна быть определана в файле определения сервиса постгресс и эта сеть должна быть прописана в списке сетей для сервиса (конфигурация практически идентична той что есть в примере в этой директории). В таком случае можно создать сеть отдельно, с помощью docker network create (Кроме того во многих GUI/ web interface есть возможность определить такую сеть без command line). Затем можно определить ее как external во всех релевантных docker compose files.. 12 | 2. Образ созданный в главном docker-compose, определяет юзера БД, таблицы, индексы и т.д. во время создания image. В случае с внешней базой все необходимое должно быть создано при запуске контейнера бекенда Флибусты. Для этого слижит скрипт flibusta_entrypoint.sh . Скрипт проверяет есть ли postgres account определенный как FLIBUSTA_DBUSER в environment docker_compose.yml (default - account flibusta, password - flibusta). Если удается подключиться с таким юзером к БД, скрипт считает что инитиализация уже состоялась при прошлых запусках. Если такая попытка не успешна, скрипт пытается инитиализировать нужные обьекты : создается счет с именем из переменной окружения FLIBUSTA_DBUSER ( default flibusta) и паролем из /run/secrets/FLIBUSTA_PWD, создается база данных и в ней все нужные таблицы, индексы и т.д. Создание нового счета и базы данных - это привeлигированные опреации, для них используется счет с именем из POSTGRES_ADMIN_USER (default - postgres) и паролем из фаила указанного в POSTGRES_ADMIN_DBPASSWORD_FILE . Проверьте что этот account имеет необходимые grants... Административный account используется толкько в скрипте и только если скрипт решает что инициализация необходима. После успешной инициализации можно заменить пароль в POSTGRES_ADMIN_DBPASSWORD_FILE на неправильный если Вы боитесь компрометации этого счета. 13 | 14 | 15 | ## Работа с reverse proxy 16 | 17 | Существуют 2 наиболее распространненых типа подключения к reverse proxy : subdomain и subdir . При подключении как subdomain, ресурс ( в данном случае библиотека) для адреса https://example.com будет доступна как https://mylib.example.com/ . При подключении как subdir адрес библиотеки https://example.com/mylib. Второй вариант конфигурации ПМСМ сложнее, и поэтому здесь обьясняется именно он. 18 | 19 | В качестве reverse proxy в примере в данной директории используется nginx . Если у Вас другой reverse proxy, вместо nginx конфигурации flibusta.conf Вам нужно будет использовать конфигурационные файлы специфичные для Вашего proxy. 20 | В конфигурации в этой директории используется вариант subdir. Для адреса она будет доступна как https://addres of my server/mylib . 21 | 22 | Для конфигурации используются следующие значения 23 | 1. /mylib - путь до "корня" библиотеки. Определяется переменной FLIBUSTA_WEBROOT в environment сервиса flibusta-fpm в docker_compose.yml 24 | 2. flibusta_net - сеть по которой backend и reverse proxy связываются, и определяется subnet 172.101.0.0/16 25 | 3. 172.101.0.101 адрес бекенда ( должен быть адресом в сети из предыдущего пункта), задается параметром ipv4_address сервиса flibusta-fpm 26 | 4. 192.168.1.0/24 -локальная сеть . Это значение используется для контроля доступа ( см. обсуждение ниже) в директиве 27 | ``` 28 | allow 192.168.1.0/24; 29 | ``` 30 | в конфигурации flibusta.conf для nginx. 31 | 5. Файлы из application/public доступны в докер контейнере nginx как /srv/mylib 32 | 33 | Шаги для конфигурации работы с reverse proxy 34 | 1. Создайте docker netwrork с именем flibusta_net и subnet 172.101.0.0/16 . Важно чтобы subnet соответствовал адресу flibusta-fpm, который прописан в nginx конфигурационном файле flibusta.conf. Добавьте эту сеть в докер контейнер nginx как external (похоже на то как она определена в docker-compose.yml из этой директории) 35 | 2. Добавьте volume /srv/mylib : 36 | 37 | ``` 38 | - /flibusta/application/public:/srv/mylib:v,ro 39 | ``` 40 | 3. Добавьте (предпологается что конфигурация внутри докера находится в /config/nginx, если нет, например часто бывает /etc/etc/config/nginx , соответственно измените mount) 41 | 42 | ``` 43 | - /flibusta/application/tools/external_services_config/flibusta.conf:/config/nginx/flibusta.conf:v,ro 44 | ``` 45 | и сделайте include конфиг файла flibusta.conf в server блок в конфигурации nginx, скорее всего конфигурация находится в /config/nginx/site-confs/default.conf или /etc/config/nginx/site-conf/default.conf 46 | 47 | ``` 48 | # main server block 49 | server { 50 | 51 | listen 443 ssl default_server; 52 | listen [::]:443 ssl default_server; 53 | http2 on; 54 | 55 | ....................... 56 | 57 | location { 58 | 59 | } 60 | 61 | location { 62 | 63 | } 64 | 65 | include /config/nginx/flibusta.conf; 66 | 67 | ....... 68 | 69 | } 70 | ``` 71 | 72 | ## Доступ к Вашему зеркалу Флибусты 73 | 74 | reverse proxy скорее всего дает доступ из интернета к Вашим сервисам ( он скорее всего и нужен чтобы был доступ к докер контейнерам на Вашем NAS). Я думаю , понятно, что предоставлять неконтролируемый доступ всему интернету к Вашему зеркалу Флибусты не очень хорошая идея. Nginx позволяет давать доступ к определенному ресурсу ( например к директории https://my address/mylib) только определенный IP адресам или только авторизированным пользователям. flibusta.conf ограничевает дотуп только локальными адресами ( из сети 192.168.1.0/24). Если subnet вашей домашней сети другой - соответственным образом измените файл. Для доступа к библеотеке из интернета для авторизованных пользователей проще всего воспользоваться механизмом Basic authenitcation ( htpassword) примерно так 75 | ``` 76 | location ^~ /mylib { 77 | #... 78 | satisfy any; 79 | allow 192.168.1.0/24; 80 | deny all; 81 | 82 | auth_basic "Flibusta forever"; 83 | auth_basic_user_file conf/htpasswd; 84 | ... 85 | } 86 | ``` 87 | 88 | -------------------------------------------------------------------------------- /application/tools/external_services_config/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | flibusta-fpm: 4 | build: phpdocker/php-fpm 5 | working_dir: /application 6 | container_name: flibusta-fpm 7 | environment: 8 | - PUID=${APPUSER_PUID} 9 | - PGID=${APPUSER_PGID} 10 | - TZ=${TIME_ZONE_VALUE} 11 | - FLIBUSTA_WEBROOT=/mylib 12 | - FLIBUSTA_DBUSER=flibusta 13 | - FLIBUSTA_DBNAME=flibusta 14 | - FLIBUSTA_DBTYPE=postgres 15 | - FLIBUSTA_DBHOST=postgresdb 16 | - FLIBUSTA_DBPASSWORD_FILE=/run/secrets/FLIBUSTA_PWD 17 | - POSTGRES_ADMIN_USER=postgres 18 | - POSTGRES_ADMIN_DBPASSWORD_FILE=/run/secrets/POSTGRES_ADMIN_PWD 19 | - DB_HOST_PINGS=3 20 | command: /application/tools/external_services_config/flibusta_entrypoint.sh 21 | networks: 22 | flibusta_net: 23 | ipv4_address: 172.101.0.101 24 | postgres_db_net: 25 | volumes: 26 | - './application:/application' 27 | - './cache:/application/cache' 28 | - './Flibusta.Net:/application/flibusta' 29 | - './FlibustaSQL:/application/sql' 30 | - './phpdocker/php-fpm/php-ini-overrides.ini:/usr/local/etc/php/conf.d/local-custom.ini' 31 | - './phpdocker/php-fpm/php-fpm.conf:/usr/local/etc/php-fpm.d/zz-php-fpm.conf' 32 | - './FlibustaSQL/lib.a.attached.zip:/application/cache/lib.a.attached.zip:ro' 33 | - './FlibustaSQL/lib.b.attached.zip:/application/cache/lib.b.attached.zip:ro' 34 | secrets: 35 | - FLIBUSTA_PWD 36 | - POSTGRES_ADMIN_PWD 37 | 38 | secrets: 39 | FLIBUSTA_PWD: 40 | file: ./secrets/flibusta_pwd.txt 41 | POSTGRES_ADMIN_PWD: 42 | file: ./secrets/postgres_admin_pwd.txt 43 | 44 | networks: 45 | postgres_db_net: 46 | external: true 47 | flibusta_net: 48 | external: true 49 | -------------------------------------------------------------------------------- /application/tools/external_services_config/flibusta.conf: -------------------------------------------------------------------------------- 1 | location ^~ /mylib { 2 | allow 192.168.1.0/24; 3 | deny all; 4 | root /srv; 5 | index index.php; 6 | try_files $uri /mylib/index.php$is_args$args; 7 | if (!-e $request_filename) { 8 | rewrite ^.*$ /mylib/index.php last; 9 | } 10 | location ~ \.php$ { 11 | add_header 'Content-Security-Policy' 'worker-src * blob:'; 12 | 13 | fastcgi_split_path_info ^\/mylib(.+?\.php)(.*)$; 14 | set $path_info $fastcgi_path_info; 15 | 16 | include fastcgi_params; 17 | fastcgi_param SCRIPT_FILENAME /application/public/$fastcgi_script_name; 18 | fastcgi_param PATH_INFO $path_info; 19 | fastcgi_param PATH_TRANSLATED /applicaition/public/$path_info; 20 | fastcgi_intercept_errors on; 21 | fastcgi_param PHP_VALUE "error_log=/var/log/nginx/application_php_errors.log"; 22 | fastcgi_buffers 16 16k; 23 | fastcgi_buffer_size 32k; 24 | fastcgi_pass 172.101.0.101:9000; 25 | fastcgi_index index.php; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /application/tools/external_services_config/flibusta_entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . /application/tools/dbinit.sh 3 | 4 | if [ -z "$DB_HOST_PINGS" ]; then 5 | DB_HOST_PINGS=3 6 | fi 7 | 8 | for i in `seq 1 $DB_HOST_PINGS` 9 | do 10 | ping -q -c 1 $FLIBUSTA_DBHOST 11 | EXITCODE=$? 12 | if [ $EXITCODE -eq 0 ]; then 13 | break 14 | fi 15 | if [ $i -lt $DB_HOST_PINGS ]; then 16 | sleep 1 17 | fi 18 | done 19 | 20 | if [ $EXITCODE -ne 0 ]; then 21 | echo $FLIBUSTA_DBHOST is unreachable, can not access DB, exiting 22 | exit 1 23 | fi 24 | 25 | 26 | if [ `$SQL_CMD -c "select 1 from pg_roles where rolname='flibusta'" | wc -l` -eq 0 ]; then 27 | echo attempt to connect to postgres db has failed. Trying to initialize the DB 28 | 29 | FLIBUSTA_DBPASSWORD=$PGPASSWORD 30 | 31 | if [ -z "$POSTGRES_ADMIN_USER" ]; then 32 | POSTGRES_ADMIN_USER=postgres 33 | fi 34 | 35 | if [ ! -z "$POSTGRES_ADMIN_DBPASSWORD_FILE" ] && [ -e $POSTGRES_ADMIN_DBPASSWORD_FILE ]; then 36 | TPOSTGRES_ADMIN_PASSWD=`cat $POSTGRES_ADMIN_DBPASSWORD_FILE` 37 | fi 38 | if [ ! -z "$TPOSTGRES_ADMIN_PASSWD" ]; then 39 | PGPASSWORD=$TPOSTGRES_ADMIN_PASSWD 40 | fi 41 | 42 | if [ ! -z "$POSTGRES_ADMIN_PASSWD" ]; then 43 | PGPASSWORD=$POSTGRES_ADMIN_PASSWD 44 | fi 45 | 46 | if [ -z "$PGPASSWORD" ]; then 47 | echo Attempt to initialize postgres DB has failed. Cant obtain PG admin password 48 | exit 1 49 | fi 50 | 51 | psql -h $FLIBUSTA_DBHOST -d $POSTGRES_ADMIN_USER -U $POSTGRES_ADMIN_USER -v FLIBUSTA_DBUSER="$FLIBUSTA_DBUSER" -v FLIBUSTA_DBPASSWORD="$FLIBUSTA_DBPASSWORD" -v FLIBUSTA_DBNAME="$FLIBUSTA_DBNAME" -f /application/tools/external_services_config/external_postgres_init.sql 52 | #restore flibusta password 53 | export PGPASSWORD=$FLIBUSTA_DBPASSWORD 54 | 55 | if [ `$SQL_CMD -c "select 1 from pg_roles where rolname='flibusta'" | wc -l` -eq 0 ]; then 56 | echo "Can't connect to the DB after initialization attempt, exiting" 57 | exit 1 58 | fi 59 | fi 60 | 61 | exec php-fpm -------------------------------------------------------------------------------- /application/tools/update_vectors.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO libavtorname_ts 2 | select avtorid, to_tsvector('russian', concat(lastname, ' ', middlename, ' ', firstname, ' ', nickname)) vector 3 | FROM libavtorname 4 | ON CONFLICT DO NOTHING; 5 | 6 | INSERT INTO libbook_ts 7 | select bookid, to_tsvector('russian',title) vector 8 | FROM libbook 9 | ON CONFLICT DO NOTHING; 10 | 11 | INSERT INTO libseqname_ts 12 | select seqid, to_tsvector('russian', seqname) vector 13 | FROM libseqname 14 | ON CONFLICT DO NOTHING; 15 | 16 | -------------------------------------------------------------------------------- /application/webroot.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blob/1: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /blob/x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/blob/x1.png -------------------------------------------------------------------------------- /blob/x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/blob/x2.png -------------------------------------------------------------------------------- /blob/x3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/blob/x3.png -------------------------------------------------------------------------------- /blob/x4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/blob/x4.png -------------------------------------------------------------------------------- /cache/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/cache/.gitkeep -------------------------------------------------------------------------------- /cache/authors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/cache/authors/.gitkeep -------------------------------------------------------------------------------- /cache/covers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/cache/covers/.gitkeep -------------------------------------------------------------------------------- /cache/tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/cache/tmp/.gitkeep -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | services: 3 | postgres: 4 | build: phpdocker/pg 5 | working_dir: /application 6 | volumes: 7 | - 'db-data:/var/lib/postgresql/data' 8 | - './application:/application' 9 | environment: 10 | - POSTGRES_USER=flibusta 11 | - POSTGRES_PASSWORD=flibusta 12 | - POSTGRES_DB=flibusta 13 | ports: 14 | - '27101:5432' 15 | 16 | webserver: 17 | image: 'nginx:alpine' 18 | working_dir: /application 19 | volumes: 20 | - './application:/application' 21 | - './cache:/application/cache' 22 | - './Flibusta.Net:/application/flibusta' 23 | - './phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf' 24 | ports: 25 | - '27100:80' 26 | 27 | php-fpm: 28 | build: phpdocker/php-fpm 29 | working_dir: /application 30 | volumes: 31 | - './application:/application' 32 | - './cache:/application/cache' 33 | - './Flibusta.Net:/application/flibusta' 34 | - './FlibustaSQL:/application/sql' 35 | - './phpdocker/php-fpm/php-ini-overrides.ini:/usr/local/etc/php/conf.d/local-custom.ini' 36 | - './phpdocker/php-fpm/php-fpm.conf:/usr/local/etc/php-fpm.d/zz-php-fpm.conf' 37 | environment: 38 | - FLIBUSTA_DBUSER=flibusta 39 | - FLIBUSTA_DBNAME=flibusta 40 | - FLIBUSTA_DBTYPE=postgres 41 | - FLIBUSTA_DBHOST=postgres 42 | - FLIBUSTA_DBPASSWORD_FILE=/run/secrets/FLIBUSTA_PWD 43 | secrets: 44 | - FLIBUSTA_PWD 45 | 46 | 47 | volumes: 48 | db-data: 49 | secrets: 50 | FLIBUSTA_PWD: 51 | file: ./secrets/flibusta_pwd.txt -------------------------------------------------------------------------------- /getcovers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #zipchkcdm='zip -T' 4 | zipchkcmd='7z -bsp0 -bso0 t' 5 | 6 | echo "Backup old zip files" 7 | mv cache/lib.a.attached.zip cache/lib.a.attached.zip.old 8 | mv cache/lib.b.attached.zip cache/lib.b.attached.zip.old 9 | 10 | wget --directory-prefix=cache -c -nc https://flibusta.is/sql/lib.a.attached.zip 11 | res=$? 12 | if test "$res" != "0"; then 13 | echo "the wget command failed with: $res" 14 | echo "Restore lib.a.attached.zip" 15 | mv cache/lib.a.attached.zip.old cache/lib.a.attached.zip 16 | fi 17 | wget --directory-prefix=cache -c -nc https://flibusta.is/sql/lib.b.attached.zip 18 | res=$? 19 | if test "$res" != "0"; then 20 | echo "the wget command failed with: $res" 21 | echo "Restore lib.b.attached.zip" 22 | mv cache/lib.b.attached.zip.old cache/lib.b.attached.zip 23 | fi 24 | 25 | 26 | eval $zipchkcmd cache/lib.a.attached.zip 27 | res=$? 28 | if test "$res" == "0"; then 29 | echo "Remove backup lib.a.attached.zip" 30 | rm cache/lib.a.attached.zip.old 31 | fi 32 | 33 | eval $zipchkcmd cache/lib.b.attached.zip 34 | res=$? 35 | if test "$res" == "0"; then 36 | echo "Remove backup lib.b.attached.zip" 37 | rm cache/lib.b.attached.zip.old 38 | fi 39 | 40 | -------------------------------------------------------------------------------- /getsql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libavtor.sql.gz 3 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libtranslator.sql.gz 4 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libavtorname.sql.gz 5 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libbook.sql.gz 6 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libfilename.sql.gz 7 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libgenre.sql.gz 8 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libgenrelist.sql.gz 9 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libjoinedbooks.sql.gz 10 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.librate.sql.gz 11 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.librecs.sql.gz 12 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libseqname.sql.gz 13 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.libseq.sql.gz 14 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.reviews.sql.gz 15 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.b.annotations.sql.gz 16 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.a.annotations.sql.gz 17 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.b.annotations_pics.sql.gz 18 | wget --directory-prefix=FlibustaSQL -c -nc http://flibusta.is/sql/lib.a.annotations_pics.sql.gz 19 | docker exec $(docker ps -q --filter "ancestor=flibusta_php-fpm") /application/tools/app_import_sql.sh 20 | -------------------------------------------------------------------------------- /phpdocker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default; 3 | 4 | client_max_body_size 508M; 5 | 6 | access_log /var/log/nginx/application.access.log; 7 | 8 | 9 | root /application/public; 10 | index index.php; 11 | 12 | # try to serve file directly, fallback to index.php 13 | location / { 14 | try_files $uri /index.php$is_args$args; 15 | } 16 | 17 | if (!-e $request_filename) { 18 | rewrite ^.*$ /index.php last; 19 | } 20 | 21 | location ~ \.php$ { 22 | add_header 'Content-Security-Policy' 'worker-src * blob:'; 23 | 24 | fastcgi_pass php-fpm:9000; 25 | fastcgi_index index.php; 26 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 27 | fastcgi_param PHP_VALUE "error_log=/var/log/nginx/application_php_errors.log"; 28 | fastcgi_buffers 16 16k; 29 | fastcgi_buffer_size 32k; 30 | include fastcgi_params; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /phpdocker/pg/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:16 2 | WORKDIR /docker-entrypoint-initdb.d 3 | ENV POSTGRES_DB flibusta 4 | COPY init_db.sql /docker-entrypoint-initdb.d/ 5 | -------------------------------------------------------------------------------- /phpdocker/php-fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-fpm-alpine 2 | 3 | ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ 4 | 5 | WORKDIR "/application" 6 | 7 | RUN apk --update add postgresql-client;\ 8 | apk add python3;\ 9 | apk add coreutils; 10 | RUN chmod +x /usr/local/bin/install-php-extensions; install-php-extensions gd bz2 pdo_pgsql zip 11 | -------------------------------------------------------------------------------- /phpdocker/php-fpm/php-fpm.conf: -------------------------------------------------------------------------------- 1 | php_flag[display_errors] = off 2 | php_admin_flag[log_errors] = on 3 | [www] 4 | pm.max_children = 10 5 | -------------------------------------------------------------------------------- /phpdocker/php-fpm/php-ini-overrides.ini: -------------------------------------------------------------------------------- 1 | upload_max_filesize = 500M 2 | post_max_size = 508M 3 | -------------------------------------------------------------------------------- /secrets/flibusta_pwd.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlsl/flibusta/a7e8fcccef6ef49ead5b3e4dd7e95d55b73ce7e1/secrets/flibusta_pwd.txt -------------------------------------------------------------------------------- /secrets/postgres_admin_pwd.txt: -------------------------------------------------------------------------------- 1 | flibusta -------------------------------------------------------------------------------- /update_daily.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | URL="http://flibusta.is/daily/" 3 | DEST_DIR="./Flibusta.Net" 4 | mkdir -p "$DEST_DIR" 5 | curl -s "$URL" > page.html 6 | grep -Eo 'href="f\.(fb2|n)\.[0-9\-]+\.zip"' page.html | sed 's/href="//;s/"//' > links.txt 7 | 8 | while IFS= read -r file; do 9 | wget -c -P "$DEST_DIR" "$URL$file" 10 | done < links.txt 11 | 12 | rm page.html links.txt 13 | --------------------------------------------------------------------------------