├── .dockerignore ├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── docker-image.yml ├── .gitignore ├── .gitmodules ├── .perltidyrc ├── COPYING ├── Dockerfile ├── README.md ├── jquery.dataTables.min.js ├── lib ├── LANraragi.pm ├── LANraragi │ ├── Controller │ │ ├── Api │ │ │ ├── Archive.pm │ │ │ ├── Category.pm │ │ │ ├── Database.pm │ │ │ ├── Minion.pm │ │ │ ├── Other.pm │ │ │ ├── Search.pm │ │ │ └── Shinobu.pm │ │ ├── Backup.pm │ │ ├── Batch.pm │ │ ├── Category.pm │ │ ├── Config.pm │ │ ├── Edit.pm │ │ ├── Index.pm │ │ ├── Logging.pm │ │ ├── Login.pm │ │ ├── Plugins.pm │ │ ├── Reader.pm │ │ ├── Stats.pm │ │ └── Upload.pm │ ├── Model │ │ ├── Archive.pm │ │ ├── Backup.pm │ │ ├── Category.pm │ │ ├── Config.pm │ │ ├── Plugins.pm │ │ ├── Reader.pm │ │ ├── Search.pm │ │ ├── Stats.pm │ │ └── Upload.pm │ ├── Plugin │ │ ├── Download │ │ │ ├── Chaika.pm │ │ │ └── EHentai.pm │ │ ├── Login │ │ │ ├── EHentai.pm │ │ │ ├── Fakku.pm │ │ │ └── nHentai.pm │ │ ├── Metadata │ │ │ ├── Chaika.pm │ │ │ ├── ChineseMeta.pm │ │ │ ├── CopyTags.pm │ │ │ ├── DateAdded.pm │ │ │ ├── EHentai.pm │ │ │ ├── Ehviewer.pm │ │ │ ├── Eze.pm │ │ │ ├── Fakku.pm │ │ │ ├── Hdoujin.pm │ │ │ ├── Hitomi.pm │ │ │ ├── Koromo.pm │ │ │ ├── MEMS.pm │ │ │ ├── RegexParse.pm │ │ │ └── nHentai.pm │ │ └── Scripts │ │ │ ├── BlacklistMigrate.pm │ │ │ ├── FolderToCat.pm │ │ │ ├── SourceFinder.pm │ │ │ └── nHentaiSourceConverter.pm │ └── Utils │ │ ├── Archive.pm │ │ ├── Database.pm │ │ ├── Generic.pm │ │ ├── Logging.pm │ │ ├── Minion.pm │ │ ├── Plugins.pm │ │ ├── Routing.pm │ │ ├── Tags.pm │ │ └── TempFolder.pm └── Shinobu.pm ├── log └── .gitignore ├── lrr.conf ├── package.json ├── public ├── .gitignore ├── css │ ├── .gitignore │ ├── config.css │ └── lrr.css ├── favicon.ico ├── img │ ├── .gitignore │ ├── empty.png │ ├── flubbed.gif │ ├── logo.png │ ├── noThumb.png │ ├── notfound.jpg │ ├── theme_preview │ │ ├── hachikuji.png │ │ ├── hverse.png │ │ ├── nadeko.png │ │ ├── sadpanda.png │ │ └── yotsugi.png │ └── wait_warmly.jpg ├── js │ ├── .gitignore │ ├── backup.js │ ├── batch.js │ ├── category.js │ ├── common.js │ ├── config.js │ ├── edit.js │ ├── index.js │ ├── index_datatables.js │ ├── logs.js │ ├── plugins.js │ ├── reader.js │ ├── server.js │ ├── stats.js │ └── upload.js ├── robots.txt └── themes │ ├── ex.css │ ├── g.css │ ├── modern.css │ ├── modern_clear.css │ └── modern_red.css ├── script ├── backup ├── lanraragi └── launcher.pl ├── templates ├── backup.html.tt2 ├── batch.html.tt2 ├── category.html.tt2 ├── config.html.tt2 ├── edit.html.tt2 ├── exception.production.html.ep ├── footer.html.tt2 ├── index.html.tt2 ├── login.html.tt2 ├── logs.html.tt2 ├── not_found.production.html.ep ├── opds.html.tt2 ├── opds_entry.html.tt2 ├── plugins.html.tt2 ├── reader.html.tt2 ├── stats.html.tt2 ├── templates_config │ ├── config_files.html.tt2 │ ├── config_global.html.tt2 │ ├── config_security.html.tt2 │ ├── config_shinobu.html.tt2 │ ├── config_tags.html.tt2 │ └── config_theme.html.tt2 └── upload.html.tt2 ├── tests ├── LANraragi │ ├── Plugin │ │ └── Metadata │ │ │ ├── Chaika.t │ │ │ ├── EHentai.t │ │ │ ├── Eze.t │ │ │ ├── Fakku.t │ │ │ ├── Generic.t │ │ │ ├── Hitomi.t │ │ │ ├── Koromo.t │ │ │ └── nHentai.t │ └── Utils │ │ ├── Generic.t │ │ └── Tags.t ├── backup.t ├── mocks.pl ├── modules.t ├── opds.t ├── plugins.t ├── samples │ ├── chaika │ │ ├── 001_gid_27240.json │ │ └── 002_sha1_response.json │ ├── eh │ │ ├── 001_gid-1866546.json │ │ └── 002_search_results.html │ ├── eze │ │ ├── eze_full_sample.json │ │ └── eze_lite_sample.json │ ├── fakku │ │ ├── 001_search_response.html │ │ └── 002_gallery_front.html │ ├── hitomi │ │ └── 2261881.js │ ├── koromo │ │ └── koromo_sample.json │ ├── nh │ │ ├── 001_search_results.html │ │ └── 002_gid_52249.html │ └── opds │ │ └── opds_sample.xml └── search.t └── tools ├── Documentation ├── .gitbook.yaml ├── .gitignore ├── .screenshots │ ├── backup.png │ ├── batch.png │ ├── batchlog.png │ ├── brew.jpg │ ├── categories.png │ ├── category_filtered.png │ ├── cloud.PNG │ ├── download.png │ ├── downloaders.png │ ├── dureader.jpg │ ├── edit.PNG │ ├── favtags.jpg │ ├── ichaival.png │ ├── jails.jpg │ ├── karen-dark.jpg │ ├── karen-distro.png │ ├── karen-light.jpg │ ├── karen-startmenu.png │ ├── karen.jpg │ ├── login.png │ ├── mountpoints.jpg │ ├── opds.jpg │ ├── ratings.png │ ├── reader.jpg │ ├── reader_options.png │ ├── search.png │ ├── shell.jpg │ ├── shiggy.png │ ├── tachiyomi.jpg │ ├── themes.png │ ├── thumbchange.png │ ├── uploading.png │ └── webext.png ├── README.md ├── SUMMARY.md ├── advanced-usage │ ├── backup-and-restore.md │ ├── batch-tagging.md │ ├── categories.md │ ├── downloading.md │ ├── external-readers.md │ ├── network-interfaces.md │ ├── proxy-setup.md │ └── tag-rules.md ├── api-documentation │ ├── archive-api.md │ ├── category-api.md │ ├── database-api.md │ ├── getting-started.md │ ├── minion-api.md │ ├── miscellaneous-other-api.md │ ├── search-api.md │ └── shinobu-api.md ├── basic-operations │ ├── archives.md │ ├── first-steps.md │ ├── metadata.md │ ├── searching.md │ ├── stats.md │ └── themes.md ├── extending-lanraragi │ ├── architecture.md │ └── index.md ├── installing-lanraragi │ ├── community.md │ ├── docker.md │ ├── jail.md │ ├── macos.md │ ├── methods.md │ ├── source.md │ └── windows.md └── plugin-docs │ ├── code-examples.md │ ├── download.md │ ├── index.md │ ├── login.md │ ├── metadata.md │ └── scripts.md ├── _screenshots ├── archive_list.png ├── archive_thumb.png ├── cfg.jpg ├── cfg.png ├── cfg_plugin.png ├── reader.jpg └── reader_overlay.jpg ├── build ├── docker │ ├── Dockerfile │ ├── Dockerfile-legacy │ ├── install-everything.sh │ ├── redis.conf │ ├── s6 │ │ ├── cont-init.d │ │ │ └── 01-lrr-setup │ │ ├── fix-attrs.d │ │ │ └── 01-lrr-dirs │ │ └── services.d │ │ │ ├── lanraragi │ │ │ ├── finish │ │ │ └── run │ │ │ └── redis │ │ │ ├── finish │ │ │ └── run │ └── wsl.conf ├── homebrew │ ├── Lanraragi.rb │ ├── lanraragi │ └── redis.conf └── windows │ └── build.ps1 ├── cpanfile ├── install.pl ├── lanraragi-systemd.service └── repository-open-graph-template.jpg /.dockerignore: -------------------------------------------------------------------------------- 1 | tools/_screenshots 2 | tools/Documentation 3 | tools/build/windows 4 | tools/build/homebrew 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jquery": true 6 | }, 7 | "extends": [ 8 | "airbnb-base", 9 | "eslint:recommended" 10 | ], 11 | "parserOptions": { 12 | "ecmaVersion": 12, 13 | "sourceType": "module" 14 | }, 15 | "globals": { 16 | "LRR": "readonly", 17 | "Common": "readonly", 18 | "Server": "readonly", 19 | "tagger": "readonly", 20 | "marked": "readonly", 21 | "Awesomplete": "readonly", 22 | "tippy": "readonly", 23 | "Index": "readonly", 24 | "IndexTable": "readonly", 25 | "Swiper": "readonly" 26 | }, 27 | "ignorePatterns": ["**/vendor/*.js"], 28 | "rules": { 29 | "func-names": ["error", "never"], 30 | "indent": ["error", 4], 31 | "quotes": ["error", "double"], 32 | "no-alert": "off", 33 | "no-else-return": "off", 34 | "no-param-reassign": ["error", { "props": false }], 35 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], 36 | "no-multi-assign": ["error", { "ignoreNonDeclaration": true }], 37 | "no-unused-expressions": [ 38 | "error", 39 | { 40 | "allowShortCircuit": true, 41 | "allowTernary": true 42 | } 43 | ], 44 | "one-var": "off", 45 | "one-var-declaration-per-line": ["error", "initializations"], 46 | "prefer-destructuring": ["error", {"object": true, "array": false}], 47 | "function-paren-newline": "off", 48 | "function-call-argument-newline": "off" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.{cmd,[cC][mM][dD]} text eol=crlf 3 | *.{bat,[bB][aA][tT]} text eol=crlf 4 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | - test-builds 6 | - actions-testing 7 | name: Docker_Image_lanraragi 8 | jobs: 9 | buildDocker: 10 | name: Build and Push Docker Image 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: Set up QEMU 15 | uses: docker/setup-qemu-action@v2 16 | - name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v2 18 | - uses: actions/cache@v3 19 | with: 20 | path: /tmp/buildxcache 21 | key: ${{ runner.os }}-docker-buildx-${{ github.sha }} 22 | restore-keys: | 23 | ${{ runner.os }}-docker-buildx- 24 | - name: Docker Login 25 | env: 26 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 27 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 28 | run: | 29 | echo "${DOCKER_PASSWORD}" | docker login -u ${DOCKER_USERNAME} --password-stdin 30 | - name: Build/Push Nightly Docker 31 | run: | 32 | docker buildx build \ 33 | --platform linux/amd64 \ 34 | --output "type=image,push=true" \ 35 | --tag dezhao/lanraragi_test:0.8.7 \ 36 | --cache-from "type=local,src=/tmp/buildxcache" \ 37 | --cache-to "type=local,dest=/tmp/buildxcache" \ 38 | --file ./tools/build/docker/Dockerfile . -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | content 3 | .minion.db* 4 | npm-debug.log 5 | dump.rdb 6 | package-lock.json 7 | .vstags 8 | .gitbook 9 | *qemu-*-static* 10 | Dockerfile.* 11 | .DS_Store 12 | config.log 13 | autobackup.json 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/build/windows/Karen"] 2 | path = tools/build/windows/Karen 3 | url = https://github.com/Difegue/Karen.git 4 | -------------------------------------------------------------------------------- /.perltidyrc: -------------------------------------------------------------------------------- 1 | -bar 2 | -ce 3 | -vt=2 4 | -nsfs 5 | -nolq 6 | -l=132 7 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Difegue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # DOCKER-VERSION 0.3.4 2 | FROM alpine:3.12 3 | LABEL git="https://github.com/uparrows/LANraragi_cn" 4 | 5 | ENV S6_OVERLAY_RELEASE v2.0.0.1 6 | ENV S6_KEEP_ENV 1 7 | 8 | # warn if we can't run stage2 (fix-attrs/cont-init) 9 | ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 1 10 | 11 | # wait 10s before KILLing 12 | ENV S6_KILL_GRACETIME 10000 13 | 14 | # s6 15 | ENTRYPOINT ["/init"] 16 | 17 | # Check application health 18 | HEALTHCHECK --interval=1m --timeout=10s --retries=3 \ 19 | CMD wget --quiet --tries=1 --no-check-certificate --spider \ 20 | http://localhost:3000 || exit 1 21 | 22 | #Default mojo server port 23 | EXPOSE 3000 24 | 25 | #Enable UTF-8 (might not do anything extra on alpine tho) 26 | ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 \ 27 | #rootless user id 28 | LRR_UID=0 LRR_GID=0 \ 29 | #Environment variables overridable by the user on container deployment 30 | LRR_NETWORK=http://*:3000 \ 31 | # extra variables 32 | EV_EXTRA_DEFS=-DEV_NO_ATFORK 33 | 34 | 35 | 36 | # we use s6-overlay-nobin to just pull in the s6-overlay arch agnostic (shell) 37 | # components, since we apk install the binaries of s6 later which are arch specific 38 | # /!\ While the s6 version here is fixed by an envvar, the apk install is not pinned and takes whatever's in alpine:latest! This certainly needs a fix. 39 | ADD https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_RELEASE}/s6-overlay-nobin.tar.gz /tmp/s6-overlay-nobin.tar.gz 40 | RUN tar -C / -xzf /tmp/s6-overlay-nobin.tar.gz && rm -f /tmp/s6-overlay-nobin.tar.gz 41 | 42 | 43 | WORKDIR /root/lanraragi 44 | 45 | #Copy cpanfile and install script before copying the entire context 46 | #This allows for Docker cache to preserve cpan dependencies 47 | COPY --chown=root:root /tools/cpanfile /tools/install.pl /tools/build/docker/install-everything.sh tools/ 48 | COPY --chown=root:root /package.json package.json 49 | 50 | # Run the install script as root 51 | RUN sh ./tools/install-everything.sh 52 | RUN rm -f /root/lanraragi/public/js/vendor/jquery.dataTables.min.js 53 | #Copy remaining LRR files from context 54 | # consider chowning in s6 setup scripts instead 55 | COPY --chown=root:root /lib lib 56 | COPY --chown=root:root /public public 57 | COPY --chown=root:root /script script 58 | COPY --chown=root:root /templates templates 59 | COPY --chown=root:root /tests tests 60 | COPY --chown=root:root /lrr.conf lrr.conf 61 | COPY --chown=root:root /tools/build/docker/redis.conf tools/build/docker/ 62 | COPY /tools/build/docker/wsl.conf /etc/wsl.conf 63 | COPY /tools/build/docker/s6/cont-init.d/ /etc/cont-init.d/ 64 | COPY /tools/build/docker/s6/services.d/ /etc/services.d/ 65 | COPY --chown=root:root /jquery.dataTables.min.js /root/lanraragi/public/js/vendor/jquery.dataTables.min.js 66 | #COPY /tools/build/docker/s6/fix-attrs.d/ /etc/fix-attrs.d/ 67 | 68 | # Persistent volumes 69 | VOLUME [ "/root/lanraragi/content" ] 70 | VOLUME [ "/root/lanraragi/database"] 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LANraragi_cn 2 | 3 | This repo is a fork of [Difegue / LANraragi](https://github.com/Difegue/LANraragi) , those things i've done was to translate this repo into chinese ,and fix chrome browser js problem .also use user root instead of user koyomi to fix the access permissions of the content folder on synology nas. 4 | 5 | also i've build a docker image , if you are a user of docker ,you need to mount your comic folder to "/root/lanraragi/content" directory, and the database folder to "/root/lanraragi/database" directory. 6 | 7 | Thanks to Difegue for developing this software ! 8 | 9 | 这是LANraragi的汉化版本,相较与原版汉化了界面,修复了chrome的js报错,并且使用root账户代替koyomi解决群晖nas上面的无法访问挂载文件夹/home/koyomi/lanraragi/content目录的问题,我构建了一个docker镜像,如果你是docker用户,你需要将漫画文件夹挂载到/root/lanraragi/content,数据库挂载到/root/lanraragi/database。 10 | 11 | ## 简介 12 | 13 | 14 | Lanraragi是一个开源的压缩包漫画阅读器,运行在Mojolicious和Redis基础上。 15 | 16 | [](https://hub.docker.com/r/dezhao/lanraragi_cn/) 17 | [![IC](https://github.com/uparrows/LANraragi_cn/actions/workflows/docker-image.yml/badge.svg?branch=main)](https://github.com/uparrows/LANraragi_cn/actions/workflows/docker-image.yml) 18 | 19 | [⏬ 下载源码](https://github.com/uparrows/LANraragi_cn/releases/latest) |[📄 教程](http://yuanfangblog.xyz/technology/251.html) | 20 | 21 | 安卓客户端(已提交中文支持):https://f-droid.org/packages/com.utazukin.ichaival/ 22 | 23 | IOS客户端(用AltStore安装): https://github.com/Doraemoe/DuReader/releases 24 | AltStore:https://altstore.io/ 25 | 26 | Windows客户端(已提交中文支持): https://www.microsoft.com/zh-cn/p/lrreader/9mz6bwwvswjh 27 | 28 | ## 扫码直接查看教程 29 | 30 | [](http://yuanfangblog.xyz/technology/251.html) 31 | 32 | 33 | 34 | ## 截图 35 | 36 | |主页, 预览图 | 主页, 列表模式 | 37 | |---|---| 38 | | [![archive_thumb](https://user-images.githubusercontent.com/38988286/111873262-6f619f80-89ca-11eb-8891-7437f1d08cb4.png)](https://user-images.githubusercontent.com/38988286/111873262-6f619f80-89ca-11eb-8891-7437f1d08cb4.png) | [![archive_list](https://user-images.githubusercontent.com/38988286/111873240-5822b200-89ca-11eb-8c0c-17b3bd374a9b.png)](https://user-images.githubusercontent.com/38988286/111873240-5822b200-89ca-11eb-8c0c-17b3bd374a9b.png) | 39 | 40 | |阅读器 | 预览 | 41 | |---|---| 42 | | [![reader](https://user-images.githubusercontent.com/38988286/111873285-899b7d80-89ca-11eb-8868-5431e7a117f9.jpg)](https://user-images.githubusercontent.com/38988286/111873285-899b7d80-89ca-11eb-8868-5431e7a117f9.jpg) | [![reader_overlay](https://user-images.githubusercontent.com/38988286/111873298-915b2200-89ca-11eb-8d61-cc67dca038f0.jpg)](https://user-images.githubusercontent.com/38988286/111873298-915b2200-89ca-11eb-8d61-cc67dca038f0.jpg) | 43 | 44 | 45 | |配置 | 插件配置 | 46 | |---|---| 47 | | [![cfg](https://user-images.githubusercontent.com/38988286/111873270-78527100-89ca-11eb-9526-35f1f78b578f.png)](https://user-images.githubusercontent.com/38988286/111873270-78527100-89ca-11eb-9526-35f1f78b578f.png) | [![cfg_plugin](https://user-images.githubusercontent.com/38988286/111873273-7f797f00-89ca-11eb-89b4-b3c21228c949.png)](https://user-images.githubusercontent.com/38988286/111873273-7f797f00-89ca-11eb-89b4-b3c21228c949.png) | 48 | 49 | 50 | ## 功能 51 | 52 | *以存档格式存储您的漫画。 (支持zip / rar / targz / lzma / 7z / xz / cbz / cbr / pdf,epub准支持) 53 | 54 | *直接从Web浏览器读取档案:服务器使用临时文件夹从压缩文件中读取。 55 | 56 | *使用内置的OPDS目录在专用的阅读器软件中阅读档案 57 | 58 | *使用客户端API与其他程序中的LANraragi进行交互 59 | 60 | *两种不同的用户界面:紧凑的存档列表,带有悬停缩略图或缩略图视图。 61 | 62 | *从5种预装的响应库样式中进行选择,或使用CSS添加您自己的样式。 63 | 64 | *命名空间的完整标签支持:添加您自己的名称或使用插件从其他来源导入它们。 65 | 66 | *设置收藏夹标签,以便能够快速在您的收藏夹中找到包含它们的档案 67 | 68 | *自动标记:将存档添加到LANraragi后,将使用插件自动导入元数据。 69 | 70 | *将数据库备份为JSON,以将标签传递到另一个LANraragi实例。 71 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Api/Database.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Api::Database; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use Redis; 5 | use Mojo::JSON qw(decode_json); 6 | 7 | use LANraragi::Model::Backup; 8 | use LANraragi::Model::Stats; 9 | use LANraragi::Utils::Generic qw(render_api_response); 10 | use LANraragi::Utils::Database qw(invalidate_cache); 11 | 12 | sub serve_backup { 13 | my $self = shift; 14 | $self->render( json => decode_json(LANraragi::Model::Backup::build_backup_JSON) ); 15 | } 16 | 17 | sub drop_database { 18 | LANraragi::Utils::Database::drop_database(); 19 | render_api_response( shift, "drop_database" ); 20 | } 21 | 22 | sub serve_tag_stats { 23 | my $self = shift; 24 | my $minscore = $self->req->param('minweight') || "1"; 25 | 26 | $self->render( json => LANraragi::Model::Stats::build_tag_stats($minscore) ); 27 | } 28 | 29 | sub clean_database { 30 | my ( $deleted, $unlinked ) = LANraragi::Utils::Database::clean_database; 31 | 32 | #Force a refresh 33 | invalidate_cache(1); 34 | 35 | shift->render( 36 | json => { 37 | operation => "clean_database", 38 | deleted => $deleted, 39 | unlinked => $unlinked, 40 | success => 1 41 | } 42 | ); 43 | } 44 | 45 | #Clear new flag in all archives. 46 | sub clear_new_all { 47 | 48 | my $self = shift; 49 | my $redis = $self->LRR_CONF->get_redis(); 50 | 51 | # Get all archives thru redis 52 | # 40-character long keys only => Archive IDs 53 | my @keys = $redis->keys('????????????????????????????????????????'); 54 | 55 | foreach my $idall (@keys) { 56 | $redis->hset( $idall, "isnew", "false" ); 57 | } 58 | 59 | # Bust search cache completely, this is a big change 60 | invalidate_cache(1); 61 | $redis->quit(); 62 | render_api_response( $self, "clear_new_all" ); 63 | } 64 | 65 | 1; 66 | 67 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Api/Minion.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Api::Minion; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use Mojo::JSON qw(encode_json decode_json); 5 | use Redis; 6 | 7 | use LANraragi::Model::Stats; 8 | use LANraragi::Utils::TempFolder qw(get_tempsize clean_temp_full); 9 | use LANraragi::Utils::Generic qw(render_api_response); 10 | use LANraragi::Utils::Plugins qw(get_plugin get_plugins get_plugin_parameters use_plugin); 11 | 12 | # Returns basic info for the given Minion job id. 13 | sub minion_job_status { 14 | my $self = shift; 15 | my $id = $self->stash('jobid'); 16 | my $job = $self->minion->job($id); 17 | 18 | if ($job) { 19 | 20 | my %info = %{ $job->info }; 21 | 22 | # Render a basic json containing only task, state and error 23 | $self->render( 24 | json => { 25 | task => $info{task}, 26 | state => $info{state}, 27 | error => $info{error} 28 | } 29 | ); 30 | 31 | } else { 32 | render_api_response( $self, "minion_job_status", "没有使用此 ID 的工作." ); 33 | } 34 | } 35 | 36 | # Returns the full info for the given Minion job id. 37 | sub minion_job_detail { 38 | my $self = shift; 39 | my $id = $self->stash('jobid'); 40 | my $job = $self->minion->job($id); 41 | 42 | if ($job) { 43 | $self->render( json => $job->info ); 44 | } else { 45 | render_api_response( $self, "minion_job_detail", "没有使用此 ID 的工作." ); 46 | } 47 | } 48 | 49 | # Queues a job into Minion. 50 | sub queue_minion_job { 51 | 52 | my ($self) = shift; 53 | my $jobname = $self->stash('jobname'); 54 | my @jobargs = decode_json( $self->req->param('args') ); 55 | my $priority = $self->req->param('priority') || 0; 56 | 57 | my $jobid = $self->minion->enqueue( $jobname => @jobargs => { priority => $priority } ); 58 | 59 | $self->render( 60 | json => { 61 | operation => "queue_minion_job", 62 | success => 1, 63 | job => $jobid 64 | } 65 | ); 66 | } 67 | 68 | 1; 69 | 70 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Api/Shinobu.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Api::Shinobu; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | use Storable; 4 | 5 | use LANraragi::Utils::Generic qw(start_shinobu render_api_response); 6 | use LANraragi::Utils::TempFolder qw(get_temp); 7 | 8 | sub shinobu_status { 9 | my $self = shift; 10 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) }; 11 | 12 | $self->render( 13 | json => { 14 | operation => "shinobu_status", 15 | success => 1, 16 | is_alive => $shinobu->poll(), 17 | pid => $shinobu->pid 18 | } 19 | ); 20 | } 21 | 22 | sub reset_filemap { 23 | my $self = shift; 24 | 25 | # This is a shinobu endpoint even though we're deleting stuff in redis 26 | # since we'll have to restart shinobu anyway to proc filemap re-creation. 27 | 28 | my $redis = $self->LRR_CONF->get_redis; 29 | $redis->del("LRR_FILEMAP"); 30 | 31 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) }; 32 | 33 | #commit sudoku 34 | $shinobu->kill(); 35 | 36 | # Create a new Process, automatically stored in TEMP_FOLDER/shinobu.pid 37 | my $proc = start_shinobu($self); 38 | 39 | $self->render( 40 | json => { 41 | operation => "shinobu_rescan", 42 | success => $proc->poll(), 43 | new_pid => $proc->pid 44 | } 45 | ); 46 | } 47 | 48 | sub stop_shinobu { 49 | my $self = shift; 50 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) }; 51 | 52 | #commit sudoku 53 | $shinobu->kill(); 54 | render_api_response( $self, "shinobu_stop" ); 55 | } 56 | 57 | sub restart_shinobu { 58 | my $self = shift; 59 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) }; 60 | 61 | #commit sudoku 62 | $shinobu->kill(); 63 | 64 | # Create a new Process, automatically stored in TEMP_FOLDER/shinobu.pid 65 | my $proc = start_shinobu($self); 66 | 67 | $self->render( 68 | json => { 69 | operation => "shinobu_restart", 70 | success => $proc->poll(), 71 | new_pid => $proc->pid 72 | } 73 | ); 74 | } 75 | 76 | 1; 77 | 78 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Backup.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Backup; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use LANraragi::Utils::Generic qw(generate_themes_header); 5 | use LANraragi::Model::Backup; 6 | 7 | # This action will render a template 8 | sub index { 9 | my $self = shift; 10 | 11 | #GET with a parameter => do backup 12 | if ( $self->req->param('dobackup') ) { 13 | my $json = LANraragi::Model::Backup::build_backup_JSON(); 14 | 15 | #Write json to file in the user directory and serve that file through render_static 16 | my $file = $self->LRR_CONF->get_userdir . '/backup.json'; 17 | 18 | if ( -e $file ) { unlink $file } 19 | 20 | my $OUTFILE; 21 | 22 | open $OUTFILE, '>>', $file; 23 | print {$OUTFILE} $json; 24 | close $OUTFILE; 25 | 26 | $self->render_file( filepath => $file ); 27 | 28 | } else { #Get with no parameters => Regular HTML printout 29 | $self->render( 30 | template => "backup", 31 | title => $self->LRR_CONF->get_htmltitle, 32 | descstr => $self->LRR_DESC, 33 | csshead => generate_themes_header($self), 34 | version => $self->LRR_VERSION 35 | ); 36 | } 37 | } 38 | 39 | sub restore { 40 | my $self = shift; 41 | my $file = $self->req->upload('file'); 42 | 43 | if ( $file->headers->content_type eq "application/json" ) { 44 | 45 | my $json = $file->slurp; 46 | LANraragi::Model::Backup::restore_from_JSON($json); 47 | 48 | $self->render( 49 | json => { 50 | operation => "restore_backup", 51 | success => 1 52 | } 53 | ); 54 | } else { 55 | $self->render( 56 | json => { 57 | operation => "restore_backup", 58 | success => 0, 59 | error => "Not a JSON file." 60 | } 61 | ); 62 | } 63 | } 64 | 65 | 1; 66 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Category.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Category; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use utf8; 5 | use URI::Escape; 6 | use Redis; 7 | use Encode; 8 | use Mojo::Util qw(xml_escape); 9 | 10 | use LANraragi::Utils::Generic qw(generate_themes_header); 11 | use LANraragi::Utils::Database qw(redis_decode); 12 | 13 | # Go through the archives in the content directory and build the template at the end. 14 | sub index { 15 | 16 | my $self = shift; 17 | my $redis = $self->LRR_CONF->get_redis(); 18 | my $force = 0; 19 | 20 | my $userlogged = $self->LRR_CONF->enable_pass == 0 || $self->session('is_logged'); 21 | 22 | $redis->quit(); 23 | 24 | #Then complete it with the rest from the database. 25 | #40-character long keys only => Archive IDs 26 | my @keys = $redis->keys('????????????????????????????????????????'); 27 | 28 | #Parse the archive list and build
  • elements accordingly. 29 | my $arclist = ""; 30 | 31 | #Only show IDs that still have their files present. 32 | foreach my $id (@keys) { 33 | my $zipfile = $redis->hget( $id, "file" ); 34 | my $title = $redis->hget( $id, "title" ); 35 | $title = redis_decode($title); 36 | $title = xml_escape($title); 37 | 38 | if ( -e $zipfile ) { 39 | $arclist .= 40 | "
  • "; 41 | $arclist .= "
  • "; 42 | } 43 | } 44 | 45 | $redis->quit(); 46 | 47 | $self->render( 48 | template => "category", 49 | arclist => $arclist, 50 | title => $self->LRR_CONF->get_htmltitle, 51 | descstr => $self->LRR_DESC, 52 | csshead => generate_themes_header($self), 53 | version => $self->LRR_VERSION 54 | ); 55 | } 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Edit.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Edit; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use File::Basename; 5 | use Redis; 6 | use Encode; 7 | use Template; 8 | use Mojo::Util qw(xml_escape); 9 | 10 | use LANraragi::Utils::Generic qw(generate_themes_header); 11 | use LANraragi::Utils::Database qw(redis_decode); 12 | use LANraragi::Utils::Plugins qw(get_plugins); 13 | 14 | sub index { 15 | my $self = shift; 16 | 17 | #Does the passed file exist in the database? 18 | my $id = $self->req->param('id'); 19 | 20 | my $redis = $self->LRR_CONF->get_redis(); 21 | 22 | if ( $redis->exists($id) ) { 23 | my %hash = $redis->hgetall($id); 24 | 25 | my ( $name, $title, $tags, $file, $thumbhash ) = @hash{qw(name title tags file thumbhash)}; 26 | 27 | ( $_ = redis_decode($_) ) for ( $name, $title, $tags ); 28 | 29 | #Build plugin listing 30 | my @pluginlist = get_plugins("metadata"); 31 | 32 | $redis->quit(); 33 | 34 | $self->render( 35 | template => "edit", 36 | id => $id, 37 | name => $name, 38 | arctitle => xml_escape($title), 39 | tags => xml_escape($tags), 40 | file => decode_utf8($file), 41 | thumbhash => $thumbhash, 42 | plugins => \@pluginlist, 43 | title => $self->LRR_CONF->get_htmltitle, 44 | descstr => $self->LRR_DESC, 45 | csshead => generate_themes_header($self), 46 | version => $self->LRR_VERSION 47 | ); 48 | } else { 49 | $self->redirect_to('index'); 50 | } 51 | } 52 | 53 | 1; 54 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Index.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Index; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use utf8; 5 | use URI::Escape; 6 | use Redis; 7 | use Encode; 8 | use File::Basename; 9 | use Authen::Passphrase; 10 | 11 | use LANraragi::Utils::Generic qw(generate_themes_header); 12 | 13 | # This endpoint is technically superseded by /api/search/random, but it's still useful in the Reader. 14 | sub random_archive { 15 | my $self = shift; 16 | my $archive = ""; 17 | my $archiveexists = 0; 18 | 19 | my $redis = $self->LRR_CONF->get_redis(); 20 | 21 | # We get a random archive ID. 22 | # We check for the length to (sort-of) avoid not getting an archive ID. 23 | # TODO: This will loop infinitely if there are zero archives in store. 24 | until ($archiveexists) { 25 | $archive = $redis->randomkey(); 26 | 27 | $self->LRR_LOGGER->debug("Found key $archive"); 28 | 29 | #We got a key, but does the matching archive still exist on the server? 30 | if ( length($archive) == 40 31 | && $redis->type($archive) eq "hash" 32 | && $redis->hexists( $archive, "file" ) ) { 33 | my $arclocation = $redis->hget( $archive, "file" ); 34 | if ( -e $arclocation ) { $archiveexists = 1; } 35 | } 36 | } 37 | 38 | $redis->quit(); 39 | 40 | #We redirect to the reader, with the key as parameter. 41 | $self->redirect_to( '/reader?id=' . $archive ); 42 | } 43 | 44 | # Render the index template with a few prefilled arguments. 45 | # Most of the work is done in JS these days. 46 | sub index { 47 | 48 | my $self = shift; 49 | 50 | #Checking if the user still has the default password enabled 51 | my $ppr = Authen::Passphrase->from_rfc2307( $self->LRR_CONF->get_password ); 52 | my $passcheck = ( $ppr->match("kamimamita") && $self->LRR_CONF->enable_pass ); 53 | 54 | my $userlogged = $self->LRR_CONF->enable_pass == 0 || $self->session('is_logged'); 55 | 56 | # Get static category list to populate the right-click menu 57 | my @categories = LANraragi::Model::Category->get_static_category_list; 58 | 59 | $self->render( 60 | template => "index", 61 | version => $self->LRR_VERSION, 62 | title => $self->LRR_CONF->get_htmltitle, 63 | descstr => $self->LRR_DESC, 64 | userlogged => $userlogged, 65 | categories => \@categories, 66 | motd => $self->LRR_CONF->get_motd, 67 | csshead => generate_themes_header($self), 68 | usingdefpass => $passcheck 69 | ); 70 | } 71 | 72 | 1; 73 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Logging.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Logging; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use Redis; 5 | use Encode; 6 | 7 | use LANraragi::Utils::Generic qw(generate_themes_header); 8 | use LANraragi::Utils::Logging qw(get_logdir get_lines_from_file); 9 | 10 | # This action will render a template 11 | sub index { 12 | 13 | my $self = shift; 14 | 15 | $self->render( 16 | template => "logs", 17 | title => $self->LRR_CONF->get_htmltitle, 18 | descstr => $self->LRR_DESC, 19 | csshead => generate_themes_header($self), 20 | version => $self->LRR_VERSION 21 | ); 22 | } 23 | 24 | sub print_lines_from_file { 25 | 26 | my ( $mojo, $file ) = @_; 27 | 28 | # Number of lines to read 29 | my $lines = 100; 30 | my $logdir = get_logdir(); 31 | 32 | if ( $mojo->req->param('lines') ) { 33 | $lines = $mojo->req->param('lines'); 34 | } 35 | 36 | $mojo->render( text => get_lines_from_file( $lines, $logdir . "/$file.log" ) ); 37 | 38 | } 39 | 40 | sub print_general { 41 | print_lines_from_file( shift, "lanraragi" ); 42 | } 43 | 44 | sub print_shinobu { 45 | print_lines_from_file( shift, "shinobu" ); 46 | } 47 | 48 | sub print_plugins { 49 | print_lines_from_file( shift, "plugins" ); 50 | } 51 | 52 | sub print_redis { 53 | print_lines_from_file( shift, "redis" ); 54 | } 55 | 56 | sub print_mojo { 57 | 58 | my $self = shift; 59 | 60 | #Depending on the mode, look for development or production.log 61 | my $mode = $self->app->mode; 62 | print_lines_from_file( $self, $mode ); 63 | } 64 | 65 | 1; 66 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Login.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Login; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | use MIME::Base64; 4 | 5 | use Redis; 6 | use Authen::Passphrase; 7 | 8 | use LANraragi::Utils::Generic qw(generate_themes_header); 9 | 10 | sub check { 11 | my $self = shift; 12 | 13 | my $pw = $self->req->param('password') || ''; 14 | 15 | #match password we got with the authen hash stored in redis 16 | my $ppr = Authen::Passphrase->from_rfc2307( $self->LRR_CONF->get_password ); 17 | 18 | if ( $ppr->match($pw) ) { 19 | 20 | $self->LRR_LOGGER->info( "Successful login attempt from " . $self->tx->remote_address ); 21 | 22 | $self->session( is_logged => 1 ); 23 | $self->session( expiration => 60 * 60 * 24 ); 24 | $self->redirect_to('index'); 25 | } else { 26 | 27 | $self->LRR_LOGGER->warn( "Failed login attempt with password '$pw' from " . $self->tx->remote_address ); 28 | 29 | $self->render( 30 | template => "login", 31 | title => $self->LRR_CONF->get_htmltitle, 32 | descstr => $self->LRR_DESC, 33 | csshead => generate_themes_header($self), 34 | version => $self->LRR_VERSION, 35 | wrongpass => 1 36 | ); 37 | } 38 | } 39 | 40 | #The request can be authentified in two ways: 41 | #Logged in normally with a session cookie 42 | #Password protection disabled 43 | sub logged_in { 44 | my $self = shift; 45 | return 1 46 | if $self->session('is_logged') 47 | || $self->LRR_CONF->enable_pass == 0; 48 | $self->redirect_to('login'); 49 | return 0; 50 | } 51 | 52 | # For APIs, the request can also be authentified with a valid API Key. 53 | sub logged_in_api { 54 | my $self = shift; 55 | 56 | # The API key is in the Authentication header. 57 | my $expected_key = $self->LRR_CONF->get_apikey; 58 | 59 | my $auth_header = $self->req->headers->authorization || ""; 60 | my $expected_header = "Bearer " . encode_base64( $expected_key, "" ); 61 | 62 | return 1 63 | if ( $expected_key ne "" && $auth_header eq $expected_header ) 64 | || $self->session('is_logged') 65 | || $self->LRR_CONF->enable_pass == 0; 66 | $self->render( 67 | json => { error => "This API is protected and requires login or an API Key." }, 68 | status => 401 69 | ); 70 | return 0; 71 | } 72 | 73 | sub setup_cors { 74 | my $self = shift; 75 | 76 | # Set Allow-Origin to wildcard 77 | $self->res->headers->header( 'Access-Control-Allow-Origin' => '*' ); 78 | 79 | # Explicitly say requests with an Authorization header (private API requests) are allowed 80 | $self->res->headers->header( 'Access-Control-Allow-Headers' => 'Authorization' ); 81 | 82 | return 1; 83 | } 84 | 85 | sub logout { 86 | my $self = shift; 87 | $self->session( expires => 1 ); 88 | $self->redirect_to('index'); 89 | } 90 | 91 | sub index { 92 | my $self = shift; 93 | $self->redirect_to('index') if $self->session('is_logged'); 94 | 95 | $self->render( 96 | template => "login", 97 | title => $self->LRR_CONF->get_htmltitle, 98 | descstr => $self->LRR_DESC, 99 | csshead => generate_themes_header($self), 100 | version => $self->LRR_VERSION 101 | ); 102 | } 103 | 104 | 1; 105 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Reader.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Reader; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | use Mojo::URL; 4 | 5 | use Encode; 6 | 7 | use LANraragi::Utils::Generic qw(generate_themes_header); 8 | 9 | use LANraragi::Model::Reader; 10 | 11 | # This action will render a template 12 | sub index { 13 | my $self = shift; 14 | 15 | if ( $self->req->param('id') ) { 16 | 17 | # Allow adding to static categories 18 | my @categories = LANraragi::Model::Category->get_static_category_list; 19 | 20 | # Get query string from referrer URL, if there's one 21 | my $referrer = $self->req->headers->referrer; 22 | my $query = ""; 23 | 24 | if ($referrer) { 25 | $query = Mojo::URL->new($referrer)->query->to_string; 26 | } 27 | 28 | $self->render( 29 | template => "reader", 30 | title => $self->LRR_CONF->get_htmltitle, 31 | use_local => $self->LRR_CONF->enable_localprogress, 32 | id => $self->req->param('id'), 33 | categories => \@categories, 34 | csshead => generate_themes_header($self), 35 | version => $self->LRR_VERSION, 36 | ref_query => $query, 37 | userlogged => $self->LRR_CONF->enable_pass == 0 || $self->session('is_logged') 38 | ); 39 | } else { 40 | 41 | # No parameters back the fuck off 42 | $self->redirect_to('index'); 43 | } 44 | } 45 | 46 | 1; 47 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Stats.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Stats; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use LANraragi::Model::Stats; 5 | use LANraragi::Utils::Generic qw(generate_themes_header); 6 | 7 | # This action will render a template 8 | sub index { 9 | my $self = shift; 10 | 11 | $self->render( 12 | template => "stats", 13 | title => $self->LRR_CONF->get_htmltitle, 14 | descstr => $self->LRR_DESC, 15 | csshead => generate_themes_header($self), 16 | archivecount => LANraragi::Model::Stats::get_archive_count, 17 | arcsize => LANraragi::Model::Stats::compute_content_size, 18 | pagestat => LANraragi::Model::Stats::get_page_stat, 19 | version => $self->LRR_VERSION 20 | ); 21 | } 22 | 23 | 1; 24 | -------------------------------------------------------------------------------- /lib/LANraragi/Controller/Upload.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Controller::Upload; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | use Redis; 5 | use File::Temp qw(tempdir); 6 | use File::Copy; 7 | use File::Find; 8 | use File::Basename; 9 | 10 | use LANraragi::Utils::Generic qw(generate_themes_header is_archive get_bytelength); 11 | 12 | sub process_upload { 13 | my $self = shift; 14 | 15 | #Receive uploaded file. 16 | my $file = $self->req->upload('file'); 17 | my $catid = $self->req->param('catid'); 18 | my $filename = $file->filename; 19 | 20 | my $uploadMime = $file->headers->content_type; 21 | 22 | #Check if the uploaded file's extension matches one we accept 23 | if ( is_archive($filename) ) { 24 | 25 | # Move file to a temp folder (not the default LRR one) 26 | my $tempdir = tempdir(); 27 | 28 | my ( $fn, $path, $ext ) = fileparse( $filename, qr/\.[^.]*/ ); 29 | my $byte_limit = LANraragi::Model::Config->enable_cryptofs ? 143 : 255; 30 | 31 | # don't allow the main filename to exceed 143/255 bytes after accounting 32 | # for extension and .upload prefix used by `handle_incoming_file` 33 | $filename = $fn; 34 | while ( get_bytelength( $filename . $ext . ".upload" ) > $byte_limit ) { 35 | $filename = substr( $filename, 0, -1 ); 36 | } 37 | $filename = $filename . $ext; 38 | 39 | my $tempfile = $tempdir . '/' . $filename; 40 | $file->move_to($tempfile) or die "无法移动上传的文件."; 41 | 42 | # Update $tempfile to the exact reference created by the host filesystem 43 | # This is done by finding the first (and only) file in $tempdir. 44 | find( 45 | sub { 46 | return if -d $_; 47 | $tempfile = $File::Find::name; 48 | $filename = $_; 49 | }, 50 | $tempdir 51 | ); 52 | 53 | # Send a job to Minion to handle the uploaded file. 54 | my $jobid = $self->minion->enqueue( handle_upload => [ $tempfile, $catid ] => { priority => 2 } ); 55 | 56 | # Reply with a reference to the job so the client can check on its progress. 57 | $self->render( 58 | json => { 59 | operation => "upload", 60 | name => $file->filename, 61 | debug_name => $filename, 62 | type => $uploadMime, 63 | success => 1, 64 | job => $jobid 65 | } 66 | ); 67 | 68 | } else { 69 | 70 | $self->render( 71 | json => { 72 | operation => "upload", 73 | name => $file->filename, 74 | type => $uploadMime, 75 | success => 0, 76 | error => "不支持的文件类型. (" . $uploadMime . ")" 77 | } 78 | ); 79 | } 80 | } 81 | 82 | sub index { 83 | 84 | my $self = shift; 85 | 86 | # Allow adding to category on direct uploads 87 | my @categories = LANraragi::Model::Category->get_static_category_list; 88 | 89 | $self->render( 90 | template => "upload", 91 | title => $self->LRR_CONF->get_htmltitle, 92 | descstr => $self->LRR_DESC, 93 | categories => \@categories, 94 | csshead => generate_themes_header($self), 95 | version => $self->LRR_VERSION 96 | ); 97 | } 98 | 99 | 1; 100 | -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Download/Chaika.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Download::Chaika; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'uninitialized'; 6 | use utf8; 7 | # Meta-information about your plugin. 8 | sub plugin_info { 9 | 10 | return ( 11 | # Standard metadata 12 | name => "Chaika.moe Downloader", 13 | type => "download", 14 | namespace => "chaikadl", 15 | author => "Difegue", 16 | version => "1.0", 17 | description => "下载给定的 chaika.moe URL 并将其添加到 LANraragi。 暂时不支持图库链接!", 18 | 19 | # Downloader-specific metadata 20 | # https://panda.chaika.moe/archive/_____/ 21 | url_regex => "https?:\/\/panda.chaika.moe\/archive\/.*" 22 | ); 23 | 24 | } 25 | 26 | # Mandatory function to be implemented by your downloader 27 | sub provide_url { 28 | shift; 29 | my $lrr_info = shift; 30 | 31 | # Get the URL to download 32 | my $url = $lrr_info->{url}; 33 | 34 | # Wow! 35 | return ( download_url => $url . "/download" ); 36 | } 37 | 38 | 1; 39 | -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Login/Fakku.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Login::Fakku; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'uninitialized'; 6 | use utf8; 7 | use Mojo::UserAgent; 8 | use LANraragi::Utils::Logging qw(get_logger); 9 | 10 | sub plugin_info { 11 | 12 | return ( 13 | name => "Fakku", 14 | type => "login", 15 | namespace => "fakkulogin", 16 | author => "Nodja", 17 | version => "0.1", 18 | description => 19 | "处理fakku登录。cookie的有效期只有 7 天,所以不要忘记更新它.", 20 | parameters => [ 21 | { type => "string", desc => "fakku_sid cookie value" } 22 | ] 23 | ); 24 | 25 | } 26 | 27 | sub do_login { 28 | 29 | shift; 30 | my ( $fakku_sid ) = @_; 31 | 32 | my $logger = get_logger( "Fakku Login", "plugins" ); 33 | my $ua = Mojo::UserAgent->new; 34 | 35 | if ( $fakku_sid ne "" ) { 36 | $logger->info("Cookie provided ($fakku_sid)!"); 37 | $ua->cookie_jar->add( 38 | Mojo::Cookie::Response->new( 39 | name => 'fakku_sid', 40 | value => $fakku_sid, 41 | domain => 'fakku.net', 42 | path => '/' 43 | ) 44 | ); 45 | } else { 46 | $logger->info("No cookies provided, returning blank UserAgent."); 47 | } 48 | 49 | return $ua; 50 | } 51 | 1; -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Login/nHentai.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Login::nHentai; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'uninitialized'; 6 | use utf8; 7 | use Mojo::UserAgent; 8 | use LANraragi::Utils::Logging qw(get_logger); 9 | 10 | #Meta-information about your plugin. 11 | sub plugin_info { 12 | 13 | return ( 14 | #Standard metadata 15 | name => "nHentai CF Bypass", 16 | type => "login", 17 | namespace => "nhentaicfbypass", 18 | author => "Pheromir", 19 | version => "0.1", 20 | description => 21 | "通过重新使用浏览器中的 cookie 绕过Cloudflare的js。 CF cookie 和用户代理必须来自同一个网络浏览器.", 22 | parameters => [ 23 | { type => "string", desc => "浏览器 UserAgent 字符串(可以在 http://useragentstring.com/ 找到您的浏览器)" }, 24 | { type => "string", desc => "域名nhentai.net的csrftoken cookie" }, 25 | { type => "string", desc => "域名nhentai.net的cf_clearance cookie" } 26 | ] 27 | ); 28 | 29 | } 30 | 31 | 32 | # Mandatory function to be implemented by your login plugin 33 | # Returns a Mojo::UserAgent object only! 34 | sub do_login { 35 | 36 | # Login plugins only receive the parameters entered by the user. 37 | shift; 38 | my ( $useragent, $csrftoken, $cf_clearance ) = @_; 39 | return get_user_agent( $useragent, $csrftoken, $cf_clearance ); 40 | } 41 | 42 | # get_user_agent(useragent, cf cookies) 43 | # Try crafting a Mojo::UserAgent object that can access nHentai. 44 | # Returns the UA object created. 45 | sub get_user_agent { 46 | 47 | my ( $useragent, $csrftoken, $cf_clearance ) = @_; 48 | 49 | my $logger = get_logger( "nHentai Cloudflare Bypass", "plugins" ); 50 | my $ua = Mojo::UserAgent->new; 51 | 52 | if ( $useragent ne "" && $csrftoken ne "" && $cf_clearance ne "") { 53 | $logger->info("Useragent and Cookies provided ($useragent $csrftoken $cf_clearance)!"); 54 | $ua->transactor->name($useragent); 55 | 56 | #Setup the needed cookies 57 | $ua->cookie_jar->add( 58 | Mojo::Cookie::Response->new( 59 | name => 'csrftoken', 60 | value => $csrftoken, 61 | domain => 'nhentai.net', 62 | path => '/' 63 | ) 64 | ); 65 | 66 | $ua->cookie_jar->add( 67 | Mojo::Cookie::Response->new( 68 | name => 'cf_clearance', 69 | value => $cf_clearance, 70 | domain => 'nhentai.net', 71 | path => '/' 72 | ) 73 | ); 74 | 75 | } else { 76 | $logger->info("No cookies provided, returning blank UserAgent."); 77 | } 78 | 79 | return $ua; 80 | 81 | } 82 | 83 | 1; -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Metadata/CopyTags.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Metadata::CopyTags; 2 | 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use LANraragi::Model::Plugins; 7 | use LANraragi::Utils::Logging qw(get_logger); 8 | 9 | #Meta-information about your plugin. 10 | sub plugin_info { 11 | 12 | return ( 13 | #Standard metadata 14 | name => "Tag Copier", 15 | type => "metadata", 16 | namespace => "copytags", 17 | author => "Difegue", 18 | version => "2.1", 19 | description => "应用自定义标签修改.", 20 | icon => 21 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA\nB3RJTUUH4wYCFQ05iQtpeQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH\nAAAD8ElEQVQ4y4WUW2yURRTHfzPfty2UWrZbqdrdhktowQAJiQ/iDUwkaFJ5IAQfGjWBByMhwYAm\nBH3xqT5ArARS9cUgJC1qjCDxCY1JEy6lLVJrW9lKq6Wlgcqy2W7b/S4zx4fdbstFneRMZjLJb/7n\nf86MOn78+MlEIvE6gIhgRRARECGTmaS0tJT6+vrvo7HKxserq6f4v9Hc3PyxiBh5yBgYGJBUKiW/\n9vZKV/eVb7s6OyN79uxhbGwMKVx8f2gRUUEQEAQhfhDg+wGeHxAaA8DZs2e50tVNZ+flJ3v7B8ra\n29uJx+M0NTXhed6DCg8dPtzs+77xfV883xfP8yVXCM/zxA9CCcNQzv3402+bX96yuK2tjd27dxOP\nx2loaCCdTj+gkIJlICAFLz0vx/RMjmx2kunpaXK5nF5au3RJb29fzdq162ouX+p4ZHh4mO3bt98j\n0DU2n5oUZqUUvudz8cIFEvEEoQkRIBarXLFr584frLVmdHRUj4yNXu3r69tbVVV1u7W1lcbGxjxQ\njMzjKxAw1rJoURlr1q7B9wMEQaFKBerDwMfzPKKLo6t+uXp12Z07d16pq6tL79ixg0gkgjamkCv5\nvItKUfkrFCilmN0orZmayrJgQSlDQ0NPHz3WskhrTXd3NwBaxBR8m007v7aza+a8RQTXcaipTTAx\nMYHn+2SnJ4nFYgwODuaBRmxB3FylEEGsLRKliBZcHSFVkSFVOUWiOk5uxiObzZJKpfJAa8wciPvA\nzIflzxSKW+EtDgx8wKUbXVSYcuM4TtEWba1VSkGkJILruvmIuFibh2gNjtI4SuNqB6XBVQ7jFTdo\nK/+GiWemNmmt59omFouNfXf6dLsIalZZGJrI6lV1zwIcOP8hQ871YsE0mpRJUVFWTnZ5WjpSF09t\n+WhbJdf4DMDdv2/foYMHDx46c+YMW7dupba2lutDw1UrV674G+B3/xr9VT1gVLFMWjk4SoOg/ope\nZyo9/elzq1/QQIs7v8u11oyMjFBTU6OszKas0I4D6t4nG1qDAqwWJnWG0IbrAdz737ZSCmsNUvAw\nmAnxJsxcHyEoV+FUgDiWhXfL2RS89FXdtSfeYss8oDEGz/Ow1mKMQReq9vXmE4SSVyPAAl3KiT9b\n2X/7XZZMPcbz9sWTl94/9+bn63vUXvaKCxCGIclkkmQyCcCxlhZJ/jHI3XR60hqjpdinloiUSJ/0\nlZQuLC/Z0L9B4n2V73zR08ORXUcEyFd2fHycTCZT7KWLHR3q5s3xyslMxlhrlYjklVvLuv6V/tE3\nTr29vGzZxlffe+q1jV82hObRwFZXVxONRvnXn/e/ounCJzU/z5yPPOzsH4cGnEj6mhLzAAAAAElF\nTkSuQmCC", 22 | parameters => [ { type => "string", desc => "要复制的标签,以逗号分隔." } ] 23 | ); 24 | 25 | } 26 | 27 | #Mandatory function to be implemented by your plugin 28 | sub get_tags { 29 | 30 | shift; 31 | my $lrr_info = shift; # Global info hash 32 | my ($tagstocopy) = @_; # Plugin parameters 33 | 34 | my $logger = get_logger( "Tag Copy", "plugins" ); 35 | 36 | #Tags to copy is the first global argument 37 | $logger->debug( "Sending the following tags to LRR: " . $tagstocopy ); 38 | return ( tags => $tagstocopy ); 39 | 40 | } 41 | 42 | 1; 43 | -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Metadata/DateAdded.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Metadata::DateAdded; 2 | 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | #Plugins can freely use all Perl packages already installed on the system 7 | #Try however to restrain yourself to the ones already installed for LRR (see tools/cpanfile) to avoid extra installations by the end-user. 8 | use Mojo::UserAgent; 9 | 10 | use LANraragi::Model::Plugins; 11 | use LANraragi::Utils::Logging qw(get_logger); 12 | 13 | #Meta-information about your plugin. 14 | sub plugin_info { 15 | 16 | return ( 17 | #Standard metadata 18 | name => "Add Timestamp Tag", 19 | type => "metadata", 20 | namespace => "DateAddedPlugin", 21 | author => "Utazukin", 22 | version => "1.0", 23 | description => 24 | "将时间戳标记添加到您的存档。
    此插件遵循时间戳标签的服务器设置,如果服务器设置设置为“使用上次修改时间”,则将使用文件修改时间.", 25 | icon => 26 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA\nB3RJTUUH4wYCFQY4HfiAJAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH\nAAADKUlEQVQ4y6WVsUtrVxzHP+fmkkiqJr2CQWKkvCTwJgkJDpmyVAR1cVOhdq04tHNB7BD8A97S\nXYkO3dRRsMSlFoIOLYFEohiDiiTNNeaGpLn5dRDv06ev75V+4SyH8/2c3/n+zuEoEeFTqtfrb5RS\nJZ/P98m1iMirI5fLMT8/L+FwWEKhkIRCIXn79q2srKxIpVL5qE/7cINms8ny8rIkEgkpl8skk0lm\nZ2eZmZkhHo+TzWYJBoOyvr4u7Xb7RYHq6ZEvLi6Ynp6WVqvFwsIC4+PjRCIRDMNAKcXNzQ2lUols\nNsvOzg6xWIxMJqOeRuEAq9UqqVRKhoaGmJubY2pqCl3XiUajXF5e0t/fz+DgIIVCAbfbzdbWFtvb\n24yMjLC/v6+eZWjbNqurq5JIJGRtbU0syxLbtsU0TXmqXq8njUZDRERubm4knU6LYRiSyWScDBER\nGo0G4XBYFhcX5fz8XP4yTbGLf0hnd0s+plqtJru7u7K0tCSRSEQ6nc77ppycnFCv10kmk4yOjoII\n2kiIv3//lfbGu1dvh1KKVCrF2NgYmqaRy+UAHoCHh4f4fD4mJiZwuVz4fT74YhDvTz/TPv2TX378\ngWKx+Azo9/sZGBhAKYVhGBSLxa8doGmaABiGQT6fp9VqPbg0jcr897w7+I3FxUVs23aAlmVxe3tL\nPB7n/v6eWq22D6A/lq+UotlsEo1G8Xg8jvFNOMzCN99iGF/icrmc+b6+PrxeL6enp7hcLpR6aLT+\nuEDTNEqlErFYDMuy8Hq9AHg8HpaXv3uRYbfbRdM0TNNE096/Dweo6zoHBwfE43F0XXeAjyf4UJVK\nhUql8iwGJ8NHeb1e9vb2CAaDADQajRcgy7IACAQCHB0d/TtQ0zQuLi7Y3Nzk+vqacrkMwNXVFXd3\nd7Tbbc7Ozuh0OmxsbHB1dfViQ/21+3V8fIxpmkxOTmKaJrZt0263sW0b27ZJp9M0m010XX8RhwN8\nNPV6PQCKxSL5fB7DMAgEAnS7XarVKtVqFbfbjVIK27ZRSjkeB9jtdikUChQKBf6vlIg4Gb3Wzc/V\n8PDwV36//1x9zhfwX/QPryPQMvGWTdEAAAAASUVORK5CYII=", 27 | parameters => [], 28 | oneshot_arg => 29 | "Use file modified time (yes/true), or use current time (no/false).
    Leaving blank uses the server setting (default: current time)" 30 | ); 31 | 32 | } 33 | 34 | #Mandatory function to be implemented by your plugin 35 | sub get_tags { 36 | 37 | shift; 38 | my $lrr_info = shift; # Global info hash 39 | my ($use_filetime) = LANraragi::Model::Config->use_lastmodified; # Server setting 40 | 41 | #Use the logger to output status - they'll be passed to a specialized logfile and written to STDOUT. 42 | my $logger = get_logger( "Date Added Plugin", "plugins" ); 43 | 44 | #Work your magic here - You can create subroutines below to organize the code better 45 | 46 | $logger->debug( "Processing file: " . $lrr_info->{file_path} ); 47 | my $newtags = ""; 48 | my $oneshotarg = $lrr_info->{oneshot_param}; 49 | my $oneshot_file_time = $oneshotarg =~ /^(yes|true)$/i; 50 | my $oneshot_current_time = $oneshotarg =~ /^(no|false)$/i; 51 | 52 | if ( $oneshot_file_time || ( $use_filetime && !$oneshot_current_time ) ) { 53 | $logger->info("Using file date"); 54 | $newtags = "date_added:" . ( stat( $lrr_info->{file_path} ) )[9]; #9 is the unix time stamp for date modified. 55 | } else { 56 | $logger->info("Using current date"); 57 | $newtags = "date_added:" . time(); 58 | } 59 | return ( tags => $newtags ); 60 | } 61 | 62 | 1; 63 | 64 | -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Scripts/BlacklistMigrate.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Scripts::BlacklistMigrate; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'uninitialized'; 6 | use utf8; 7 | use LANraragi::Utils::Logging qw(get_plugin_logger); 8 | use LANraragi::Utils::Database qw(save_computed_tagrules); 9 | use LANraragi::Utils::Tags qw(tags_rules_to_array restore_CRLF); 10 | use LANraragi::Utils::Generic qw(remove_spaces); 11 | use Mojo::JSON qw(encode_json); 12 | use LANraragi::Model::Config; 13 | 14 | #Meta-information about your plugin. 15 | sub plugin_info { 16 | 17 | return ( 18 | #Standard metadata 19 | name => "Blacklist Migration", 20 | type => "script", 21 | namespace => "blist2rule", 22 | author => "Difegue", 23 | version => "1.0", 24 | description => "将您的黑名单从 LANraragi < 0.8.0 的数据库迁移到新的标签规则系统." 25 | ); 26 | 27 | } 28 | 29 | # Mandatory function to be implemented by your script 30 | sub run_script { 31 | shift; 32 | my $lrr_info = shift; # Global info hash 33 | 34 | my $logger = get_plugin_logger(); 35 | my $redis = LANraragi::Model::Config->get_redis; 36 | 37 | my $blist = LANraragi::Model::Config::get_redis_conf( "blacklist", undef ); 38 | my $rules = LANraragi::Model::Config::get_redis_conf( "tagrules", 39 | "-already uploaded;-forbidden content;-incomplete;-ongoing;-complete;-various;-digital;-translated;-russian;-chinese;-portuguese;-french;-spanish;-italian;-vietnamese;-german;-indonesian" 40 | ); 41 | 42 | unless ($blist) { 43 | $logger->info("No blacklist in config, nothing to migrate!"); 44 | return ( status => "Nothing to migrate" ); 45 | } 46 | 47 | $logger->debug("Blacklist is $blist"); 48 | $logger->debug("Rules are $rules"); 49 | my @blacklist = split( ',', $blist ); # array-ize the blacklist string 50 | my $migrated = 0; 51 | 52 | # Parse the blacklist and add matching tag rules. 53 | foreach my $tag (@blacklist) { 54 | 55 | remove_spaces($tag); 56 | if ( index( uc($rules), uc($tag) ) == -1 ) { 57 | $logger->debug("Adding rule -$tag"); 58 | $rules = $rules . ";-$tag"; 59 | $migrated++; 60 | } 61 | } 62 | 63 | # Save rules and recompute them 64 | $redis->hset( "LRR_CONFIG", "tagrules", $rules ); 65 | $redis->hdel( "LRR_CONFIG", "blacklist" ); 66 | 67 | my @computed_tagrules = tags_rules_to_array( restore_CRLF($rules) ); 68 | $logger->debug( "Saving computed tag rules : " . encode_json( \@computed_tagrules ) ); 69 | save_computed_tagrules( \@computed_tagrules ); 70 | 71 | return ( migrated_tags => $migrated ); 72 | } 73 | 74 | 1; 75 | -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Scripts/SourceFinder.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Scripts::SourceFinder; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'uninitialized'; 6 | use utf8; 7 | use Mojo::UserAgent; 8 | use LANraragi::Utils::Logging qw(get_plugin_logger); 9 | use LANraragi::Model::Stats; 10 | use LANraragi::Utils::Generic qw(trim_url); 11 | 12 | #Meta-information about your plugin. 13 | sub plugin_info { 14 | 15 | return ( 16 | #Standard metadata 17 | name => "Source Finder", 18 | type => "script", 19 | namespace => "urlfinder", 20 | author => "Difegue", 21 | version => "2.0", 22 | description => "在数据库中查找匹配给定链接 'source:' 的标签 .", 23 | icon => 24 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABZSURBVDhPzY5JCgAhDATzSl+e/2irOUjQSFzQog5hhqIl3uBEHPxIXK7oFXwVE+Hj5IYX4lYVtN6MUW4tGw5jNdjdt5bLkwX1q2rFU0/EIJ9OUEm8xquYOQFEhr9vvu2U8gAAAABJRU5ErkJggg==", 25 | oneshot_arg => "要搜索的网址." 26 | ); 27 | 28 | } 29 | 30 | # Mandatory function to be implemented by your script 31 | sub run_script { 32 | shift; 33 | my $lrr_info = shift; # Global info hash 34 | my $logger = get_plugin_logger(); 35 | 36 | # Only info we need is the URL to search 37 | my $url = $lrr_info->{oneshot_param}; 38 | $logger->debug( "Looking for URL " . $url ); 39 | 40 | trim_url($url); 41 | 42 | if ( $url eq "" ) { 43 | return ( error => "No URL specified!", total => 0 ); 44 | } 45 | 46 | my $recorded_id = LANraragi::Model::Stats::is_url_recorded($url); 47 | if ($recorded_id) { 48 | return ( 49 | total => 1, 50 | id => $recorded_id 51 | ); 52 | } 53 | 54 | # Specific variant for EH/Ex URLs, where we'll check with the other domain as well. 55 | my $last_chance_id = ""; 56 | if ( $url =~ /https?:\/\/exhentai\.org\/g\/([0-9]*)\/([0-z]*)\/*.*/gi ) { 57 | my $url2 = "https://e-hentai.org/g/$1/$2"; 58 | $last_chance_id = LANraragi::Model::Stats::is_url_recorded($url2); 59 | } 60 | 61 | if ( $url =~ /https?:\/\/e-hentai\.org\/g\/([0-9]*)\/([0-z]*)\/*.*/gi ) { 62 | my $url2 = "https://exhentai.org/g/$1/$2"; 63 | $last_chance_id = LANraragi::Model::Stats::is_url_recorded($url); 64 | } 65 | 66 | if ($last_chance_id) { 67 | return ( 68 | total => 1, 69 | id => $last_chance_id 70 | ); 71 | } 72 | 73 | return ( error => "URL not found in database.", total => 0 ); 74 | } 75 | 76 | 1; 77 | -------------------------------------------------------------------------------- /lib/LANraragi/Plugin/Scripts/nHentaiSourceConverter.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Plugin::Scripts::nHentaiSourceConverter; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'uninitialized'; 6 | use utf8; 7 | use LANraragi::Utils::Logging qw(get_plugin_logger); 8 | use LANraragi::Utils::Database qw(invalidate_cache); 9 | use LANraragi::Model::Config; 10 | 11 | #Meta-information about your plugin. 12 | sub plugin_info { 13 | 14 | return ( 15 | #Standard metadata 16 | name => "nHentai Source Converter", 17 | type => "script", 18 | namespace => "nhsrcconv", 19 | author => "Guerra24", 20 | version => "1.0", 21 | description => 22 | "转换 \"source:{id}\" 6 位或更少位数的标签 \"source:nhentai.net/g/{id}\"" 23 | ); 24 | 25 | } 26 | 27 | # Mandatory function to be implemented by your script 28 | sub run_script { 29 | shift; 30 | my $lrr_info = shift; # Global info hash 31 | 32 | my $logger = get_plugin_logger(); 33 | my $redis = LANraragi::Model::Config->get_redis; 34 | 35 | my @keys = $redis->keys('????????????????????????????????????????'); #40-character long keys only => Archive IDs 36 | 37 | my $count = 0; 38 | #Parse the archive list and add them to JSON. 39 | foreach my $id (@keys) { 40 | 41 | my %hash = $redis->hgetall($id); 42 | my ( $tags ) = @hash{qw(tags)}; 43 | 44 | if ( $tags =~ s/source:(\d{1,6})/source:nhentai\.net\/g\/$1/igm ) { 45 | $count++; 46 | } 47 | 48 | $redis->hset( $id, "tags", $tags ); 49 | } 50 | 51 | invalidate_cache(); 52 | $redis->quit(); 53 | 54 | return ( modified => $count ); 55 | } 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /lib/LANraragi/Utils/Logging.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Utils::Logging; 2 | 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | 7 | use feature 'say'; 8 | use POSIX; 9 | use FindBin; 10 | 11 | use Encode; 12 | use File::ReadBackwards; 13 | 14 | # Contains all functions related to logging. 15 | use Exporter 'import'; 16 | our @EXPORT_OK = qw(get_logger get_plugin_logger get_logdir get_lines_from_file); 17 | 18 | # Get the Log folder. 19 | sub get_logdir { 20 | 21 | my $log_folder = "$FindBin::Bin/../log"; 22 | 23 | # Folder location can be overriden by LRR_LOG_DIRECTORY 24 | if ( $ENV{LRR_LOG_DIRECTORY} ) { 25 | $log_folder = $ENV{LRR_LOG_DIRECTORY}; 26 | } 27 | mkdir $log_folder; 28 | return $log_folder; 29 | } 30 | 31 | # Returns a Logger object with a custom name and a filename for the log file. 32 | sub get_logger { 33 | 34 | #Customize log file location and minimum log level 35 | my $pgname = $_[0]; 36 | my $logfile = $_[1]; 37 | 38 | my $logpath = get_logdir . "/$logfile.log"; 39 | 40 | if ( -e $logpath && -s $logpath > 1048576 ) { 41 | 42 | # Rotate log if it's > 1MB 43 | say "Rotating logfile $logfile"; 44 | new Logfile::Rotate( File => $logpath, Gzip => 'lib' )->rotate(); 45 | } 46 | 47 | my $log = Mojo::Log->new( 48 | path => $logpath, 49 | level => 'info' 50 | ); 51 | 52 | my $devmode = LANraragi::Model::Config->enable_devmode; 53 | 54 | #Tell logger to store debug logs as well in debug mode 55 | if ($devmode) { 56 | $log->level('debug'); 57 | } 58 | 59 | #Copy logged messages to STDOUT with the matching name 60 | $log->on( 61 | message => sub { 62 | my ( $time, $level, @lines ) = @_; 63 | 64 | #Like with logging to file, debug logs are only printed in debug mode 65 | unless ( $devmode == 0 && $level eq 'debug' ) { 66 | print "[$pgname] [$level] "; 67 | say $lines[0]; 68 | } 69 | } 70 | ); 71 | 72 | $log->format( 73 | sub { 74 | my ( $time, $level, @lines ) = @_; 75 | my $time2 = strftime( "%Y-%m-%d %H:%M:%S", localtime($time) ); 76 | 77 | my $logstring = join( "\n", @lines ); 78 | 79 | # We'd like to make sure we always show proper UTF-8. 80 | # redis_decode, while not initially designed for this, does the job. 81 | $logstring = LANraragi::Utils::Database::redis_decode($logstring); 82 | 83 | return "[$time2] [$pgname] [$level] $logstring\n"; 84 | } 85 | ); 86 | 87 | return $log; 88 | } 89 | 90 | sub get_plugin_logger { 91 | 92 | my ( $pkg, $filename, $line ) = caller; 93 | 94 | if ( !$pkg->can('plugin_info') ) { 95 | die "\"get_plugin_logger\" cannot be called from \"$pkg\"; line $line at $filename\n"; 96 | } 97 | my %pi = $pkg->plugin_info(); 98 | return get_logger( $pi{name}, "plugins" ); 99 | } 100 | 101 | sub get_lines_from_file { 102 | 103 | my $lines = $_[0]; 104 | my $file = $_[1]; 105 | 106 | #Load the last X lines of file 107 | if ( -e $file ) { 108 | my $bw = File::ReadBackwards->new($file); 109 | my $res = ""; 110 | for ( my $i = 0; $i <= $lines; $i++ ) { 111 | my $line = $bw->readline(); 112 | if ($line) { 113 | $res = $line . $res; 114 | } 115 | 116 | } 117 | 118 | return decode_utf8($res); 119 | } 120 | 121 | return "No logs to be found here!"; 122 | 123 | } 124 | 125 | 1; 126 | -------------------------------------------------------------------------------- /lib/LANraragi/Utils/Tags.pm: -------------------------------------------------------------------------------- 1 | package LANraragi::Utils::Tags; 2 | 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use feature "switch"; 7 | no warnings 'experimental'; 8 | 9 | use LANraragi::Utils::Generic qw(remove_spaces remove_newlines); 10 | 11 | # Functions related to the Tag system. 12 | use Exporter 'import'; 13 | our @EXPORT_OK = 14 | qw( unflat_tagrules replace_CRLF restore_CRLF tags_rules_to_array rewrite_tags split_tags_to_array ); 15 | 16 | sub is_null_or_empty { 17 | return !length(shift); 18 | } 19 | 20 | sub replace_CRLF { 21 | my ($val) = @_; 22 | $val =~ s/\x{d}\x{a}/;/g if ( $val ); 23 | return $val; 24 | } 25 | 26 | sub restore_CRLF { 27 | my ($val) = @_; 28 | $val =~ s/;/\x{d}\x{a}/g if ( $val ); 29 | return $val; 30 | } 31 | 32 | sub unflat_tagrules { 33 | my ( $flattened_rules ) = @_; 34 | my @tagrules = (); 35 | while (@{$flattened_rules || []}) { 36 | push(@tagrules, [ splice(@$flattened_rules, 0, 3) ]); 37 | } 38 | return @tagrules; 39 | } 40 | 41 | sub split_tags_to_array { 42 | my ( $tags_string ) = @_; 43 | my @tags = split( ',', $tags_string ); 44 | foreach my $tags (@tags) { 45 | remove_spaces($tags); 46 | remove_newlines($tags); 47 | } 48 | return @tags; 49 | } 50 | 51 | sub tags_rules_to_array { 52 | my ( $text_rules ) = @_; 53 | my @rules; 54 | my @lines = split( '\n', $text_rules ); 55 | foreach my $line ( @lines ) { 56 | my ( $match, $value ) = split( '->', $line ); 57 | remove_spaces($match); 58 | remove_spaces($value); 59 | if (!is_null_or_empty($match)) { 60 | 61 | my $rule_type; 62 | if ( !$value && $match =~ m/^-.*:\*$/ ) { 63 | $rule_type = 'remove_ns'; 64 | $match = substr ($match, 1, length($match)-3); 65 | } elsif ( !$value && $match =~ m/^-/ ) { 66 | $rule_type = 'remove'; 67 | $match = substr ($match, 1); 68 | } elsif ( !$value && $match =~ m/^~/ ) { 69 | $rule_type = 'strip_ns'; 70 | $match = substr ($match, 1); 71 | } elsif ( $match =~ m/:\*$/ && $value =~ m/:\*$/ ) { 72 | $rule_type = 'replace_ns'; 73 | $match = substr ($match, 0, length($match)-2); 74 | $value = substr ($value, 0, length($value)-2); 75 | } elsif ( !$value ) { 76 | $rule_type = 'remove'; # blacklist mode 77 | } else { 78 | $rule_type = 'replace' 79 | } 80 | 81 | push( @rules, [ $rule_type, lc $match, $value || '' ] ) if ($rule_type); 82 | } 83 | } 84 | return @rules; 85 | } 86 | 87 | sub rewrite_tags { 88 | my ( $tags, $rules ) = @_; 89 | return @$tags if ( !@$rules ); 90 | 91 | my @parsed_tags; 92 | foreach my $tag ( @$tags ) { 93 | my $new_tag = apply_rules($tag, $rules); 94 | push(@parsed_tags, $new_tag) if ($new_tag); 95 | } 96 | return @parsed_tags; 97 | } 98 | 99 | sub apply_rules { 100 | my ( $tag, $rules ) = @_; 101 | 102 | foreach my $rule ( @$rules ) { 103 | my $match = $rule->[1]; 104 | my $value = $rule->[2]; 105 | given($rule->[0]) { 106 | when ('remove') { return if ( lc $tag eq $match ); } 107 | when ('remove_ns') { return if ( $tag =~ m/^$match:/i ); } 108 | when ('replace_ns') { $tag =~ s/^\Q$match:/$value\:/i; } 109 | when ('strip_ns') { $tag =~ s/^\Q$match://i; } 110 | default { $tag = $value if ( lc $tag eq $match ); } 111 | } 112 | } 113 | 114 | return $tag; 115 | } 116 | 117 | 1; 118 | -------------------------------------------------------------------------------- /log/.gitignore: -------------------------------------------------------------------------------- 1 | *.log* 2 | -------------------------------------------------------------------------------- /lrr.conf: -------------------------------------------------------------------------------- 1 | { 2 | redis_address => "127.0.0.1:6379", 3 | redis_database => "0", 4 | redis_database_minion => "1" 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lanraragi", 3 | "version": "0.8.7", 4 | "version_name": "外边", 5 | "description": "我一世英名恐怕要毁在日本文化的影响下了!", 6 | "scripts": { 7 | "test": "prove -r -l -v tests/", 8 | "lanraragi-installer": "perl ./tools/install.pl", 9 | "lint": "eslint --ext .js public/", 10 | "start": "perl ./script/launcher.pl -f ./script/lanraragi", 11 | "dev-server": "perl ./script/launcher.pl -m -v ./script/lanraragi", 12 | "docker-build": "docker build -t difegue/lanraragi -f ./tools/build/docker/Dockerfile .", 13 | "critic": "perlcritic ./lib/* ./script/* ./tools/install.pl", 14 | "backup-db": "perl ./script/backup", 15 | "get-version": "perl -Mojo -E \"my \\$conf = j(f(qw(package.json))->slurp); say %\\$conf{version} .q/ - '/. %\\$conf{version_name} .q/'/ \"" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/uparrows/LANraragi_cn.git" 20 | }, 21 | "author": "dezhao", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/uparrows/LANraragi_cn/issues" 25 | }, 26 | "homepage": "https://github.com/uparrows/LANraragi_cn#readme", 27 | "dependencies": { 28 | "@fortawesome/fontawesome-free": "^5.15.4", 29 | "@jcubic/tagger": "^0.4.2", 30 | "allcollapsible": "^1.1.0", 31 | "awesomplete": "^1.1.5", 32 | "blueimp-file-upload": "^10.32.0", 33 | "clsx": "^1.1.1", 34 | "datatables.net": "^1.11.5", 35 | "fscreen": "^1.2.0", 36 | "inter-ui": "^3.19.3", 37 | "jqcloud2": "^2.0.3", 38 | "jquery": "^3.6.0", 39 | "jquery-contextmenu": "^2.9.2", 40 | "marked": "^4.0.14", 41 | "open-sans-fontface": "^1.4.0", 42 | "preact": "^10.7.1", 43 | "react-toastify": "^9.0.0-rc-2", 44 | "roboto-fontface": "^0.8.0", 45 | "sweetalert2": "^11.4.10", 46 | "swiper": "^8.1.4", 47 | "tippy.js": "^6.3.7" 48 | }, 49 | "devDependencies": { 50 | "eslint": "^7.32.0", 51 | "eslint-config-airbnb-base": "^15.0.0", 52 | "eslint-plugin-import": "^2.26.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp 3 | -------------------------------------------------------------------------------- /public/css/.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | webfonts 3 | -------------------------------------------------------------------------------- /public/css/config.css: -------------------------------------------------------------------------------- 1 | /*** custom checkboxes ***/ 2 | input[type='checkbox'] { 3 | -webkit-appearance:none; 4 | -moz-appearance:none; 5 | appearance:none; 6 | font-size:20px; 7 | cursor:pointer; 8 | margin-left: 0px; 9 | color: inherit; 10 | } 11 | 12 | input[type='checkbox']::before { 13 | content: "\f204"; 14 | } 15 | 16 | input[type='checkbox']::after { 17 | font-size: 15px; 18 | margin-left: 5px; 19 | font-weight: bold; 20 | font-family: sans-serif; 21 | content: "OFF"; 22 | } 23 | 24 | input[type='checkbox']:checked::before { 25 | content: "\f205"; 26 | } 27 | 28 | input[type='checkbox']:checked::after { 29 | font-size: 15px; 30 | margin-left: 5px; 31 | font-weight: bold; 32 | font-family: sans-serif; 33 | content: "ON"; 34 | } 35 | 36 | .config-panel { 37 | float:left; 38 | margin-left: 5% !important; 39 | text-align: left; 40 | width:90%; 41 | margin-bottom: 10px; 42 | } 43 | 44 | .config-btn { 45 | display:inline; 46 | min-width:96px !important; 47 | height:28px !important; 48 | } 49 | 50 | /* lrr.css override */ 51 | .config-td { 52 | max-width: 2000px !important; 53 | width: 30vw; 54 | vertical-align: middle; 55 | text-align: left; 56 | padding-bottom: 10px; 57 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/favicon.ico -------------------------------------------------------------------------------- /public/img/.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | -------------------------------------------------------------------------------- /public/img/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/empty.png -------------------------------------------------------------------------------- /public/img/flubbed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/flubbed.gif -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/logo.png -------------------------------------------------------------------------------- /public/img/noThumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/noThumb.png -------------------------------------------------------------------------------- /public/img/notfound.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/notfound.jpg -------------------------------------------------------------------------------- /public/img/theme_preview/hachikuji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/hachikuji.png -------------------------------------------------------------------------------- /public/img/theme_preview/hverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/hverse.png -------------------------------------------------------------------------------- /public/img/theme_preview/nadeko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/nadeko.png -------------------------------------------------------------------------------- /public/img/theme_preview/sadpanda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/sadpanda.png -------------------------------------------------------------------------------- /public/img/theme_preview/yotsugi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/yotsugi.png -------------------------------------------------------------------------------- /public/img/wait_warmly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/wait_warmly.jpg -------------------------------------------------------------------------------- /public/js/.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /public/js/backup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Backup Operations. 3 | */ 4 | const Backup = {}; 5 | 6 | Backup.initializeAll = function () { 7 | // bind events to DOM 8 | $(document).on("click.return", "#return", () => { window.location.href = "/"; }); 9 | $(document).on("click.do-backup", "#do-backup", () => { window.open("./backup?dobackup=1", "_blank"); }); 10 | 11 | // Handler for file uploading. 12 | $("#fileupload").fileupload({ 13 | dataType: "json", 14 | done(e, data) { 15 | $("#processing").attr("style", "display:none"); 16 | 17 | if (data.result.success === 1) $("#result").html("Backup restored!"); 18 | else $("#result").html(data.result.error); 19 | }, 20 | 21 | fail() { 22 | $("#processing").attr("style", "display:none"); 23 | $("#result").html("An error occured server-side. woops.
    Maybe your JSON is badly formatted ?"); 24 | }, 25 | 26 | progressall() { 27 | $("#result").html(""); 28 | $("#processing").attr("style", ""); 29 | }, 30 | 31 | }); 32 | }; 33 | 34 | jQuery(() => { 35 | Backup.initializeAll(); 36 | }); 37 | -------------------------------------------------------------------------------- /public/js/edit.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/js/edit.js -------------------------------------------------------------------------------- /public/js/logs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Logs Operations. 3 | */ 4 | const Logs = {}; 5 | 6 | Logs.lastType = ""; 7 | 8 | Logs.initializeAll = function () { 9 | // bind events to DOM 10 | $(document).on("click.refresh", "#refresh", Logs.refreshLog); 11 | $(document).on("click.loglines", "#loglines", Logs.refreshLog); 12 | $(document).on("click.show-general", "#show-general", () => Logs.showLog("general")); 13 | $(document).on("click.show-shinobu", "#show-shinobu", () => Logs.showLog("shinobu")); 14 | $(document).on("click.show-plugins", "#show-plugins", () => Logs.showLog("plugins")); 15 | $(document).on("click.show-mojo", "#show-mojo", () => Logs.showLog("mojo")); 16 | $(document).on("click.show-redis", "#show-redis", () => Logs.showLog("redis")); 17 | $(document).on("click.return", "#return", () => { window.location.href = "/"; }); 18 | 19 | Logs.showLog("general"); 20 | }; 21 | 22 | Logs.showLog = function (type) { 23 | fetch(`/logs/${type}?lines=${$("#loglines").val()}`) 24 | .then((response) => response.text()) 25 | .then((data) => { 26 | $("#log-container").html(LRR.encodeHTML(data)); 27 | $("#indicator").html(type); 28 | $("#log-container").scrollTop($("#log-container").prop("scrollHeight")); 29 | Logs.lastType = type; 30 | }) 31 | .catch((error) => LRR.showErrorToast("从服务器获取日志时出错", error)); 32 | }; 33 | 34 | Logs.refreshLog = function () { 35 | Logs.showLog(Logs.lastType); 36 | }; 37 | 38 | jQuery(() => { 39 | Logs.initializeAll(); 40 | }); 41 | -------------------------------------------------------------------------------- /public/js/plugins.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugins Operations. 3 | */ 4 | const Plugins = {}; 5 | 6 | Plugins.initializeAll = function () { 7 | // bind events to DOM 8 | $(document).on("click.save", "#save", () => Server.saveFormData("#editPluginForm")); 9 | $(document).on("click.return", "#return", () => { window.location.href = "/"; }); 10 | 11 | // Handler for file uploading. 12 | $("#fileupload").fileupload({ 13 | url: "/config/plugins/upload", 14 | dataType: "json", 15 | done(e, data) { 16 | if (data.result.success) { 17 | LRR.toast({ 18 | heading: "插件上传成功!", 19 | text: `该插件 "${data.result.name}" 已添加成功. 刷新页面即可查看.`, 20 | icon: "info", 21 | hideAfter: 10000, 22 | }); 23 | } else { 24 | LRR.toast({ 25 | heading: "上传插件出错", 26 | text: data.result.error, 27 | icon: "error", 28 | hideAfter: false, 29 | }); 30 | } 31 | }, 32 | 33 | }); 34 | }; 35 | 36 | jQuery(() => { 37 | Plugins.initializeAll(); 38 | }); 39 | -------------------------------------------------------------------------------- /public/js/stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Stats Operations. 3 | */ 4 | const Stats = {}; 5 | 6 | Stats.initializeAll = function () { 7 | // bind events to DOM 8 | $(document).on("click.goback", "#goback", () => { window.location.replace("./"); }); 9 | 10 | Server.callAPI("/api/database/stats?minweight=2", "GET", null, "无法加载标签统计数据", 11 | (data) => { 12 | $("#statsLoading").hide(); 13 | $("#tagcount").html(data.length); 14 | $("#tagCloud").jQCloud(data, { 15 | autoResize: true, 16 | }); 17 | 18 | // Sort data by weight 19 | data.sort((a, b) => b.weight - a.weight); 20 | 21 | // Buildup detailed stats 22 | const tagList = $("#tagList"); 23 | data.forEach((tag) => { 24 | const namespacedTag = LRR.buildNamespacedTag(tag.namespace, tag.text); 25 | const url = LRR.getTagSearchURL(tag.namespace, tag.text); 26 | 27 | const ocss = "max-width: 95%; display: flex;"; 28 | const icss = "text-overflow: ellipsis; white-space: nowrap; overflow: hidden; min-width: 0; max-width: 100%;"; 29 | 30 | const html = `${namespacedTag} (${tag.weight})`; 31 | tagList.append(html); 32 | }); 33 | 34 | $("#detailedStats").show(); 35 | }, 36 | ); 37 | }; 38 | 39 | jQuery(() => { 40 | Stats.initializeAll(); 41 | }); 42 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /script/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | BEGIN { unshift @INC, "$FindBin::Bin/../lib"; } 8 | use Mojolicious; 9 | use LANraragi::Model::Backup; 10 | use LANraragi::Model::Config; 11 | 12 | print LANraragi::Model::Backup::build_backup_JSON(); 13 | -------------------------------------------------------------------------------- /script/lanraragi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | BEGIN { unshift @INC, "$FindBin::Bin/../lib"; } 8 | use Mojolicious::Commands; 9 | 10 | # Start command line interface for application 11 | Mojolicious::Commands->start_app('LANraragi'); 12 | -------------------------------------------------------------------------------- /script/launcher.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Cwd 'abs_path'; 6 | 7 | use Mojo::Base -strict; 8 | use Mojo::Server::Morbo; 9 | use Mojo::Server::Prefork; 10 | use Mojo::Util qw(extract_usage getopt); 11 | use File::Path qw(make_path); 12 | 13 | getopt 14 | 'm|morbo' => \my $morbo, 15 | 'f|foreground' => \$ENV{HYPNOTOAD_FOREGROUND}, 16 | 'h|help' => \my $help, 17 | 'v|verbose' => \$ENV{MORBO_VERBOSE}; 18 | 19 | if ( $ENV{LRR_DATA_DIRECTORY} ) { 20 | make_path( $ENV{LRR_DATA_DIRECTORY} ); 21 | } 22 | 23 | if ( $ENV{LRR_THUMB_DIRECTORY} ) { 24 | make_path( $ENV{LRR_THUMB_DIRECTORY} ); 25 | } 26 | 27 | if ( $ENV{LRR_TEMP_DIRECTORY} ) { 28 | make_path( $ENV{LRR_TEMP_DIRECTORY} ); 29 | } 30 | 31 | die extract_usage if $help || !( my $app = shift || $ENV{HYPNOTOAD_APP} ); 32 | 33 | my @listen; 34 | if ( $ENV{LRR_NETWORK} ) { 35 | @listen = [ $ENV{LRR_NETWORK} ]; 36 | } else { 37 | @listen = ["http://*:3000"]; 38 | } 39 | 40 | # Relocate the Prefork PID file 41 | my $hypno_pid; 42 | if ( $ENV{LRR_TEMP_DIRECTORY} ) { 43 | $hypno_pid = $ENV{LRR_TEMP_DIRECTORY} . "/server.pid"; 44 | } else { 45 | $hypno_pid = "./public/temp/server.pid"; 46 | eval { make_path("./public/temp"); } # Try creating the temp directory 47 | } 48 | 49 | $hypno_pid = abs_path($hypno_pid); 50 | 51 | my $backend; 52 | if ($morbo) { 53 | $backend = Mojo::Server::Morbo->new( keep_alive_timeout => 30 ); 54 | $ENV{MOJO_MODE} = "development"; 55 | $backend->daemon->listen(@listen); 56 | $backend->run($app); 57 | } else { 58 | print "Server PID will be at " . $hypno_pid . "\n"; 59 | 60 | $backend = Mojo::Server::Prefork->new( keep_alive_timeout => 30 ); 61 | $backend->pid_file($hypno_pid); 62 | $backend->listen(@listen); 63 | 64 | $backend->load_app($app); 65 | 66 | $backend->start; 67 | $backend->daemonize if !$ENV{HYPNOTOAD_FOREGROUND}; 68 | 69 | # Start accepting connections 70 | $backend->cleanup(1)->run; 71 | } 72 | 73 | exit; 74 | 75 | =encoding utf8 76 | 77 | =head1 NAME 78 | 79 | LRR launcher using either morbo or Prefork. Morbo always starts in dev mode. 80 | To change the listen port, use the LRR_NETWORK environment variable. 81 | 82 | =head1 SYNOPSIS 83 | 84 | Usage: perl launcher.pl [OPTIONS] lanraragi 85 | 86 | Options: 87 | -m, --morbo Use morbo instead of Prefork 88 | -f, --foreground Keep manager process in foreground (Prefork) 89 | -v, --verbose Print details about what files changed to 90 | STDOUT (morbo) 91 | -h, --help Show this message 92 | 93 | =head1 DESCRIPTION 94 | 95 | Start L and L applications with the 96 | L web server. 97 | 98 | =head1 SEE ALSO 99 | 100 | L, L, L. 101 | 102 | =cut 103 | -------------------------------------------------------------------------------- /templates/backup.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [% title %] - 数据库备份/恢复 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | [% csshead %] 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    38 |

    数据库备份/恢复

    39 | 40 |
    41 | 您可以在此处备份现有数据库,或还原现有备份。

    42 | 通过备份,您可以下载一个包含所有类别和档案ID以及它们匹配的元数据的JSON 43 | 文件。
    44 | 从备份还原将针对数据库中已存在的 ID还原此元数据。
    45 | (分类将始终被恢复)
    46 | 47 | 48 | 49 | 50 | 56 | 63 | 64 |
    51 | 52 |
    53 | 备份数据库 54 |
    55 |
    57 | 58 |
    59 | 恢复备份 60 | 61 |
    62 |
    65 |

    66 | 67 | 68 | 69 | 73 | 74 |

    75 | 76 |
    77 | 78 | 79 |


    80 | 81 | 82 | 83 |

    84 | 85 |
    86 | 87 | [% INCLUDE footer %] 88 | 89 | 90 | -------------------------------------------------------------------------------- /templates/exception.production.html.ep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/templates/exception.production.html.ep -------------------------------------------------------------------------------- /templates/footer.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    4 | [% IF descstr %] 5 | [% descstr %] 6 |
    7 | [% END %] 8 | 由
    LANraragi 驱动. 9 |

    -------------------------------------------------------------------------------- /templates/login.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [% title %] - 登录 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | [% csshead %] 14 | 15 | 16 | 17 | 18 | 19 | 20 |
    21 |

    需要登录才能访问该页面。

    22 | 23 |
    24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 38 | 39 | 40 | [% IF wrongpass %] 41 | 42 | 44 | 45 | [% END %] 46 | 47 |
    密码: 30 | 32 |
    36 | 37 |
    密码 43 | 错误.
    48 |
    49 | 50 |
    51 | 52 | [% INCLUDE footer %] 53 | 54 | 55 | -------------------------------------------------------------------------------- /templates/logs.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [% title %] - 日志 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | [% csshead %] 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
    31 |

    软件日志

    32 | 33 |
    34 | 您可以在此处检查LANraragi日志以进行调试。
    35 | 默认情况下,此视图仅显示每个日志文件的最后100行,最后一行显示最新行。

    36 | 43 |
    汉化:昭君。访问 我的博客 Bilibili空间 获取更多信息和教程
    44 |
    点击下载 安卓客户端汉化版 IOS客户端 Windows客户端 .
    45 |

    46 | 47 |

    当前视图: 通常

    48 | 49 |
    50 | 51 | 52 | 53 | 54 | 55 | 显示行: 56 | 57 |
    58 | 59 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 |
    64 |
    
    65 | 					
    70 | 71 |

    72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |

    84 | 85 | 86 |
    87 | 88 |
    89 | 90 | [% INCLUDE footer %] 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /templates/not_found.production.html.ep: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 找不到网页 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/opds.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | urn:lrr:0 7 | 8 | 11 | 12 | 15 | 16 | [% title %] 17 | 2010-01-10T10:03:10Z 18 | [% motd %] 19 | /favicon.ico 20 | 21 | [% version %] 22 | http://github.org/Difegue/LANraragi 23 | 24 | 25 | [% FOREACH arc IN arclist %] 26 | 27 | [% arc.title %] 28 | urn:lrr:[% arc.arcid %] 29 | [% arc.dateadded %] 30 | [% arc.dateadded %] 31 | 32 | [% arc.author %] 33 | 34 | [% arc.series %] 35 | [% arc.language %] 36 | [% arc.circle %] 37 | [% arc.event %] 38 | [% IF arc.isnew %] 39 | 40 | [% ELSE %] 41 | 42 | [% END %] 43 | [% arc.tags %] 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | [% END %] 56 | 57 | 58 | -------------------------------------------------------------------------------- /templates/opds_entry.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | [% arc.title %] 10 | urn:lrr:[% arc.arcid %] 11 | [% arc.dateadded %] 12 | [% arc.dateadded %] 13 | 14 | [% arc.author %] 15 | 16 | [% arc.series %] 17 | [% arc.language %] 18 | [% arc.circle %] 19 | [% arc.event %] 20 | [% IF arc.isnew %] 21 | 22 | [% ELSE %] 23 | 24 | [% END %] 25 | [% arc.tags %] 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /templates/templates_config/config_files.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    档案文件夹

    4 | 5 | 6 | 9 |
    档案将被放置的目录,如果不存在,将被自动创建. 10 |
    确保运行 LANraragi 的系统的用户对该目录具有读取权限.
    11 | 12 | 13 | 14 | 15 | 16 |

    群晖 eCryptFS 兼容模式

    17 | 18 | 19 | [% IF enablecryptofs %] 20 | [% ELSE %] 21 | [% END %] 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 单击此按钮以触发存档目录的重新扫描,以防丢失文件,
    36 | 或一些数据,例如总页数.
    37 |
    38 | 39 | 40 | 41 | 42 | 43 |

    最大
    临时存档占用

    44 | 45 | 46 | 48 |
    以MB为单位。 临时文件夹包含最近打开的档案,以便于后续读取。
    49 | 当它超过此指定大小时,将自动清空。 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 当前大小: 59 |

    [%tempsize%] MBs

    60 |
    单击此按钮手动清空临时文件夹。 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 存档索引中最后一次执行的搜索将被缓存以加快加载速度。
    72 | 如果上述缓存出了问题,您可以通过单击此按钮将其重置。
    73 |
    74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 在您打开新归档文件之前,它们会在索引中标记为“NEW”。
    85 | 如果要清除这些标志,请单击此按钮。
    86 |
    87 | 88 | -------------------------------------------------------------------------------- /templates/templates_config/config_security.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    启用密码

    4 | 5 | 6 | [% IF enablepass %] 7 | [% ELSE %] 8 | [% END %] 9 | 12 | 13 | 14 | 15 | 16 | 17 |

    新密码

    18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |

    确认密码

    27 | 28 | 29 | 30 |
    如果要更改密码,请仅编辑这些字段。
    31 | 否则将使用已经存储的密码。 32 | 33 | 34 | 35 | 36 | 37 |

    自闭模式

    38 | 39 | 40 | [% IF nofunmode %] 41 | [% ELSE %] 42 | [% END %] 43 | 47 | 48 | 49 | 50 | 51 | 52 |

    API 密钥

    53 | 54 | 55 | 57 |
    如果您希望启用客户端API并输入密码,则必须在此处设置密钥。
    58 | 空密钥将 不会 工作! 59 |
    将会在每个受保护的API调用中提供此密钥作为授权 身份验证头。 60 | 61 | 62 | 63 | 64 | 65 |

    对客户端API启用CORS

    66 | 67 | 68 | [% IF enablecors %] 69 | [% ELSE %] 70 | [% END %] 71 | 77 | 78 | -------------------------------------------------------------------------------- /templates/templates_config/config_shinobu.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    运行状态

    4 | 5 | 6 | 当前后台监视进程工作

    👍 8 | 正常!

    9 | 当前后台监视进程工作

    👹 11 | 异常!

    12 | (PID: ) 13 |
    该进程负责监视您的内容目录,并在 14 | 新档案出现时 15 | 自动进行处理。
    16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 如果进程已死或无响应,您可以通过单击此按钮来重新启动它。 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Minion Worker 处理的备用任务太长,无法在Web的请求/响应生命周期内 34 | 执行。
    35 | 控制台显示当前正在运行和已结束的任务。 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /templates/templates_config/config_tags.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    缩略图文件夹

    4 | 5 | 6 | 9 |
    存档缩略图将位于相应的目录,如果不存在它将被自动创建. 10 |
    确保运行LANraragi的用户对该目录具有读/写访问权限.
    11 | 12 | 13 | 14 | 15 | 16 |

    为页面使用高质量的缩略图

    17 | 18 | 19 | [% IF hqthumbpages %] 20 | [% ELSE %] 21 | [% END %] 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 为所有尚未创建缩略图的档案生成缩略图. 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 重建缩略图中,这可能需要一段时间! 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |

    添加时间戳

    53 | 54 | 55 | [% IF usedateadded %] 56 | [% ELSE %] 57 | [% END %] 58 | 62 | 63 | 64 | 65 | 66 | 67 |

    使用"上次修改时间"

    68 | 69 | 70 | [% IF usedatemodified %] 71 | [% ELSE %] 72 | [% END %] 73 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |

    标签规则

    84 | 85 | 86 | 87 |
    88 | 89 |
    使用插件标记档案时,此处指定的规则将在保存之前应用于标记档案 90 | 到数据库. 91 |
    使用换行符拆分规则. 92 |
    -tag | tag : 删除标签(如黑名单) 93 |
    -namespace:* : 删除此命名空间内的所有标签 94 |
    ~namespace : 从标签中去除命名空间 95 |
    tag -> new-tag : 替换一个标签 96 |
    namespace:* -> new-namespace:* : 用新命名空间替换旧的命名空间 97 | 98 | 99 | -------------------------------------------------------------------------------- /templates/templates_config/config_theme.html.tt2: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 | 所选主题将应用于整个应用程序并显示给所有用户.
    5 | 如果您使用的浏览器支持“theme-color”,则主题的主要颜色也将被应用 6 | 在该处.

    7 | 保存前单击主题进行预览! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
    17 | 19 | 26 |
    27 | 28 |
    29 | 31 | 37 |
    38 | 39 |
    40 | 42 | 48 |
    49 | 50 |
    51 | 52 | 58 |
    59 | 60 |
    61 | 62 | 68 |
    69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /tests/LANraragi/Plugin/Metadata/Chaika.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | 6 | use Cwd qw( getcwd ); 7 | use Mojo::JSON qw(decode_json encode_json); 8 | use Mojo::File; 9 | 10 | use Test::More; 11 | use Test::Deep; 12 | 13 | my $cwd = getcwd(); 14 | my $SAMPLES = "$cwd/tests/samples"; 15 | require "$cwd/tests/mocks.pl"; 16 | 17 | my @tags_list= ( 18 | 'female:sole female', 'male:sole male', 'artist:kemuri haku', 'full censorship', 19 | 'male:shotacon', 'female:defloration', 'female:nakadashi', 'female:big breasts', 20 | 'language:translated', 'language:english' 21 | ); 22 | 23 | use_ok('LANraragi::Plugin::Metadata::Chaika'); 24 | 25 | note ( 'testing retrieving tags by ID ...' ); 26 | 27 | { 28 | my $json = decode_json( Mojo::File->new("$SAMPLES/chaika/001_gid_27240.json")->slurp ); 29 | 30 | no warnings 'once', 'redefine'; 31 | local *LANraragi::Plugin::Metadata::Chaika::get_json_from_chaika = sub { return $json; }; 32 | 33 | my ( $tags, $title ) = LANraragi::Plugin::Metadata::Chaika::tags_from_chaika_id( "my-type", 123 ); 34 | 35 | is($title, $json->{title}, 'gallery title'); 36 | cmp_bag( [ split( ', ', $tags ) ] , \@tags_list, 'gallery tag list'); 37 | } 38 | 39 | note ( 'testing retrieving tags by SHA1 ...' ); 40 | 41 | { 42 | my $json_by_sha1 = decode_json( Mojo::File->new("$SAMPLES/chaika/002_sha1_response.json")->slurp ); 43 | my $json = decode_json( Mojo::File->new("$SAMPLES/chaika/001_gid_27240.json")->slurp ); 44 | my @type_params = (); 45 | 46 | no warnings 'once', 'redefine'; 47 | local *LANraragi::Plugin::Metadata::Chaika::get_plugin_logger = sub { return get_logger_mock(); }; 48 | local *LANraragi::Plugin::Metadata::Chaika::get_json_from_chaika = sub { 49 | my ( $type, $value ) = @_; 50 | push( @type_params, $type ); 51 | return ( $type eq 'sha1' ) ? $json_by_sha1 : $json; 52 | }; 53 | 54 | my ( $tags, $title ) = LANraragi::Plugin::Metadata::Chaika::tags_from_sha1( "my-hash" ); 55 | 56 | is($title, $json->{title}, 'gallery title'); 57 | cmp_bag( [ split( ', ', $tags ) ] , \@tags_list, 'gallery tag list'); 58 | cmp_deeply( \@type_params, [ 'sha1' ], 'API call sequence'); 59 | } 60 | 61 | done_testing(); 62 | -------------------------------------------------------------------------------- /tests/LANraragi/Plugin/Metadata/Fakku.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | 6 | use Cwd qw( getcwd ); 7 | use Mojo::JSON qw(decode_json encode_json); 8 | use Mojo::File; 9 | 10 | use Test::More; 11 | use Test::Deep; 12 | 13 | my $cwd = getcwd(); 14 | my $SAMPLES = "$cwd/tests/samples"; 15 | require "$cwd/tests/mocks.pl"; 16 | 17 | my @tags_list_from_gallery = ( 18 | 'Artist:Hamao', 19 | 'Parody:Original Work', 20 | 'Magazine:Comic Kairakuten 2020-04', 21 | 'Publisher:FAKKU', 'color', 'schoolgirl outfit', 22 | 'osananajimi', 'unlimited', 'non-h', 'illustration' 23 | ); 24 | 25 | use_ok('LANraragi::Plugin::Metadata::Fakku'); 26 | 27 | note("testing searching URL by title ..."); 28 | 29 | { 30 | my $html = ( Mojo::File->new("$SAMPLES/fakku/001_search_response.html")->slurp ); 31 | no warnings 'once', 'redefine'; 32 | local *LANraragi::Plugin::Metadata::Fakku::get_search_result_dom = sub { return Mojo::DOM->new($html); }; 33 | local *LANraragi::Plugin::Metadata::Fakku::get_plugin_logger = sub { return get_logger_mock(); }; 34 | 35 | my $url = LANraragi::Plugin::Metadata::Fakku::search_for_fakku_url("my wonderful manga"); 36 | is( $url, "https://www.fakku.net/hentai/kairakuten-cover-girls-episode-009-hamao-english", "url check" ); 37 | } 38 | 39 | note("testing parsing gallery front page ..."); 40 | 41 | { 42 | my $html = ( Mojo::File->new("$SAMPLES/fakku/002_gallery_front.html")->slurp ); 43 | no warnings 'once', 'redefine'; 44 | local *LANraragi::Plugin::Metadata::Fakku::get_dom_from_fakku = sub { return Mojo::DOM->new($html); }; 45 | local *LANraragi::Plugin::Metadata::Fakku::get_plugin_logger = sub { return get_logger_mock(); }; 46 | 47 | my ( $tags, $title ) = LANraragi::Plugin::Metadata::Fakku::get_tags_from_fakku("https://url/to/my/page.html"); 48 | cmp_bag( [ split( ', ', $tags ) ], \@tags_list_from_gallery, "tag check" ); 49 | is( $title, 'Kairakuten Cover Girl\'s Episode 009: Hamao', "title check" ); 50 | } 51 | 52 | done_testing(); 53 | -------------------------------------------------------------------------------- /tests/LANraragi/Plugin/Metadata/Generic.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | 6 | use Module::Pluggable require => 1, search_path => ['LANraragi::Plugin::Metadata']; 7 | 8 | use Test::More; 9 | use Test::Deep; 10 | 11 | my @required_keywords = qw( author description name namespace type version ); 12 | my @keywords = ( @required_keywords, qw( cooldown icon login_from oneshot_arg parameters ) ); 13 | 14 | my @metadata_modules = plugins(); 15 | 16 | foreach my $plugin (@metadata_modules) { 17 | use_ok($plugin); 18 | can_ok($plugin, 'plugin_info'); 19 | can_ok($plugin, 'get_tags'); 20 | 21 | my %pluginfo = $plugin->plugin_info(); 22 | my @keys = keys %pluginfo; 23 | cmp_deeply( \@keys, subsetof( @keywords ), 'valid keywords' ); 24 | cmp_deeply( \@keys, supersetof( @required_keywords ), 'required keywords' ); 25 | is($pluginfo{type}, 'metadata', 'plugin type'); 26 | } 27 | 28 | done_testing(); 29 | -------------------------------------------------------------------------------- /tests/LANraragi/Plugin/Metadata/Hitomi.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | 6 | use Cwd qw( getcwd ); 7 | use Mojo::JSON qw(decode_json encode_json); 8 | use Mojo::File; 9 | 10 | use Test::More; 11 | use Test::Deep; 12 | 13 | my $cwd = getcwd(); 14 | my $SAMPLES = "$cwd/tests/samples"; 15 | require "$cwd/tests/mocks.pl"; 16 | setup_redis_mock(); 17 | 18 | my @all_tags = ( 19 | 'female:big breasts', 'female:big clit', 'female:blindfold', 'female:clit stimulation', 20 | 'female:collar', 'female:cunnilingus', 'female:elf', 'female:exhibitionism', 21 | 'female:females only', 'female:fff threesome', 'female:fingering', 'female:group', 22 | 'female:masturbation', 'female:nun', 'female:pixie cut', 'female:ponytail', 23 | 'female:slime', 'female:small breasts', 'female:squirting', 'female:stockings', 24 | 'female:tentacles', 'female:tomboy', 'female:tribadism', 'female:unusual pupils', 25 | 'female:yuri', 'story arc', 'parody:original', 'artist:yukataro', 26 | 'group:sonotaozey', 'type:doujinshi', 'language:english' 27 | ); 28 | 29 | use_ok('LANraragi::Plugin::Metadata::Hitomi'); 30 | 31 | note('testing getting tags from JSON ...'); 32 | 33 | { 34 | no warnings 'once', 'redefine'; 35 | local *LANraragi::Plugin::Metadata::Hitomi::get_plugin_logger = sub { return get_logger_mock(); }; 36 | 37 | my $json = decode_json( Mojo::File->new("$SAMPLES/hitomi/2261881.js")->slurp ); 38 | my @tags = LANraragi::Plugin::Metadata::Hitomi::get_tags_from_taglist($json); 39 | 40 | cmp_bag( \@tags, \@all_tags, 'tag list' ); 41 | } 42 | 43 | note('testing getting title from JSON ...'); 44 | 45 | { 46 | no warnings 'once', 'redefine'; 47 | local *LANraragi::Plugin::Metadata::Hitomi::get_plugin_logger = sub { return get_logger_mock(); }; 48 | 49 | my $json = decode_json( Mojo::File->new("$SAMPLES/hitomi/2261881.js")->slurp ); 50 | my @tags = LANraragi::Plugin::Metadata::Hitomi::get_tags_from_taglist($json); 51 | 52 | my $title = LANraragi::Plugin::Metadata::Hitomi::get_title_from_json($json); 53 | 54 | is( $title, 55 | 'Nakayoshi Onna Boukensha wa Yoru ni Naru to Yadoya de Mechakucha Ecchi Suru | Party of Female Adventurers Fuck a lot at the Inn Once Nighttime Comes.', 56 | 'title' 57 | ); 58 | } 59 | 60 | done_testing(); 61 | -------------------------------------------------------------------------------- /tests/LANraragi/Plugin/Metadata/Koromo.t: -------------------------------------------------------------------------------- 1 | # LANraragi::Plugin::Metadata::Koromo 2 | use strict; 3 | use warnings; 4 | use utf8; 5 | use Data::Dumper; 6 | use File::Temp qw(tempfile); 7 | use File::Copy "cp"; 8 | 9 | use Cwd qw( getcwd ); 10 | 11 | use Test::Trap; 12 | use Test::More; 13 | use Test::Deep; 14 | 15 | my $cwd = getcwd(); 16 | my $SAMPLES = "$cwd/tests/samples"; 17 | require "$cwd/tests/mocks.pl"; 18 | 19 | use_ok('LANraragi::Plugin::Metadata::Koromo'); 20 | 21 | note("Koromo Tests"); 22 | { 23 | # Copy the koromo sample json to a temporary directory as it's deleted once parsed 24 | my ( $fh, $filename ) = tempfile(); 25 | cp( $SAMPLES . "/koromo/koromo_sample.json", $fh ); 26 | 27 | # Mock LANraragi::Utils::Archive's subs to return the temporary sample JSON 28 | # Since we're using exports, the methods are under the plugin's namespace. 29 | no warnings 'once', 'redefine'; 30 | local *LANraragi::Plugin::Metadata::Koromo::get_plugin_logger = sub { return get_logger_mock(); }; 31 | local *LANraragi::Plugin::Metadata::Koromo::extract_file_from_archive = sub { $filename }; 32 | local *LANraragi::Plugin::Metadata::Koromo::is_file_in_archive = sub { 1 }; 33 | 34 | my %dummyhash = ( something => 22, file_path => "test" ); 35 | 36 | # Since this is calling the sub directly and not in an object context, 37 | # we pass a dummy string as first parameter to replace the object. 38 | my %ko_tags = trap { LANraragi::Plugin::Metadata::Koromo::get_tags( "", \%dummyhash, 1 ); }; 39 | 40 | my $expected_tags = 41 | "Teacher, Schoolgirl Outfit, Cheating, Hentai, Ahegao, Creampie, Uncensored, Condom, Unlimited, Heart Pupils, Love Hotel, series:Original Work, artist:▲ Chimaki, language:English, source:https://www.fakku.net/hentai/after-school-english_1632947200"; 42 | is( $ko_tags{title}, "After School", "Koromo parsing test 1/2" ); 43 | is( $ko_tags{tags}, $expected_tags, "Koromo parsing test 2/2" ); 44 | } 45 | 46 | done_testing(); 47 | -------------------------------------------------------------------------------- /tests/LANraragi/Plugin/Metadata/nHentai.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | 6 | use Cwd qw( getcwd ); 7 | use Mojo::JSON qw(decode_json encode_json); 8 | use Mojo::File; 9 | 10 | use Test::More; 11 | use Test::Deep; 12 | 13 | my $cwd = getcwd(); 14 | my $SAMPLES = "$cwd/tests/samples"; 15 | require "$cwd/tests/mocks.pl"; 16 | setup_redis_mock(); 17 | 18 | my @all_tags = ( 'language:japanese', 'artist:masamune shirow', 'full color', 'non-h', 'artbook', 'category:manga' ); 19 | 20 | use_ok('LANraragi::Plugin::Metadata::nHentai'); 21 | 22 | note('testing searching gallery by title ...'); 23 | 24 | { 25 | no warnings 'once', 'redefine'; 26 | local *LANraragi::Plugin::Metadata::nHentai::get_plugin_logger = sub { return get_logger_mock(); }; 27 | local *LANraragi::Plugin::Metadata::nHentai::get_gallery_dom_by_title = sub { return; }; 28 | 29 | my $gID = LANraragi::Plugin::Metadata::nHentai::get_gallery_id_from_title("you will not find this"); 30 | 31 | is( $gID, undef, 'empty gallery ID' ); 32 | } 33 | 34 | { 35 | my $body = Mojo::File->new("$SAMPLES/nh/001_search_results.html")->slurp; 36 | 37 | no warnings 'once', 'redefine'; 38 | local *LANraragi::Plugin::Metadata::nHentai::get_plugin_logger = sub { return get_logger_mock(); }; 39 | local *LANraragi::Plugin::Metadata::nHentai::get_gallery_dom_by_title = sub { return Mojo::DOM->new($body); }; 40 | 41 | my $gID = LANraragi::Plugin::Metadata::nHentai::get_gallery_id_from_title("a title that exists"); 42 | 43 | is( $gID, '999999', 'gallery ID' ); 44 | } 45 | 46 | note('testing parsing JSON from HTML ...'); 47 | 48 | { 49 | no warnings 'once', 'redefine'; 50 | local *LANraragi::Plugin::Metadata::nHentai::get_plugin_logger = sub { return get_logger_mock(); }; 51 | 52 | my $body = Mojo::File->new("$SAMPLES/nh/002_gid_52249.html")->slurp; 53 | 54 | my $json = LANraragi::Plugin::Metadata::nHentai::get_json_from_html($body); 55 | 56 | isa_ok( $json, 'HASH', 'json' ); 57 | is( $json->{id}, 52249, 'gallery ID' ); 58 | isa_ok( $json->{title}, 'HASH', 'json.title' ); 59 | is( $json->{title}{pretty}, 'Pieces 1', 'json.title.pretty' ); 60 | isa_ok( $json->{tags}, 'ARRAY', 'json.tags' ); 61 | is( scalar @{ $json->{tags} }, 6, 'tags count' ); 62 | } 63 | 64 | note('testing getting tags from JSON ...'); 65 | 66 | { 67 | no warnings 'once', 'redefine'; 68 | local *LANraragi::Plugin::Metadata::nHentai::get_plugin_logger = sub { return get_logger_mock(); }; 69 | 70 | my $body = Mojo::File->new("$SAMPLES/nh/002_gid_52249.html")->slurp; 71 | my $json = LANraragi::Plugin::Metadata::nHentai::get_json_from_html($body); 72 | 73 | my @tags = LANraragi::Plugin::Metadata::nHentai::get_tags_from_json($json); 74 | 75 | cmp_bag( \@tags, \@all_tags, 'tag list' ); 76 | } 77 | 78 | note('testing getting tags from JSON ...'); 79 | 80 | { 81 | no warnings 'once', 'redefine'; 82 | local *LANraragi::Plugin::Metadata::nHentai::get_plugin_logger = sub { return get_logger_mock(); }; 83 | 84 | my $body = Mojo::File->new("$SAMPLES/nh/002_gid_52249.html")->slurp; 85 | my $json = LANraragi::Plugin::Metadata::nHentai::get_json_from_html($body); 86 | 87 | my $title = LANraragi::Plugin::Metadata::nHentai::get_title_from_json($json); 88 | 89 | is( $title, 'Pieces 1', 'title' ); 90 | } 91 | 92 | done_testing(); 93 | -------------------------------------------------------------------------------- /tests/LANraragi/Utils/Generic.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | 6 | use Test::More; 7 | use Test::Deep; 8 | 9 | BEGIN { use_ok('LANraragi::Utils::Generic'); } 10 | 11 | note('testing rules flattening...'); 12 | 13 | { 14 | my $tagrules = [ 15 | [ 'strip_ns', 'namespace', '' ], 16 | [ 'replace', 'scream', 'Please Stop' ], 17 | [ 'remove', 'ping', ''] 18 | ]; 19 | 20 | my @flattened_rules = LANraragi::Utils::Generic::flat(@$tagrules); 21 | cmp_deeply( 22 | \@flattened_rules, 23 | [ 24 | 'strip_ns', 25 | 'namespace', 26 | '', 27 | 'replace', 28 | 'scream', 29 | 'Please Stop', 30 | 'remove', 31 | 'ping', 32 | '' 33 | ], 34 | 'flattened rules'); 35 | } 36 | 37 | done_testing(); -------------------------------------------------------------------------------- /tests/backup.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Cwd; 5 | 6 | use Mojo::Base 'Mojolicious'; 7 | 8 | use Test::More tests => 1; 9 | use Test::Mojo; 10 | use Test::MockObject; 11 | use Mojo::JSON qw(decode_json encode_json); 12 | use Data::Dumper; 13 | 14 | use LANraragi::Model::Config; 15 | use LANraragi::Model::Backup; 16 | 17 | # Mock Redis 18 | my $cwd = getcwd; 19 | require $cwd . "/tests/mocks.pl"; 20 | setup_redis_mock(); 21 | 22 | # Would've liked to compare JSON strings directly here, but since the key order is non-deterministic it's easier to compare the result hashes. 23 | my %expected_backup = 24 | %{ decode_json 25 | qq({"archives":[{"arcid":"e4c422fd10943dc169e3489a38cdbf57101a5f7e","filename":null,"tags":"parody: jojo's bizarre adventure","thumbhash":null,"title":"Rohan Kishibe goes to Gucci"},{"arcid":"4857fd2e7c00db8b0af0337b94055d8445118630","filename":null,"tags":"artist:shirow masamune","thumbhash":null,"title":"Ghost in the Shell 1.5 - Human-Error Processor vol01ch01"},{"arcid":"e69e43e1355267f7d32a4f9b7f2fe108d2401ebf","filename":null,"tags":"character:segata sanshiro","thumbhash":null,"title":"Saturn Backup Cartridge - Japanese Manual"},{"arcid":"e69e43e1355267f7d32a4f9b7f2fe108d2401ebg","filename":null,"tags":"character:segata","thumbhash":null,"title":"Saturn Backup Cartridge - American Manual"},{"arcid":"28697b96f0ac5858be2614ed10ca47742c9522fd","filename":null,"tags":"parody:fate grand order, group:wadamemo, artist:wada rco, artbook, full color","thumbhash":null,"title":"Fate GO MEMO"},{"arcid":"2810d5e0a8d027ecefebca6237031a0fa7b91eb3","filename":null,"tags":"parody:fate grand order, character:abigail williams, character:artoria pendragon alter, character:asterios, character:ereshkigal, character:gilgamesh, character:hans christian andersen, character:hassan of serenity, character:hector, character:helena blavatsky, character:irisviel von einzbern, character:jeanne alter, character:jeanne darc, character:kiara sessyoin, character:kiyohime, character:lancer, character:martha, character:minamoto no raikou, character:mochizuki chiyome, character:mordred pendragon, character:nitocris, character:oda nobunaga, character:osakabehime, character:penthesilea, character:queen of sheba, character:rin tosaka, character:saber, character:sakata kintoki, character:scheherazade, character:sherlock holmes, character:suzuka gozen, character:tamamo no mae, character:ushiwakamaru, character:waver velvet, character:xuanzang, character:zhuge liang, group:wadamemo, artist:wada rco, artbook, full color","thumbhash":null,"title":"Fate GO MEMO 2"}],"categories":[{"archives":["e69e43e1355267f7d32a4f9b7f2fe108d2401ebf","e69e43e1355267f7d32a4f9b7f2fe108d2401ebg"],"catid":"SET_1589141306","name":"Segata Sanshiro","search":""},{"archives":[],"catid":"SET_1589138380","name":"AMERICA ONRY","search":"American"}]}) 26 | }; 27 | 28 | # Backup the mocked Redis DB and compare it against our known backup object 29 | my $resultJSON = LANraragi::Model::Backup::build_backup_JSON(); 30 | is( %{ decode_json $resultJSON}, %expected_backup, "Backup creation test" ); 31 | -------------------------------------------------------------------------------- /tests/modules.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Cwd; 5 | 6 | use Mojo::Base 'Mojolicious'; 7 | use Test::More; 8 | use Test::Mojo; 9 | use Test::MockObject; 10 | 11 | sub test_module { 12 | my $module_name = shift; 13 | 14 | eval "require $module_name"; 15 | if ($@) { 16 | warn "Could not load module: $@\n"; 17 | return 0; 18 | } 19 | 20 | return 1; 21 | } 22 | 23 | # Mock Redis 24 | my $cwd = getcwd; 25 | require $cwd . "/tests/mocks.pl"; 26 | setup_redis_mock(); 27 | 28 | my @modules = ( 29 | "Shinobu", "LANraragi", 30 | "LANraragi::Utils::Archive", "LANraragi::Utils::Database", 31 | "LANraragi::Utils::Generic", "LANraragi::Utils::Plugins", 32 | "LANraragi::Utils::Routing", "LANraragi::Utils::TempFolder", 33 | "LANraragi::Utils::Logging", "LANraragi::Utils::Minion", 34 | "LANraragi::Utils::Tags", "LANraragi::Controller::Api::Archive", 35 | "LANraragi::Controller::Api::Search", "LANraragi::Controller::Api::Category", 36 | "LANraragi::Controller::Api::Database", "LANraragi::Controller::Api::Shinobu", 37 | "LANraragi::Controller::Api::Minion", "LANraragi::Controller::Api::Other", 38 | "LANraragi::Controller::Backup", "LANraragi::Controller::Batch", 39 | "LANraragi::Controller::Config", "LANraragi::Controller::Edit", 40 | "LANraragi::Controller::Index", "LANraragi::Controller::Logging", 41 | "LANraragi::Controller::Login", "LANraragi::Controller::Plugins", 42 | "LANraragi::Controller::Reader", "LANraragi::Controller::Stats", 43 | "LANraragi::Controller::Upload", "LANraragi::Controller::Category", 44 | "LANraragi::Model::Archive", "LANraragi::Model::Backup", 45 | "LANraragi::Model::Config", "LANraragi::Model::Plugins", 46 | "LANraragi::Model::Reader", "LANraragi::Model::Search", 47 | "LANraragi::Model::Stats", "LANraragi::Model::Category", 48 | "LANraragi::Model::Upload", "LANraragi::Plugin::Metadata::Chaika", 49 | "LANraragi::Plugin::Metadata::CopyTags", "LANraragi::Plugin::Metadata::DateAdded", 50 | "LANraragi::Plugin::Metadata::EHentai", "LANraragi::Plugin::Metadata::Eze", 51 | "LANraragi::Plugin::Metadata::Hdoujin", "LANraragi::Plugin::Metadata::Koromo", 52 | "LANraragi::Plugin::Metadata::MEMS", "LANraragi::Plugin::Metadata::nHentai", 53 | "LANraragi::Plugin::Metadata::RegexParse", "LANraragi::Plugin::Metadata::Fakku", 54 | "LANraragi::Plugin::Login::EHentai", "LANraragi::Plugin::Login::Fakku", 55 | "LANraragi::Plugin::Scripts::SourceFinder", "LANraragi::Plugin::Scripts::FolderToCat", 56 | "LANraragi::Plugin::Download::EHentai", "LANraragi::Plugin::Download::Chaika", 57 | "LANraragi::Plugin::Scripts::nHentaiSourceConverter", "LANraragi::Plugin::Scripts::BlacklistMigrate", 58 | "LANraragi::Plugin::Metadata::Hitomi" 59 | ); 60 | 61 | # Test all modules load properly 62 | foreach my $module_name (@modules) { 63 | ok( test_module($module_name), $module_name ); 64 | } 65 | 66 | done_testing(); 67 | -------------------------------------------------------------------------------- /tests/opds.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Cwd; 5 | 6 | use Mojo::Base 'Mojolicious'; 7 | 8 | use Test::More tests => 1; 9 | use Test::Mojo; 10 | use Test::MockObject; 11 | use Mojo::JSON qw(decode_json encode_json); 12 | use Data::Dumper; 13 | 14 | use Template; 15 | use Mojo::File; 16 | 17 | use LANraragi::Model::Config; 18 | use LANraragi::Model::Archive; 19 | 20 | # Mock Redis 21 | my $cwd = getcwd; 22 | my $SAMPLES = "$cwd/tests/samples"; 23 | require $cwd . "/tests/mocks.pl"; 24 | setup_redis_mock(); 25 | 26 | # Mock basic mojo stuff used by the opds call 27 | my $mojo = Test::MockObject->new(); 28 | $mojo->mock( 'LRR_CONF', sub { LANraragi::Model::Config:: } ); 29 | $mojo->mock( 'LRR_VERSION', sub { return "9.9.9" } ); 30 | $mojo->mock( 'LRR_VERNAME', sub { return "I Can't Give Everything Away" } ); 31 | $mojo->mock( 32 | 'req', 33 | sub { 34 | return Test::MockObject->new()->mock( 'param', sub { return "" } ); 35 | } 36 | ); 37 | $mojo->mock( 38 | 'render_to_string', 39 | sub { 40 | shift; 41 | my %vars = @_; 42 | 43 | #print Dumper \%vars; 44 | 45 | # Use the OG Template::Toolkit here since we don't instantiate mojo 46 | my $tt = Template->new( { INCLUDE_PATH => $cwd . "/templates" } ) || die "$Template::ERROR\n"; 47 | my $output; 48 | 49 | $tt->process( $vars{template} . ".html.tt2", \%vars, \$output ) || die $tt->error(), "\n"; 50 | return $output; 51 | } 52 | ); 53 | 54 | my $expected_opds = ( Mojo::File->new("$SAMPLES/opds/opds_sample.xml")->slurp ); 55 | 56 | # Generate a new OPDS Catalog and compare it against our sample 57 | my $opds_result = LANraragi::Model::Archive::generate_opds_catalog($mojo); 58 | is( $opds_result, $expected_opds, "OPDS API Test" ); 59 | -------------------------------------------------------------------------------- /tests/samples/chaika/001_gid_27240.json: -------------------------------------------------------------------------------- 1 | { 2 | "category": "Manga", 3 | "download": "/archive/27240/download/", 4 | "expunged": false, 5 | "filecount": 19, 6 | "filesize": 63418139, 7 | "fjord": true, 8 | "gallery": 23532, 9 | "posted": 1521353952, 10 | "rating": 4.45, 11 | "tags": [ 12 | "female:sole_female", 13 | "male:sole_male", 14 | "artist:kemuri_haku", 15 | "full_censorship", 16 | "male:shotacon", 17 | "female:defloration", 18 | "female:nakadashi", 19 | "female:big_breasts", 20 | "language:translated", 21 | "language:english" 22 | ], 23 | "title": "[Kemuri Haku] Zettai Seikou Keikaku | Absolute Intercourse Plan (COMIC Shitsurakuten 2016-03) [English] [Redlantern]", 24 | "title_jpn": "[煙ハク] 絶対セイコウ計画 (COMIC 失楽天 2016年3月号) [英訳]", 25 | "uploader": "dankestdungeon" 26 | } -------------------------------------------------------------------------------- /tests/samples/chaika/002_sha1_response.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "category": "Manga", 3 | "download": "/archive/27240/download/", 4 | "expunged": false, 5 | "filecount": 19, 6 | "filesize": 63418139, 7 | "fjord": true, 8 | "gallery": 23532, 9 | "posted": 1521353952, 10 | "rating": 4.45, 11 | "tags": [ 12 | "female:sole_female", 13 | "male:sole_male", 14 | "artist:kemuri_haku", 15 | "full_censorship", 16 | "male:shotacon", 17 | "female:defloration", 18 | "female:nakadashi", 19 | "female:big_breasts", 20 | "language:translated", 21 | "language:english" 22 | ], 23 | "title": "[Kemuri Haku] Zettai Seikou Keikaku | Absolute Intercourse Plan (COMIC Shitsurakuten 2016-03) [English] [Redlantern]", 24 | "title_jpn": "[煙ハク] 絶対セイコウ計画 (COMIC 失楽天 2016年3月号) [英訳]", 25 | "uploader": "dankestdungeon" 26 | }] -------------------------------------------------------------------------------- /tests/samples/eh/001_gid-1866546.json: -------------------------------------------------------------------------------- 1 | { 2 | "gmetadata": [{ 3 | "gid": 1866546, 4 | "token": "2e521d4407", 5 | "archiver_key": "451476--4612fb6468a96cd31b267a06f5bb3293248252f1", 6 | "title": "[Yatsuki Hiyori] Choro Sugi! [Digital]", 7 | "title_jpn": "[\u516b\u6a39\u3072\u3088\u308a] \u3061\u3087\u308d\u3059\u304e\u3063! [DL\u7248]", 8 | "category": "Manga", 9 | "thumb": "https:\/\/ehgt.org\/22\/aa\/22aa3e435e5a7e72e0e7dcb5bbffc6af3a65f76a-1592520-1057-1500-jpg_l.jpg", 10 | "uploader": "hobohobo", 11 | "posted": "1615623691", 12 | "filecount": "69", 13 | "filesize": 76529215, 14 | "expunged": false, 15 | "rating": "4.74", 16 | "torrentcount": "1", 17 | "torrents": [{ 18 | "hash": "939c58055075204838cff0f36499c90dd807734c", 19 | "added": "1615623788", 20 | "name": "(\u6210\u5e74\u30b3\u30df\u30c3\u30af) [\u516b\u6a39\u3072\u3088\u308a] \u3061\u3087\u308d\u3059\u304e\u3063! [DL\u7248].zip", 21 | "tsize": "12016", 22 | "fsize": "76354525" 23 | }], 24 | "tags": [ 25 | "artist:yatsuki hiyori", 26 | "male:business suit", 27 | "male:glasses", 28 | "female:anal", 29 | "female:beauty mark", 30 | "female:big breasts", 31 | "female:business suit", 32 | "female:dark skin", 33 | "female:gyaru", 34 | "female:hair buns", 35 | "female:milf", 36 | "female:nakadashi", 37 | "female:paizuri", 38 | "female:pantyhose", 39 | "female:ponytail", 40 | "female:schoolgirl uniform", 41 | "female:stockings", 42 | "female:sweating", 43 | "tankoubon" 44 | ] 45 | }] 46 | } -------------------------------------------------------------------------------- /tests/samples/eze/eze_full_sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "gallery_info": { 3 | "title": "(C91) [HitenKei (Hiten)] R.E.I.N.A [Chinese] [無邪気漢化組]", 4 | "title_original": "(C91) [HitenKei (Hiten)] R.E.I.N.A [中国翻訳]", 5 | "category": "doujinshi", 6 | "tags": { 7 | "language": [ 8 | "chinese", 9 | "translated" 10 | ], 11 | "parody": [ 12 | "original" 13 | ], 14 | "group": [ 15 | "hitenkei" 16 | ], 17 | "artist": [ 18 | "hiten" 19 | ], 20 | "male": [ 21 | "sole male" 22 | ], 23 | "female": [ 24 | "defloration", 25 | "pantyhose", 26 | "sole female" 27 | ] 28 | }, 29 | "language": "Chinese", 30 | "translated": true, 31 | "favorite_category": null, 32 | "upload_date": [ 33 | 2017, 34 | 1, 35 | 14, 36 | 16, 37 | 46, 38 | 0 39 | ], 40 | "source": { 41 | "site": "exhentai", 42 | "gid": 1017975, 43 | "token": "49b3c275a1", 44 | "parent_gallery": null, 45 | "newer_versions": [] 46 | } 47 | }, 48 | "gallery_info_full": { 49 | "gallery": { 50 | "gid": 1017975, 51 | "token": "49b3c275a1" 52 | }, 53 | "title": "(C91) [HitenKei (Hiten)] R.E.I.N.A [Chinese] [無邪気漢化組]", 54 | "title_original": "(C91) [HitenKei (Hiten)] R.E.I.N.A [中国翻訳]", 55 | "date_uploaded": 1484412360000, 56 | "category": "doujinshi", 57 | "uploader": "cocy", 58 | "rating": { 59 | "average": 4.85, 60 | "count": 557 61 | }, 62 | "favorites": { 63 | "category": -1, 64 | "category_title": "", 65 | "count": 5166 66 | }, 67 | "parent": null, 68 | "newer_versions": [], 69 | "thumbnail": "https://exhentai.org/t/03/a6/03a6180e6de784661608a2c7416588dae530110a-4062963-2116-3000-png_250.jpg", 70 | "thumbnail_size": "large", 71 | "thumbnail_rows": 4, 72 | "image_count": 23, 73 | "images_resized": false, 74 | "total_file_size_approx": 56675532, 75 | "visible": true, 76 | "visible_reason": "", 77 | "language": "Chinese", 78 | "translated": true, 79 | "tags": { 80 | "language": [ 81 | "chinese", 82 | "translated" 83 | ], 84 | "parody": [ 85 | "original" 86 | ], 87 | "group": [ 88 | "hitenkei" 89 | ], 90 | "artist": [ 91 | "hiten" 92 | ], 93 | "male": [ 94 | "sole male" 95 | ], 96 | "female": [ 97 | "defloration", 98 | "pantyhose", 99 | "sole female" 100 | ] 101 | }, 102 | "tags_have_namespace": true, 103 | "torrent_count": 1, 104 | "archiver_key": "458297--6d569f8eddc13d17401566b8786598987906ee0b", 105 | "source": "html", 106 | "source_site": "exhentai", 107 | "date_generated": 1649871435117 108 | } 109 | } -------------------------------------------------------------------------------- /tests/samples/eze/eze_lite_sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "gallery_info": { 3 | "title": "(C72) [Mitarashi Club (Mitarashi Kousei)] Akiko-san to Issho (Kanon) [English] [Belldandy100] [Decensored]", 4 | "title_original": "", 5 | "category": "", 6 | "tags": { 7 | "language": [ 8 | "english", 9 | "translated" 10 | ], 11 | "parody": [ 12 | "kanon" 13 | ], 14 | "character": [ 15 | "akiko minase", 16 | "yuuichi aizawa" 17 | ], 18 | "group": [ 19 | "mitarashi club" 20 | ], 21 | "artist": [ 22 | "mitarashi kousei" 23 | ], 24 | "male": [ 25 | "sole male" 26 | ], 27 | "female": [ 28 | "aunt", 29 | "lingerie", 30 | "sole female" 31 | ], 32 | "misc": [ 33 | "multi-work series" 34 | ] 35 | }, 36 | "language": "English", 37 | "translated": true, 38 | "favorite_category": null, 39 | "upload_date": [ 40 | 2018, 41 | 2, 42 | 2, 43 | 3, 44 | 3, 45 | 0 46 | ], 47 | "source": { 48 | "site": "website", 49 | "gid": 1179590, 50 | "token": "7c5815c77b", 51 | "parent_gallery": null, 52 | "newer_versions": [] 53 | } 54 | }, 55 | "image_api_key": "25ndeea9d85" 56 | } -------------------------------------------------------------------------------- /tests/samples/koromo/koromo_sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "Artist": "▲ Chimaki", 3 | "Description": "Naughty things feel so good! ❤", 4 | "Favorites": 713, 5 | "Language": "English", 6 | "Magazine": "Comic Bavel 2021-11", 7 | "Pages": 20, 8 | "Parody": "Original Work", 9 | "Publisher": "FAKKU", 10 | "Related": [ 11 | "https://www.fakku.net/hentai/after-school-encounter-english", 12 | "https://www.fakku.net/hentai/after-school-thrills-english", 13 | "https://www.fakku.net/hentai/after-school-goddess-2-english", 14 | "https://www.fakku.net/hentai/mark-of-the-saint-5-english", 15 | "https://www.fakku.net/hentai/after-school-with-a-pushover-gal-english", 16 | "https://www.fakku.net/hentai/killing-time-after-school-english", 17 | "https://www.fakku.net/hentai/after-school-revenge-time-english", 18 | "https://www.fakku.net/hentai/after-school-service-time-english", 19 | "https://www.fakku.net/hentai/after-school-temptation-english-1485463915", 20 | "https://www.fakku.net/hentai/after-school-activities-english", 21 | "https://www.fakku.net/hentai/after-school-humiliation-english", 22 | "https://www.fakku.net/hentai/after-school-disclosure-english" 23 | ], 24 | "Tags": [ 25 | "Teacher", 26 | "Schoolgirl Outfit", 27 | "Cheating", 28 | "Hentai", 29 | "Ahegao", 30 | "Creampie", 31 | "Uncensored", 32 | "Condom", 33 | "Unlimited", 34 | "Heart Pupils", 35 | "Love Hotel" 36 | ], 37 | "Thumb": "https://t.fakku.net/images/manga/a/after-school-english_1632947200_1632947200/thumbs/001.thumb.jpg", 38 | "Title": "After School", 39 | "URL": "https://www.fakku.net/hentai/after-school-english_1632947200", 40 | "Released": "2021-10-12 16:00:03 UTC" 41 | } -------------------------------------------------------------------------------- /tools/Documentation/.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./tools/Documentation 2 | 3 | structure: 4 | readme: README.md 5 | summary: SUMMARY.md 6 | -------------------------------------------------------------------------------- /tools/Documentation/.gitignore: -------------------------------------------------------------------------------- 1 | .gitbook -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/backup.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/batch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/batch.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/batchlog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/batchlog.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/brew.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/brew.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/categories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/categories.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/category_filtered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/category_filtered.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/cloud.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/cloud.PNG -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/download.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/downloaders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/downloaders.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/dureader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/dureader.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/edit.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/edit.PNG -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/favtags.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/favtags.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/ichaival.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/ichaival.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/jails.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/jails.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/karen-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/karen-dark.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/karen-distro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/karen-distro.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/karen-light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/karen-light.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/karen-startmenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/karen-startmenu.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/karen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/karen.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/login.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/mountpoints.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/mountpoints.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/opds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/opds.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/ratings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/ratings.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/reader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/reader.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/reader_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/reader_options.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/search.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/shell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/shell.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/shiggy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/shiggy.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/tachiyomi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/tachiyomi.jpg -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/themes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/themes.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/thumbchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/thumbchange.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/uploading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/uploading.png -------------------------------------------------------------------------------- /tools/Documentation/.screenshots/webext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/Documentation/.screenshots/webext.png -------------------------------------------------------------------------------- /tools/Documentation/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | cover: ../_screenshots/archive_thumb.png 3 | coverY: -164.01117318435755 4 | --- 5 | 6 | # LANraragi Documentation 7 | 8 | _I know when to go out_\ 9 | _Know when to stay in_\ 10 | _Get things done_ 11 | 12 | Thank you for showing interest in this networked chinese comic reading software. 13 | 14 | Just getting started? Check out how to install the software: 15 | 16 | {% content-ref url="installing-lanraragi/methods.md" %} 17 | [methods.md](installing-lanraragi/methods.md) 18 | {% endcontent-ref %} 19 | 20 | Or how to use your freshly installed instance: 21 | 22 | {% content-ref url="basic-operations/first-steps.md" %} 23 | [first-steps.md](basic-operations/first-steps.md) 24 | {% endcontent-ref %} 25 | -------------------------------------------------------------------------------- /tools/Documentation/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [LANraragi Documentation](README.md) 4 | 5 | ## Installing LANraragi 6 | 7 | * [❓ Which installation method is best for me?](installing-lanraragi/methods.md) 8 | * [🪟 LRR for Windows (Win10)](installing-lanraragi/windows.md) 9 | * [🍎 Homebrew (macOS)](installing-lanraragi/macos.md) 10 | * [🐳 Docker (All platforms)](installing-lanraragi/docker.md) 11 | * [🛠 Source Code (Linux/macOS)](installing-lanraragi/source.md) 12 | * [🐧 Community (Linux)](installing-lanraragi/community.md) 13 | * [👿 Jail (FreeBSD)](installing-lanraragi/jail.md) 14 | 15 | ## Basic Operations 16 | 17 | * [🚀 Getting Started](basic-operations/first-steps.md) 18 | * [📚 Reading Archives](basic-operations/archives.md) 19 | * [✒ Adding Metadata](basic-operations/metadata.md) 20 | * [🔎 Searching the Archive Index](basic-operations/searching.md) 21 | * [📈 Statistics and Logs](basic-operations/stats.md) 22 | * [🖌 Themes](basic-operations/themes.md) 23 | 24 | ## Advanced Usage 25 | 26 | * [🦇 Batch Operations](advanced-usage/batch-tagging.md) 27 | * [📂 Categories](advanced-usage/categories.md) 28 | * [⬇ Downloading Archives](advanced-usage/downloading.md) 29 | * [💾 Backup and Restore](advanced-usage/backup-and-restore.md) 30 | * [📱 Using External Readers](advanced-usage/external-readers.md) 31 | * [🌐 Network Interface Setup](advanced-usage/network-interfaces.md) 32 | * [🕵️ Proxy Setup](advanced-usage/proxy-setup.md) 33 | * [📏 Tag Rules](advanced-usage/tag-rules.md) 34 | 35 | ## Developer Guide 36 | 37 | * [🏗 Setup a Development Environment](extending-lanraragi/index.md) 38 | * [🏛 Architecture & Style](extending-lanraragi/architecture.md) 39 | 40 | ## API Documentation 41 | 42 | * [🔑 Getting started](api-documentation/getting-started.md) 43 | * [Search API](api-documentation/search-api.md) 44 | * [Archive API](api-documentation/archive-api.md) 45 | * [Database API](api-documentation/database-api.md) 46 | * [Category API](api-documentation/category-api.md) 47 | * [Shinobu API](api-documentation/shinobu-api.md) 48 | * [Minion API](api-documentation/minion-api.md) 49 | * [Miscellaneous other API](api-documentation/miscellaneous-other-api.md) 50 | 51 | ## Writing Plugins 52 | 53 | * [🧩 Getting started](plugin-docs/index.md) 54 | * [Login Plugins](plugin-docs/login.md) 55 | * [Metadata Plugins](plugin-docs/metadata.md) 56 | * [Downloader Plugins](plugin-docs/download.md) 57 | * [Generic Plugins ("Scripts")](plugin-docs/scripts.md) 58 | * [Code Examples](plugin-docs/code-examples.md) 59 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/backup-and-restore.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: You do make backups, right? 3 | --- 4 | 5 | # 💾 Backup and Restore 6 | 7 | This page, available from the top menu once logged in, allows you to backup the entire database to a JSON file. 8 | This includes **all your categories**, and for **every file in the database**: 9 | 10 | * The unique ID of the archive (For the more technologically-enclined: LRR uses a SHA-1 hash of the first 512KBs of the file as the ID) 11 | * The saved tags 12 | * The saved title 13 | 14 | This JSON can then be restored in another LRR instance, if it has archives with matching unique IDs. 15 | 16 | {% hint style="info" %} 17 | You can also make backup JSONs with the `npm run backup-db` command or through the Client API. 18 | {% endhint %} 19 | 20 | ![Average backup.json](../.screenshots/backup.png) 21 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/batch-tagging.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Batch Operations allow you to execute a task on your choice on a selection of 4 | archives. 5 | --- 6 | 7 | # 🦇 Batch Operations 8 | 9 | It's a pretty common occurence: You imported archives without enabling automatic tagging, or you suddenly want to add tags to a lot of archives at once. 10 | 11 | Editing tags manually for each file ain't gonna cut it... 12 | 13 | Enter **Batch Tagging**, allowing for laser-focus, one-time operations over large sets of archives. 14 | 15 | ![Batch Tagging interface as of 0.5.6](../.screenshots/batch.png) 16 | 17 | All your archives are shown in the checklist on the right, with archives with no tags pre-checked for ease of access. 18 | 19 | Past that, it's just a matter of selecting what you want to do, optionally plugging in special arguments for the run, and going ham on batching! 20 | 21 | The currently available operations are: 22 | - **Use Plugin**: Use a plugin on the selected archives. The arguments available for overriding will depend on the plugin. 23 | - **Apply Tag Rules**: Apply your default [Tag Rules](./tag-rules.md) to the selected archives. 24 | - **Clear New**: Remove new flag from selected archives. 25 | - **Delete**: Delete the selected archives. 26 | 27 | {% hint style="info" %} 28 | As shown in the screenshot, you can only override **Global Arguments** in Batch Tagging. 29 | 30 | One-shot arguments, such as specifying a E-Hentai URL, are only available when editing a single archive through the classic Edit menu. 31 | {% endhint %} 32 | 33 | If you set a timeout value, the batch session will wait the specified time between archives. 34 | 35 | ![Batch Tagging status window](../.screenshots/batchlog.png) 36 | 37 | While a batch session runs, you get a live summary of what the server is doing, and can cancel at any time. 38 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/categories.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Organize your archives in dynamic or static categories. 3 | --- 4 | 5 | # 📂 Categories 6 | 7 | Categories appear in the archive index as shortcut buttons. 8 | 9 | ![Example categories](<../.screenshots/favtags.jpg>) 10 | 11 | There are two distinct kinds: 12 | 13 | * 📁 Static Categories are arbitrary collections of Archives, where you can add as many items as you want. 14 | * ⚡ Dynamic Categories contain all archives matching a given predicate, and automatically update alongside your library. 15 | 16 | Toggling a category in the index will restrict all your searches to that category, for as long as it is toggled. If you have a lot of categories, the most recently used will appear first in the list. 17 | **📌Pinned** Categories will always show first. 18 | 19 | ![filtered](../.screenshots/category\_filtered.png) 20 | 21 | To create categories, you can use the dedicated setting page in the app: 22 | 23 | ![Category creation page](../.screenshots/categories.png) 24 | 25 | {% hint style="info" %} 26 | If you have an existing folder hierarchy for your Archives, LRR can automatically create categories from said hierarchy through the dedicated utility Script. 27 | 28 | Look for it in Plugin Configuration. 29 | {% endhint %} 30 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/downloading.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Download remote URLs directly to LANraragi. 3 | --- 4 | 5 | # ⬇ Downloading Archives 6 | 7 | Starting with version 0.7.3, LANraragi can directly download URLs to its content folder. 8 | This allows you to seamlessly add archives from the Internet to your LRR instance for safekeeping. 9 | 10 | ![Upload Center with a PDF downloaded](../.screenshots/download.png) 11 | 12 | By default, we will try to download any URL you chuck at us! This will mostly work for simple URLs that point directly to a file we support. 13 | (For example, something like this very nice Quake booklet: `https://archive.org/download/quake-essays-sep-15-fin-4-graco-l-cl/QUAKE_essays_SEP15_FIN4_GRACoL_CL.pdf` will download without a fuss.) 14 | 15 | {% hint style="info" %} 16 | Downloaded archives will automatically get a `source:` tag with the URL they were downloaded from. 17 | Said source tags can often be used with compatible Metadata plugins to fetch metadata precisely. (Supported by E-H and nH) 18 | {% endhint %} 19 | 20 | For non-direct links, you will need to have a matching **Downloader Plugin** configured. 21 | LANraragi currently ships with Downloaders handling E-H and Chaika links. 22 | 23 | ![Downloader Plugins](../.screenshots/downloaders.png) 24 | 25 | {% hint style="info" %} 26 | Just like with Metadata plugins, Downloaders might require using a matching **Login Plugin** to authenticate to the remote website. 27 | {% endhint %} 28 | 29 | ## Browser Extension 30 | 31 | You can also install the [Tsukihi Browser Extension](https://github.com/Difegue/Tsukihi) to automatically verify/download URLs you browse to your server instance. 32 | 33 | ![Tsukihi Web Extension](../.screenshots/webext.png) 34 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/external-readers.md: -------------------------------------------------------------------------------- 1 | # 📱 Using External Readers 2 | 3 | If the built-in Web Reader is not enough for you, LANraragi's content can be consumed by multiple external applications who provide their own readers. 4 | 5 | ## Dedicated LANraragi readers 6 | 7 | Dedicated LANraragi readers use the Client API to provide an experience closely matching the capabilities of the Web interface. 8 | 9 | Here are some existing clients: 10 | 11 | ### [Ichaival (Android)](https://github.com/Utazukin/Ichaival) 12 | 13 | [Download it here.](https://github.com/Utazukin/Ichaival)\ 14 | ![ichaival](<../.screenshots/ichaival.png>) 15 | 16 | **Features:** 17 | 18 | * View and search your LANraragi database 19 | * View tags 20 | * Read archives 21 | * Bookmark archives to keep track of your current page 22 | * Sort archives by date (requires Timestamp tags to be enabled) 23 | 24 | ### [DuReader (iOS)](https://github.com/Doraemoe/DuReader) 25 | 26 | [Download it here.](https://github.com/Doraemoe/DuReader)\ 27 | ![dureader](../.screenshots/dureader.jpg) 28 | 29 | ### [LRReader (Windows 10)](https://github.com/Guerra24/LRReader) 30 | 31 | [Download it here.](https://github.com/Guerra24/LRReader)\ 32 | ![lrreader]() 33 | 34 | **Features:** 35 | 36 | * Archives list. 37 | * Search. 38 | * Archive overview and reader. 39 | * Bookmarks. 40 | * Multiple servers/profiles. 41 | * Manage your server from within the app. 42 | 43 | ### Tachiyomi reader 44 | 45 | ![Tachiyomi](<../.screenshots/tachiyomi.jpg>) 46 | 47 | The open-source [Tachiyomi](https://tachiyomi.org) Android reader has a readymade plugin to consume the LANraragi API. 48 | You can download it [here.](https://github.com/inorichi/tachiyomi-extensions/blob/repo/apk/tachiyomi-all.lanraragi-v1.2.1.apk) 49 | 50 | ## Generic OPDS readers 51 | 52 | ![Example OPDS reader](<../.screenshots/opds.jpg>) 53 | 54 | Some readers can leverage the [OPDS Catalog](https://opds.io) exposed by LANraragi to visualize and read the available archives. 55 | Those programs can't exploit all of LRR's features(Search, Database backup, Streaming images), but they might have reading features you won't find in the current dedicated clients. 56 | 57 | The URL for the OPDS Catalog is `[YOUR_LANRARAGI_URL]/api/opds`. 58 | You can use [the Demo](https://lrr.tvc-16.science/api/opds) as an example. 59 | Refer to your reader's documentation to figure out where to put this URL. 60 | 61 | {% hint style="warning" %} 62 | If you have No-Fun Mode enabled, remember that you'll need to add the API Key to this URL for the catalog to be available to your reader application. 63 | {% endhint %} 64 | 65 | The following readers have been tested with the OPDS Catalog: 66 | 67 | * [**Moon+ Reader (Android)**](https://play.google.com/store/apps/details?id=com.flyersoft.moonreader) 68 | * [**Librera Reader (Android)**](https://librera.mobi) 69 | * [**Challenger Comics Viewer (Android)**](https://play.google.com/store/apps/details?id=org.kill.geek.bdviewer) 70 | * [**AlfaReader (Windows 7/8/10)**](https://www.alfareader.org) 71 | 72 | The following readers haven't been tested but should work: 73 | 74 | * [**TiReader (iOS)**](http://tireader.com) 75 | * [**Chunky Reader (iOS)**](http://chunkyreader.com) 76 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/network-interfaces.md: -------------------------------------------------------------------------------- 1 | # 🌐 Network Interface Setup 2 | 3 | By default, LRR listens on all IPv4 Interfaces on port 3000. To change this, you have to specify a different network location when starting the app. 4 | 5 | ## Building your network location string 6 | 7 | The network location format accepted by LRR looks like this:\ 8 | `http(s)://*:(port)` 9 | 10 | All listen locations [supported by "listen" in Mojo::Server::Daemon](http://www.mojolicious.org/perldoc/Mojo/Server/Daemon#listen) are valid. 11 | 12 | For example, if you want to listen on port 5555 with SSL only, the string would look like:\ 13 | `https://*:5555?cert=/path/to/server.crt&key=/path/to/server.key` 14 | 15 | Once you have your string ready, you can assign it to the environment variable `LRR_NETWORK`. It'll be picked up automagically. 16 | 17 | {% hint style="info" %} 18 | If you're using Docker, remember to mount your cert and keys to a path reachable by the container:\ 19 | The arguments above will resolve within the container's filesystem! 20 | {% endhint %} 21 | 22 | ## Source Installs 23 | 24 | ```bash 25 | export LRR_NETWORK=http://127.0.0.1:8000 26 | npm start 27 | 28 | > lanraragi@0.6.0 start /mnt/c/Users/tiki/Desktop/lrr 29 | > perl ./script/launcher.pl -f ./script/lanraragi 30 | 31 | キタ━━━━━━(゚∀゚)━━━━━━!!!!! 32 | [LANraragi] [info] LANraragi 0.6.0-BETA.2 (re-)started. (Debug Mode) 33 | [...] 34 | [Mojolicious] Listening at "http://127.0.0.1:8000" 35 | Server available at http://127.0.0.1:8000 36 | ``` 37 | 38 | ## Docker 39 | 40 | ```bash 41 | docker run --name=lanraragi -p 8000:8000 \ 42 | --mount type=bind,source=[YOUR_CONTENT_DIRECTORY],\ 43 | target=/home/koyomi/lanraragi/content \ 44 | -e LRR_NETWORK=http://*:8000 difegue/lanraragi 45 | ``` 46 | 47 | ## Docker with SSL 48 | 49 | ```bash 50 | docker run --name=lanraragi-ssl -p 3333:3333 \ 51 | --mount type=bind,source=[YOUR_CONTENT_DIRECTORY],\ 52 | target=/home/koyomi/lanraragi/content \ 53 | --mount type=bind,source=[DIRECTORY_CONTAINING_SSL_CERT],target=/ssl \ 54 | -e LRR_NETWORK="https://*:3333?cert=/ssl/crt.crt&key=/ssl/crt.key" difegue/lanraragi 55 | ``` 56 | 57 | Notice that the certificate and key must come from your host filesystem and henceforth might need a second --mount command. 58 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/proxy-setup.md: -------------------------------------------------------------------------------- 1 | # 🕵️ Proxy Setup 2 | 3 | ## Setting up LANraragi behind a proxy (reverse proxy setup) 4 | 5 | A common post-install setup is to make requests to the app transit through a gateway server such as Apache or nginx. 6 | If you do so, please note that archive uploads through LRR will likely **not work out of the box** due to maximum sizes on uploads those servers can enforce. The example below is for nginx: 7 | 8 | ``` 9 | http { 10 | client_max_body_size 0; <----------------------- This line here 11 | } 12 | 13 | server { 14 | listen 80; 15 | 16 | server_name lanraragi.[REDACTED].net; 17 | 18 | return 301 https://$host$request_uri; 19 | } 20 | 21 | server { 22 | listen 443 ssl; 23 | index index.php index.html index.htm; 24 | server_name lanraragi.[REDACTED].net; 25 | 26 | client_max_body_size 0; <----------------------- And this line here 27 | 28 | # Cert Stuff Omitted 29 | 30 | location / { 31 | proxy_pass http://0.0.0.0:3000; 32 | proxy_http_version 1.1; 33 | <----- The two following lines are needed for batch tagger support with SSL -----> 34 | proxy_set_header Upgrade $http_upgrade; 35 | proxy_set_header Connection $connection_upgrade; 36 | } 37 | } 38 | ``` 39 | 40 | ## Setting up LANraragi to use a proxy for outbound network requests 41 | 42 | This is a less common scenario, but you might want to have downloads or metadata requests to external services go through a proxy, in case said external services are blocked by your friendly local totalitarian regime. 43 | 44 | LANraragi runs on top of the Mojolicious web server, which has [built-in](https://docs.mojolicious.org/Mojo/UserAgent/Proxy#detect) support for proxifying external requests. 45 | 46 | To enable automatic proxy detection, the `MOJO_PROXY` environment variable must be set to 1 on your machine: This is enabled by default on Docker builds. 47 | Once said detection enabled, environment variables `HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, NO_PROXY` and `no_proxy` will be checked for proxy information. 48 | 49 | Here's an example for a Docker-compose setup: 50 | 51 | ``` 52 | --- 53 | version: "2.1" 54 | services: 55 | lanraragi: 56 | image: difegue/lanraragi:latest 57 | container_name: lanraragi 58 | environment: 59 | - http_proxy=http://192.168.10.186:1082 60 | - https_proxy=http://192.168.10.186:1082 61 | volumes: 62 | - [database]:/home/koyomi/lanraragi/database 63 | - [content]:/home/koyomi/lanraragi/content 64 | ports: 65 | - 7070:3000 66 | restart: unless-stopped 67 | ``` 68 | -------------------------------------------------------------------------------- /tools/Documentation/advanced-usage/tag-rules.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Blacklist or rewrite tags the way you want 3 | --- 4 | 5 | # 📏 Tag Rules 6 | 7 | _Tag Rules_ allow you to write rules that will extend the basic functionality of automatic tagging. The rules will be applied to the list of tags returned by the plugins in order to filter or rewrite the tags based on your tastes. 8 | 9 | _Tag Rules_ must be written one rule per line, without "_comma_" or "_semicolon_" unless they are part of the tag. 10 | 11 | The format is as follows: 12 | 13 | * `tag | -tag` : removes the tag 14 | * `-namespace:*` : removes all tags within this namespace 15 | * `~namespace` : strips the namespace from the tags 16 | * `tag -> new-tag` : replaces one tag 17 | * `namespace:* -> new-namespace:*` : replaces the namespace in all tags that contain it 18 | 19 | Also note that _the match is case insensitive_, but the replacement will keep the case specified in the rule, so you can write this rule 20 | 21 | ``` 22 | serie:one piece -> parody:One Piece 23 | ``` 24 | 25 | to replace `SERIE:ONE PIECE` with `parody:One Piece`. 26 | 27 | ## Blacklist 28 | 29 | This is the simplest list of rules you can write. Both formats are valid: 30 | 31 | ``` 32 | already uploaded 33 | forbidden content 34 | incomplete 35 | ongoing 36 | complete 37 | various 38 | digital 39 | translated 40 | ``` 41 | 42 | ``` 43 | -already uploaded 44 | -forbidden content 45 | -incomplete 46 | -ongoing 47 | -complete 48 | -various 49 | -digital 50 | -translated 51 | ``` 52 | 53 | ## Advanced usage 54 | 55 | Combining the above rules, you can make _LRR_ do some work for you. For example the following set of rules: 56 | 57 | ``` 58 | -already uploaded 59 | -misc:* 60 | serie:* -> parody:* 61 | various -> various artists 62 | ~language 63 | ``` 64 | 65 | will transform this tag list 66 | 67 | ``` 68 | already uploaded, misc:ongoing, misc:complete, language:english, 69 | serie:one piece, serie:naruto, various, crossover' 70 | ``` 71 | 72 | in this 73 | 74 | ``` 75 | english, parody:one piece, parody:naruto, various artists, crossover 76 | ``` 77 | -------------------------------------------------------------------------------- /tools/Documentation/api-documentation/getting-started.md: -------------------------------------------------------------------------------- 1 | # 🔑 Getting started 2 | 3 | The Client API allows you to communicate with a running LANraragi instance from a dedicated client. All the (public)endpoints below can be tested on the demo! 4 | 5 | ## Authenticating with the API 6 | 7 | Most of the API endpoints require a form of authentication. 8 | 9 | Said authentication is provided by a configurable **API Key,** which is set by the user in the LRR settings. 10 | 11 | This key must be added to your calls as an `Authentication: Bearer` header, with the key encoded in base64: 12 | 13 | ```bash 14 | DELETE /api/search/cache HTTP/1.1 15 | Accept: application/json 16 | Authorization: Bearer SEVBVEhFTg== 17 | ``` 18 | 19 | If you fail to meet this requirement, the API endpoint will return error 401 and the following JSON: 20 | 21 | ``` 22 | { 23 | "error":"This API is protected and requires login or an API Key." 24 | } 25 | ``` 26 | 27 | {% hint style="warning" %} 28 | If the user's LRR installation is running under **No-Fun Mode**, all API methods will be locked behind the key. 29 | Empty API Keys will **not** work, even if there's no key set in Configuration. 30 | {% endhint %} 31 | 32 | Private endpoints will be indicated by a 🔑 symbol next to their name in the following sections. 33 | -------------------------------------------------------------------------------- /tools/Documentation/api-documentation/minion-api.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Control the built-in Minion Job Queue. 3 | --- 4 | 5 | # Minion API 6 | 7 | {% swagger baseUrl="http://lrr.tvc-16.science" path="/api/minion/:jobid" method="get" summary="Get the basic status of a Minion Job" %} 8 | {% swagger-description %} 9 | For a given Minion job ID, check whether it succeeded or failed. 10 | Minion jobs are ran for various occasions like thumbnails, cache warmup and handling incoming files. 11 | {% endswagger-description %} 12 | 13 | {% swagger-parameter name="id" type="string" required="true" in="path" %} 14 | ID of the Job. 15 | {% endswagger-parameter %} 16 | 17 | {% swagger-response status="200" description="You get job data." %} 18 | ```javascript 19 | { 20 | "state": "finished", 21 | "task": "handle_upload", 22 | "error": null 23 | } 24 | 25 | { 26 | "state": "failed", 27 | "task": "thumbnail_task", 28 | "error": "oh no" 29 | } 30 | ``` 31 | {% endswagger-response %} 32 | {% endswagger %} 33 | 34 | {% swagger baseUrl="http://lrr.tvc-16.science" path="/api/minion/:jobid/detail" method="get" summary="🔑Get the full status of a Minion Job" %} 35 | {% swagger-description %} 36 | Get the status of a Minion Job. 37 | This API is there for internal usage mostly, but you can use it to get detailed status for jobs like plugin runs or URL downloads. 38 | {% endswagger-description %} 39 | 40 | {% swagger-parameter name="id" type="string" required="true" in="path" %} 41 | ID of the Job. 42 | {% endswagger-parameter %} 43 | 44 | {% swagger-response status="200" description="You get detailed job data." %} 45 | ```javascript 46 | { 47 | "args": ["\/tmp\/QF3UCnKdMr\/myfile.zip"], 48 | "attempts": 1, 49 | "children": [], 50 | "created": "1601145004", 51 | "delayed": "1601145004", 52 | "expires": null, 53 | "finished": "1601145004", 54 | "id": 7, 55 | "lax": 0, 56 | "notes": {}, 57 | "parents": [], 58 | "priority": 0, 59 | "queue": "default", 60 | "result": { 61 | "id": "75d18ce470dc99f83dc355bdad66319d1f33c82b", 62 | "message": "This file already exists in the Library.", 63 | "success": 0 64 | }, 65 | "retried": null, 66 | "retries": 0, 67 | "started": "1601145004", 68 | "state": "finished", 69 | "task": "handle_upload", 70 | "time": "1601145005", 71 | "worker": 1 72 | } 73 | ``` 74 | {% endswagger-response %} 75 | {% endswagger %} 76 | 77 | 78 | -------------------------------------------------------------------------------- /tools/Documentation/api-documentation/shinobu-api.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Control the built-in Background Worker. 3 | --- 4 | 5 | # Shinobu API 6 | 7 | {% api-method method="get" host="http://lrr.tvc-16.science" path="/api/shinobu" %} 8 | {% api-method-summary %} 9 | 🔑Get Shinobu Status 10 | {% endapi-method-summary %} 11 | 12 | {% api-method-description %} 13 | Get the current status of the Worker. 14 | {% endapi-method-description %} 15 | 16 | {% api-method-spec %} 17 | {% api-method-request %} 18 | {% endapi-method-request %} 19 | {% api-method-response %} 20 | {% api-method-response-example httpCode=200 %} 21 | {% api-method-response-example-description %} 22 | Get the Shinobu process PID, and whether or not it's alive. 23 | {% endapi-method-response-example-description %} 24 | 25 | ```javascript 26 | { 27 | "success": 1, 28 | "is_alive": 1, 29 | "operation": "shinobu_status", 30 | "pid": 1608 31 | } 32 | ``` 33 | {% endapi-method-response-example %} 34 | {% endapi-method-response %} 35 | {% endapi-method-spec %} 36 | {% endapi-method %} 37 | 38 | {% api-method method="post" host="http://lrr.tvc-16.science" path="/api/shinobu/stop" %} 39 | {% api-method-summary %} 40 | 🔑Stop Shinobu 41 | {% endapi-method-summary %} 42 | 43 | {% api-method-description %} 44 | Stop the Worker. 45 | {% endapi-method-description %} 46 | 47 | {% api-method-spec %} 48 | {% api-method-request %} 49 | {% endapi-method-request %} 50 | {% api-method-response %} 51 | {% api-method-response-example httpCode=200 %} 52 | {% api-method-response-example-description %} 53 | Worker is killed. 54 | {% endapi-method-response-example-description %} 55 | 56 | ```javascript 57 | { 58 | "operation": "shinobu_stop", 59 | "success": 1 60 | } 61 | ``` 62 | {% endapi-method-response-example %} 63 | {% endapi-method-response %} 64 | {% endapi-method-spec %} 65 | {% endapi-method %} 66 | 67 | {% api-method method="post" host="http://lrr.tvc-16.science" path="/api/shinobu/restart" %} 68 | {% api-method-summary %} 69 | 🔑Restart Shinobu 70 | {% endapi-method-summary %} 71 | 72 | {% api-method-description %} 73 | \(Re\)-start the Worker. 74 | {% endapi-method-description %} 75 | 76 | {% api-method-spec %} 77 | {% api-method-request %} 78 | {% endapi-method-request %} 79 | {% api-method-response %} 80 | {% api-method-response-example httpCode=200 %} 81 | {% api-method-response-example-description %} 82 | Worker is started with a new PID. 83 | {% endapi-method-response-example-description %} 84 | 85 | ```javascript 86 | { 87 | "new_pid": 1727, 88 | "operation": "shinobu_restart", 89 | "success": 1 90 | } 91 | ``` 92 | {% endapi-method-response-example %} 93 | {% endapi-method-response %} 94 | {% endapi-method-spec %} 95 | {% endapi-method %} 96 | 97 | -------------------------------------------------------------------------------- /tools/Documentation/basic-operations/archives.md: -------------------------------------------------------------------------------- 1 | # 📚 Reading Archives 2 | 3 | ## Using the Archive Index 4 | 5 | The main page will show all the Archives loaded into the application, sorted by name. 6 | 7 | ![Archive Index in Thumbnail mode]() 8 | 9 | You can customize display options to show the index in compact mode, as well as to select which namespace to use in the two extra columns. 10 | 11 | ![Customized compact mode to sort by ratings](../.screenshots/ratings.png) 12 | 13 | The topmost **carousel** view will show random archives from your current search in both thumbnail and compact modes -- It can also be configured to show New or Untagged archives instead. 14 | 15 | ![Index page of a regular LRR install in compact mode]() 16 | 17 | {% hint style="info" %} 18 | Don't forget you can **right-click** archives to show a context menu, allowing you to edit/download/delete them, or to add them to a category. 19 | {% endhint %} 20 | 21 | When reading an archive, it is automatically extracted to a temporary folder. 22 | This folder is then simply loaded into the built-in Web Reader. 23 | The temporary folder will clean up on its own when it reaches a large enough size, so there's no need to worry about your disk filling up over time. 24 | 25 | ## Web Reader Options 26 | 27 | In the reader, you can use the keyboard arrows or the built-in arrow icons to move from page to page. 28 | You can also simply click the right or left side of the image. 29 | When reading an archive, the three button icons on the rightside of the page offer various options. 30 | 31 | ![Reader View](../.screenshots/reader.jpg) 32 | 33 | You can click the information icon on the right-side of the Reader to get a quick refresher about its controls. 34 | The Reader Options button shows the various options you can toggle to change the reading experience. (Double page, Japanese read order, etc.) 35 | 36 | The Page Overlay button (also actionable by pressing **CTRL**) will show all the pages of the current archive, allowing for quick navigation and preview. 37 | 38 | ![Reader with overlay](https://raw.githubusercontent.com/Difegue/LANraragi/dev/tools/\_screenshots/reader\_overlay.jpg) 39 | 40 | Starting with 0.7.8, you can also quickly check the metadata here. 41 | 42 | The Reader comes with its own set of clientside options to customize the reading experience as you want. 43 | 44 | ![Reader Options](../.screenshots/reader\_options.png) 45 | 46 | If "Progression Tracking" is enabled, when you close and reopen the archive, it'll show you the page you last stopped at. 47 | 48 | ## Non-Web Reader (Third-Party applications) 49 | 50 | If you want to use a third-party application to read your archives, you can use the software listed in the following page: 51 | 52 | {% content-ref url="../advanced-usage/external-readers.md" %} 53 | [external-readers.md](../advanced-usage/external-readers.md) 54 | {% endcontent-ref %} 55 | -------------------------------------------------------------------------------- /tools/Documentation/basic-operations/metadata.md: -------------------------------------------------------------------------------- 1 | # ✒ Adding Metadata 2 | 3 | ## Editing an Archive manually 4 | 5 | To use a plugin on a single archive, you need to access its **editing** page by clicking the pencil icon on the main view. From here, you can modify all metadata manually. Don't forget to save! 6 | 7 | ![Editing an Archive's Metadata](../.screenshots/edit.PNG) 8 | 9 | {% hint style="danger" %} 10 | The "Delete Archive" button will permanently wipe the Archive from your filesystem! 11 | {% endhint %} 12 | 13 | ## Using Plugins 14 | 15 | LANraragi supports the use of **Plugins** to fetch tags for your archives 16 | Said Plugins can be used in three different ways: 17 | 18 | * On a per-archive basis through the standard Edit dialog 19 | * Automatically on every newly added archive. 20 | * During a Batch Tagging session. 21 | 22 | For more info on Batch Tagging, check the following article: 23 | 24 | {% content-ref url="../advanced-usage/batch-tagging.md" %} 25 | [batch-tagging.md](../advanced-usage/batch-tagging.md) 26 | {% endcontent-ref %} 27 | 28 | To use plugins automatically, you have to first use the **Plugin Configuration** page to choose which plugins will be automatically executed, and set their options if they need any. 29 | 30 | ![Plugin Configuration (on this screenshot, eze and regex parsing will be executed automatically.)](https://raw.githubusercontent.com/Difegue/LANraragi/dev/tools/\_screenshots/cfg\_plugin.png) 31 | 32 | LRR ships with a few plugins out of the box, in the _/lib/LANraragi/Plugins_ folder. 33 | To install other Plugins (in .pm format), drag them to this folder and they'll appear in Plugin Configuration. 34 | 35 | You can also install Plugins through the "Install Plugin" button in Plugin Configuration. 36 | This feature requires Debug Mode to be enabled for security purposes. Debug Mode can be disabled once you're done installing Plugins. 37 | 38 | {% hint style="warning" %} 39 | Plugins have as much control over your system as the main LANraragi application does! 40 | 41 | When installing Plugins from unknown sources, do a little research first. 42 | {% endhint %} 43 | 44 | ## About source: tags 45 | 46 | If your archive has a `source:` tag (likely from the use of the [built-in downloading feature](../advanced-usage/downloading.md)), many plugins will use said tag to directly fetch metadata from it without having to use heuristics of any kind to guess what your archive is. 47 | 48 | If you have the URL on hand directly, you can either add it as a `source:` tag to your archive, or use it as a one-shot parameter on most downloader plugins. -------------------------------------------------------------------------------- /tools/Documentation/basic-operations/searching.md: -------------------------------------------------------------------------------- 1 | # 🔎 Searching the Archive Index 2 | 3 | The search bar in LANraragi tries to not be too dumb and will actively suggest tags to you as you type. 4 | 5 | ![Search suggestions](../.screenshots/search.png) 6 | 7 | You can also use the following special characters in a search: 8 | 9 | **Quotation Marks ("...")**\ 10 | Exact string search. Allows a search term to include spaces. Everything placed inside a pair of quotation marks is treated as a singular term. Wildcard characters are still interpreted as wildcards. 11 | 12 | **Question Mark (?), Underscore (\_)**\ 13 | Wildcard. Can match any single character. 14 | 15 | **Asterisk (\*), Percentage Sign (%)**\ 16 | Wildcard. Can match any sequence of characters (including none). 17 | 18 | **Subtraction Sign (-)**\ 19 | Exclusion. When placed before a term, prevents search results from including that term. 20 | 21 | **Dollar Sign ($)**\ 22 | Add at the end of a tag to perform an exact tag search rather than displaying all elements that start with the term. Only matches tags regardless of search parameters and can be used as an exclusion to ignore misc tags in the search query. 23 | -------------------------------------------------------------------------------- /tools/Documentation/basic-operations/stats.md: -------------------------------------------------------------------------------- 1 | # 📈 Statistics and Logs 2 | 3 | ## Statistics 4 | 5 | This page shows basic stats about your content folder, as well as your most used tags. 6 | 7 | ![](../.screenshots/cloud.PNG) 8 | 9 | ## Logs 10 | 11 | This page allows you to quickly see logs from the app, in case something went wrong. 12 | If you enable _Debug Mode_ in Configuration, more logs will be displayed. 13 | 14 | {% hint style="warning" %} 15 | If you enable Debug Mode for troubleshooting purposes, make sure to disable it once you're done! 16 | {% endhint %} 17 | -------------------------------------------------------------------------------- /tools/Documentation/basic-operations/themes.md: -------------------------------------------------------------------------------- 1 | # 🖌 Themes 2 | 3 | The front-end interface of LANraragi is customizable out of the box through CSS. 4 | A few themes are built-in already. Theme Preference is saved server-wide and will be shown to all users. 5 | 6 | ![Theme Selector](<../.screenshots/themes.png>) 7 | 8 | Changing Themes can be done alongside all the other app settings. 9 | You can write your own themes by modifying the existing ones - Dropping them in the _/public/themes_ folder will make them appear in the selection. 10 | 11 | {% hint style="warning" %} 12 | For users who don't have access to the app folder and want to make custom themes, your only option currently is to use a custom CSS browser extension. 13 | Docker users can try binding a folder on their machine to the _/home/koyomi/lanraragi/public/themes_ folder. 14 | {% endhint %} 15 | -------------------------------------------------------------------------------- /tools/Documentation/extending-lanraragi/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: I'd like to interject for a moment 3 | --- 4 | 5 | # 🏗 Setup a Development Environment 6 | 7 | ## Quick rundown 8 | 9 | LRR is written in Perl on the server-side with the help of the [Mojolicious](http://mojolicious.org) framework, with basic JQuery on the clientside. 10 | **npm** is used for JavaScript dependency management and basic shortcuts, while **cpanm** is used for Perl dependency management. 11 | 12 | As of v.0.5.5, a basic Client API is available for you to write clients that can connect to a LANraragi instance. 13 | 14 | ## Quick setup 15 | 16 | Once you've got a running LANraragi instance, you can basically dive right into the files to modify stuff to your needs. As you need raw access to the files, a native OS install is needed!\ 17 | I recommend a Linux or WSL install, as the _morbo_ development server only works on Linux. 18 | 19 | Said development server can be ran with the `npm run dev-server` command. 20 | The major difference is that this server will automatically reload when you modify any file within LANraragi. Background worker included! 21 | 22 | You'll also probably want to enable **Debug Mode** in the LRR Options, as that will allow you to view debug-tier logs, alongside the raw Mojolicious logs. 23 | -------------------------------------------------------------------------------- /tools/Documentation/installing-lanraragi/community.md: -------------------------------------------------------------------------------- 1 | # 🐧 Community (Linux) 2 | 3 | ## UnRAID 4 | 5 | An UnRAID package based on the Docker images is available [here.](https://github.com/naipilk/LANraragi-unraid-template/) 6 | 7 | ## Arch Linux 8 | 9 | An installation package is provided [in the AUR](https://aur.archlinux.org/packages/lanraragi/) (Arch User Repository). 10 | 11 | Using the AUR package the installation process in Arch Linux should be as easy as entering `pikaur -S lanraragi` in the command line (if using pikaur). Using other AUR managers should be just as easy. 12 | 13 | To install lanraragi without an AUR package manager the installation process would be something like: 14 | 15 | ``` 16 | wget https://aur.archlinux.org/cgit/aur.git/snapshot/lanraragi.tar.gz -O - | tar -xz 17 | cd lanraragi 18 | makepkg -rsi 19 | ``` 20 | 21 | That would take care of installing LRR together with build and normal dependencies and deleting build dependencies after successful building and installing. 22 | 23 | The installer also creates a lanraragi.service unit file for starting, restarting and stopping LRR with systemd's `systemctl`. If Redis is down it will get it up. 24 | 25 | `systemctl start lanraragi.service` `systemctl restart lanraragi.service` `systemctl stop lanraragi.service` `systemctl status lanraragi.service` 26 | 27 | Systemd integration also gives an easy way to read the log (if necessary). 28 | 29 | `journalctl -u lanraragi -f` 30 | -------------------------------------------------------------------------------- /tools/Documentation/installing-lanraragi/macos.md: -------------------------------------------------------------------------------- 1 | # 🍎 Homebrew (macOS/Linux) 2 | 3 | ## Migration 4 | 5 | To use all your existing files within a brewed LRR, you can issue the following commands: 6 | 7 | ```bash 8 | lrr="${HOME}/Library/Application Support/LANraragi/" 9 | # if you’re on Linux, use the next line instead: 10 | #lrr="${HOME}/LANraragi/" 11 | cd 12 | mkdir -p "${lrr}" 13 | mv content "${lrr}/content" 14 | mv log "${lrr}/log" 15 | mv public/temp "${lrr}/temp" 16 | mv database.rdb "${lrr}/database/database.rdb" 17 | ``` 18 | 19 | {% hint style="info" %} 20 | This simply moves all your files to the default location where LRR looks for them when installed with Homebrew. You can do that manually too, if you chose so. 21 | {% endhint %} 22 | 23 | If you succeeded in moving, you can proceed to the next step! 24 | 25 | ## Installation 26 | 27 | If you do not have Homebrew installed yet, simply use the command on [their page](https://brew.sh). 28 | 29 | The next step is to then install LRR. 30 | 31 | ``` 32 | brew install lanraragi 33 | ``` 34 | 35 | ## Configuration 36 | 37 | Your content folder is stored by default in `${HOME}/Library/Application Support/LANraragi`. (`${HOME}/LANraragi/content` on Linux.) 38 | The Redis database is stored in `${HOME}/Library/Application Support/LANraragi/database`. (`${HOME}/LANraragi/database` on Linux.) 39 | While the in-app settings page won't allow you to change the location of the content folder, you can do so by overriding the `LRR_DATA_DIRECTORY` environment variable before launching. 40 | 41 | ## Usage 42 | 43 | Once installed, you can get started by running `lanraragi` and opening [http://localhost:3000](http://localhost:3000). 44 | 45 | ![brew](<../.screenshots/brew.jpg>) 46 | 47 | To change the default port or add SSL support, see this page: 48 | 49 | {% content-ref url="../advanced-usage/network-interfaces.md" %} 50 | [network-interfaces.md](../advanced-usage/network-interfaces.md) 51 | {% endcontent-ref %} 52 | 53 | {% hint style="info" %} 54 | By default, LRR listens on all IPv4 Interfaces on port 3000, unsecured HTTP. 55 | {% endhint %} 56 | 57 | ## Updating 58 | 59 | Simply run `brew install lanraragi --HEAD` again to update to the latest version. 60 | 61 | {% hint style="warning" %} 62 | The same warning as in the Installation step applies. 63 | {% endhint %} 64 | 65 | ## Uninstallation 66 | 67 | Run `brew remove lanraragi` to uninstall the app. 68 | Data in the `${HOME}/Library/Application Support/LANraragi`/`${HOME}/LANraragi/` folder is not deleted. 69 | -------------------------------------------------------------------------------- /tools/Documentation/installing-lanraragi/methods.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: This is a by-OS breakdown of how you can install the software on your machine. 3 | --- 4 | 5 | # ❓ Which installation method is best for me? 6 | 7 | As LRR is a server app first and foremost, its setup is a bit more complex than your usual Desktop application. 8 | However, a lot of work as been done behind the scenes to make it easy! 9 | 10 | Look at the methods below for something that fits your OS and usage. 11 | 12 | ## Linux/macOS: _Homebrew_ 13 | 14 | [Homebrew](https://brew.sh) allows you to quickly setup LRR on macOS and Linux without relying on containers or modifying your preinstalled system libaries. 15 | 16 | ![brew](<../.screenshots/brew.jpg>) 17 | 18 | {% content-ref url="macos.md" %} 19 | [macos.md](macos.md) 20 | {% endcontent-ref %} 21 | 22 | ## Windows 10/11: _LRR for Windows_ 23 | 24 | {% hint style="warning" %} 25 | This method works on **64-bit** editions of Windows 10 only. 26 | {% endhint %} 27 | 28 | ![win10](../.screenshots/karen.jpg) 29 | 30 | I provide a dedicated installer for Windows machines as of 0.6.0, complete with a GUI and autostart. 31 | 32 | {% content-ref url="windows.md" %} 33 | [windows.md](windows.md) 34 | {% endcontent-ref %} 35 | 36 | ## Linux/macOS/Windows 10: _Docker_ 37 | 38 | Taking a page from sysadmin books, you can easily install LRR as a **container** with Docker. 39 | They're lightweight, easy to update, and automatically built/tested. I recommend this for NAS setups! 40 | 41 | {% content-ref url="docker.md" %} 42 | [docker.md](docker.md) 43 | {% endcontent-ref %} 44 | 45 | ## Linux/macOS: _Installing from Source_ 46 | 47 | Installing from **source** is a more involved procedure, but it does put you in full control and able to hack up the app's files as you wish. 48 | 49 | {% content-ref url="source.md" %} 50 | [source.md](source.md) 51 | {% endcontent-ref %} 52 | 53 | ## Linux/Community: _Community provided install packages_ 54 | 55 | Ready-to-install packages provided by voluntary maintainers or by a linux distribution itself. 56 | 57 | {% content-ref url="community.md" %} 58 | [community.md](community.md) 59 | {% endcontent-ref %} 60 | 61 | ## FreeBSD/Jail 62 | 63 | Similar to installing from source with an altered process for FreeBSD compatability. 64 | 65 | {% content-ref url="jail.md" %} 66 | [jail.md](jail.md) 67 | {% endcontent-ref %} 68 | 69 | ## Windows 7 or 8: don't 70 | 71 | ![I really hope you guys don't do this](../.screenshots/shiggy.png) 72 | 73 | Switch to 10 or Linux. 74 | -------------------------------------------------------------------------------- /tools/Documentation/installing-lanraragi/source.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: The following instructions are based on Debian Stretch. 3 | --- 4 | 5 | # 🛠 Source Code (Linux/macOS) 6 | 7 | ## A small FYI about Vendor Perl 8 | 9 | As you might have noticed, LANraragi entirely depends on the Perl programming language. 10 | A version of Perl ships already compiled on most Linux distributions(and macOS). It's usually called "Vendor Perl". 11 | 12 | Using vendor Perl is [generally discouraged](http://www.modernperlbooks.com/mt/2012/01/avoiding-the-vendor-perl-fad-diet.html) due to possible fuck-ups by the Linux distribution creator. 13 | As such, you might want to install LANraragi with your own compiled Perl, using a tool such as [Perlbrew](https://perlbrew.pl). 14 | 15 | For information, my personal tests are done using Debian's vendor Perl. 16 | 17 | ## Needed dependencies 18 | 19 | ``` 20 | apt-get update 21 | apt-get upgrade -y 22 | apt-get install build-essential make gnupg pkg-config \ 23 | cpanminus redis-server libarchive-dev imagemagick webp libssl-dev zlib1g-dev libjxl-dev \ 24 | perlmagick ghostscript npm 25 | ``` 26 | 27 | _Base software dependencies._ 28 | 29 | {% hint style="info" %} 30 | If your package manager requires you to specify which ImageMagick version to install, choose version 7. 31 | {% endhint %} 32 | 33 | {% hint style="info" %} 34 | For macOS, you should be able to install the dependencies using Homebrew. 35 | {% endhint %} 36 | 37 | ## Installing LRR 38 | 39 | All you need to do is clone the git repo somewhere (or download one of [the releases](https://github.com/Difegue/LANraragi/releases)) and run the installer. 40 | I recommend doing this with a brand new Linux user account. (I'm using "koyomi" here): 41 | 42 | ``` 43 | git clone -b master http://github.com/Difegue/LANraragi /home/koyomi/lanraragi 44 | cd /home/koyomi/lanraragi && sudo npm run lanraragi-installer install-full 45 | ``` 46 | 47 | {% hint style="info"} 48 | Do not use `sudo` in the above command if you are using `perlbrew`. 49 | Arch users might need to install `perl-config-autoconf` and use env variable `export PERL5LIB=~/perl5/lib/perl5` before running the installer. 50 | {% endhint %} 51 | 52 | Once this is done, you can get started by running `npm start` and opening [http://localhost:3000](http://localhost:3000). 53 | 54 | To change the default port or add SSL support, see this page: 55 | 56 | {% content-ref url="../advanced-usage/network-interfaces.md" %} 57 | [network-interfaces.md](../advanced-usage/network-interfaces.md) 58 | {% endcontent-ref %} 59 | 60 | {% hint style="info" %} 61 | By default, LRR listens on all IPv4 Interfaces on port 3000, unsecured HTTP. 62 | {% endhint %} 63 | 64 | ### Updating 65 | 66 | Getting all the files from the latest release and pasting them in the directory of the application should give you a painless update 95% of the time. 67 | 68 | To be on the safe side, make sure to rerun the installer once this is done: 69 | 70 | ```bash 71 | npm run lanraragi-installer install-full 72 | ``` 73 | -------------------------------------------------------------------------------- /tools/Documentation/plugin-docs/code-examples.md: -------------------------------------------------------------------------------- 1 | # Code Examples 2 | 3 | This section contains a few bits of code for things you might want to do with Plugins. 4 | 5 | ## **Write messages to the Plugin Log** 6 | 7 | ```perl 8 | # Import the LRR logging module 9 | use LANraragi::Utils::Logging qw(get_plugin_logger); 10 | # Use the logger to output status - they'll be passed to a specialized logfile and written to STDOUT. 11 | my $logger = get_plugin_logger(); 12 | 13 | $plugin->debug("This message will only show if LRR is in Debug Mode") 14 | $plugin->info("You know me the fighting freak Knuckles and we're at Pumpkin Hill"); 15 | $plugin->warn("You ready?"); 16 | $plugin->error("Oh no"); 17 | ``` 18 | 19 | The logger is a preconfigured [Mojo::Log](http://mojolicious.org/perldoc/Mojo/Log) object. 20 | 21 | ## **Make requests to a remote WebService** 22 | 23 | [Mojo::UserAgent](http://mojolicious.org/perldoc/Mojo/UserAgent) is a full-featured HTTP client coming with LRR you can use. 24 | 25 | ```perl 26 | use Mojo::UserAgent; 27 | 28 | my $ua = $lrr_info->{user_agent}; 29 | 30 | #Get HTML from a simple GET request 31 | $ua->get("http://example.com")->result->body; 32 | 33 | #Make a POST request and get the JSON result 34 | my $rep = $ua->post( 35 | "https://jsonplaceholder.typicode.com/" => json => { 36 | stage => "Meteor Herd", 37 | location => [ [ "space", "colony" ] ], 38 | emeralds => 3 39 | } 40 | )->result; 41 | 42 | #JSON decoded to a perl object 43 | my $jsonresponse = $rep->json; 44 | 45 | #JSON decoded to a string 46 | my $textrep = $rep->body; 47 | ``` 48 | 49 | ## **Read values in the LRR Database** 50 | 51 | ```perl 52 | my $redis = LANraragi::Model::Config->get_redis; 53 | 54 | my $value = $redis->get("key"); 55 | ``` 56 | 57 | This uses the excellent [Perl binding library](http://search.cpan.org/~dams/Redis-1.991/lib/Redis.pm) for Redis. 58 | 59 | ## **Extract files from the archive being examined** 60 | 61 | If you're running 0.5.2 or later: 62 | 63 | ```perl 64 | use LANraragi::Utils::Archive qw(is_file_in_archive extract_file_from_archive); 65 | 66 | # Check if info.json is in the archive located at $file and get its precise path 67 | my $info_path = is_file_in_archive($file, "info.json"); 68 | if ($info_path) { 69 | 70 | #Extract info.json 71 | my $filepath = extract_file_from_archive($file, $info_path); 72 | 73 | #Do whatever you need with the extracted file 74 | open( my $fh, '<:encoding(UTF-8)', $filepath ) 75 | or return ( error => "Could not open $filepath!" ); 76 | 77 | while ( my $row = <$fh> ) { 78 | #... 79 | } 80 | 81 | #Delete it 82 | unlink $filepath; 83 | } 84 | ``` 85 | 86 | -------------------------------------------------------------------------------- /tools/Documentation/plugin-docs/login.md: -------------------------------------------------------------------------------- 1 | # Login Plugins 2 | 3 | Login Plugins mostly play a support role: They can be called by all other plugins: Metadata, Downloader and Script Plugins. 4 | Their role is to provide a configured [Mojo::UserAgent](https://mojolicious.org/perldoc/Mojo/UserAgent) object that can be used to perform authenticated operations on a remote Web service. 5 | 6 | ## Required subroutines 7 | 8 | Only one subroutine needs to be implemented for the module to be recognized: `do_login`, which contains your working code. You're free to implement other subroutines for cleaner code, of course. 9 | 10 | ### Expected Input 11 | 12 | When executing your Plugin, LRR will call the `do_login` subroutine and give it the following variables: 13 | 14 | ```perl 15 | sub do_login { 16 | 17 | #First lines you should have in the subroutine 18 | shift; 19 | my ($param1, $param2, $param3) = @_; # Plugin parameters 20 | ``` 21 | 22 | The variables match the parameters you've entered in the `plugin_info` subroutine. 23 | 24 | ### Expected Output 25 | 26 | Your plugin must return a [Mojo::UserAgent](https://mojolicious.org/perldoc/Mojo/UserAgent) object. That's it! 27 | 28 | There's no particular error handling for Login Plugins at the moment, so I recommend you return an empty UserAgent if Login fails and handle the error in the matching Metadata/Script plugin. 29 | 30 | ## Plugin Template 31 | 32 | ```perl 33 | package LANraragi::Plugin::Login::MyNewPlugin; 34 | 35 | use strict; 36 | use warnings; 37 | 38 | use Mojo::UserAgent; 39 | use LANraragi::Utils::Logging qw(get_logger); 40 | 41 | #Meta-information about your plugin. 42 | sub plugin_info { 43 | 44 | return ( 45 | #Standard metadata 46 | name => "Login Plugin", 47 | type => "login", 48 | namespace => "dummylogin", 49 | author => "Hackerman", 50 | version => "0.001", 51 | description => "This is base boilerplate for writing LRR plugins.", 52 | #If your plugin uses/needs custom arguments, input their name here. 53 | #This name will be displayed in plugin configuration next to an input box. 54 | parameters => [ 55 | {type => "bool", desc => "Enable logging in to service X", default_value => "1"}, 56 | {type => "int", desc => "User ID"}, 57 | {type => "string", desc => "Password"} 58 | ] 59 | ); 60 | 61 | } 62 | 63 | # Mandatory function to be implemented by your login plugin 64 | # Returns a Mojo::UserAgent object only! 65 | sub do_login { 66 | 67 | # Login plugins only receive the parameters entered by the user. 68 | shift; 69 | my ( $loginenabled, $uID, $password) = @_; 70 | 71 | my $logger = get_logger( "Undernet Login", "plugins" ); 72 | my $ua = Mojo::UserAgent->new; 73 | 74 | if ($loginenabled) { 75 | 76 | $ua->cookie_jar->add( 77 | Mojo::Cookie::Response->new( 78 | name => 'userID', 79 | value => $uID, 80 | domain => 'example.com', 81 | path => '/' 82 | ) 83 | ); 84 | 85 | $ua->cookie_jar->add( 86 | Mojo::Cookie::Response->new( 87 | name => 'password', 88 | value => $password, 89 | domain => 'example.com', 90 | path => '/' 91 | ) 92 | ); 93 | 94 | } else { 95 | $logger->info( "No cookies provided, returning blank UserAgent."); 96 | } 97 | 98 | return $ua; 99 | } 100 | 101 | 1; 102 | ``` 103 | 104 | -------------------------------------------------------------------------------- /tools/_screenshots/archive_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/archive_list.png -------------------------------------------------------------------------------- /tools/_screenshots/archive_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/archive_thumb.png -------------------------------------------------------------------------------- /tools/_screenshots/cfg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/cfg.jpg -------------------------------------------------------------------------------- /tools/_screenshots/cfg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/cfg.png -------------------------------------------------------------------------------- /tools/_screenshots/cfg_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/cfg_plugin.png -------------------------------------------------------------------------------- /tools/_screenshots/reader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/reader.jpg -------------------------------------------------------------------------------- /tools/_screenshots/reader_overlay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/_screenshots/reader_overlay.jpg -------------------------------------------------------------------------------- /tools/build/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # DOCKER-VERSION 0.3.4 2 | FROM alpine:3.12 3 | LABEL git="https://github.com/uparrows/LANraragi_cn" 4 | 5 | ENV S6_OVERLAY_RELEASE v2.0.0.1 6 | ENV S6_KEEP_ENV 1 7 | 8 | # warn if we can't run stage2 (fix-attrs/cont-init) 9 | ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 1 10 | 11 | # wait 10s before KILLing 12 | ENV S6_KILL_GRACETIME 10000 13 | 14 | # s6 15 | ENTRYPOINT ["/init"] 16 | 17 | # Check application health 18 | HEALTHCHECK --interval=1m --timeout=10s --retries=3 \ 19 | CMD wget --quiet --tries=1 --no-check-certificate --spider \ 20 | http://localhost:3000 || exit 1 21 | 22 | #Default mojo server port 23 | EXPOSE 3000 24 | 25 | #Enable UTF-8 (might not do anything extra on alpine tho) 26 | ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 \ 27 | #rootless user id 28 | LRR_UID=0 LRR_GID=0 \ 29 | #Environment variables overridable by the user on container deployment 30 | LRR_NETWORK=http://*:3000 \ 31 | # extra variables 32 | EV_EXTRA_DEFS=-DEV_NO_ATFORK 33 | 34 | 35 | 36 | # we use s6-overlay-nobin to just pull in the s6-overlay arch agnostic (shell) 37 | # components, since we apk install the binaries of s6 later which are arch specific 38 | # /!\ While the s6 version here is fixed by an envvar, the apk install is not pinned and takes whatever's in alpine:latest! This certainly needs a fix. 39 | ADD https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_RELEASE}/s6-overlay-nobin.tar.gz /tmp/s6-overlay-nobin.tar.gz 40 | RUN tar -C / -xzf /tmp/s6-overlay-nobin.tar.gz && rm -f /tmp/s6-overlay-nobin.tar.gz 41 | 42 | 43 | WORKDIR /root/lanraragi 44 | 45 | #Copy cpanfile and install script before copying the entire context 46 | #This allows for Docker cache to preserve cpan dependencies 47 | COPY --chown=root:root /tools/cpanfile /tools/install.pl /tools/build/docker/install-everything.sh tools/ 48 | COPY --chown=root:root /package.json package.json 49 | 50 | # Run the install script as root 51 | RUN sh ./tools/install-everything.sh 52 | RUN rm -f /root/lanraragi/public/js/vendor/jquery.dataTables.min.js 53 | #Copy remaining LRR files from context 54 | # consider chowning in s6 setup scripts instead 55 | COPY --chown=root:root /lib lib 56 | COPY --chown=root:root /public public 57 | COPY --chown=root:root /script script 58 | COPY --chown=root:root /templates templates 59 | COPY --chown=root:root /tests tests 60 | COPY --chown=root:root /lrr.conf lrr.conf 61 | COPY --chown=root:root /tools/build/docker/redis.conf tools/build/docker/ 62 | COPY /tools/build/docker/wsl.conf /etc/wsl.conf 63 | COPY /tools/build/docker/s6/cont-init.d/ /etc/cont-init.d/ 64 | COPY /tools/build/docker/s6/services.d/ /etc/services.d/ 65 | COPY --chown=root:root /jquery.dataTables.min.js /root/lanraragi/public/js/vendor/jquery.dataTables.min.js 66 | #COPY /tools/build/docker/s6/fix-attrs.d/ /etc/fix-attrs.d/ 67 | 68 | # Persistent volumes 69 | VOLUME [ "/root/lanraragi/content" ] 70 | VOLUME [ "/root/lanraragi/database"] 71 | -------------------------------------------------------------------------------- /tools/build/docker/Dockerfile-legacy: -------------------------------------------------------------------------------- 1 | # DOCKER-VERSION 0.3.4 2 | FROM alpine:3.12.12 3 | LABEL git="https://github.com/Difegue/LANraragi" 4 | 5 | ENV S6_KEEP_ENV 1 6 | 7 | # warn if we can't run stage2 (fix-attrs/cont-init) 8 | ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 1 9 | 10 | # wait 10s before KILLing 11 | ENV S6_KILL_GRACETIME 10000 12 | 13 | # s6 14 | ENTRYPOINT ["/init"] 15 | 16 | # Check application health 17 | HEALTHCHECK --interval=1m --timeout=10s --retries=3 \ 18 | CMD wget --quiet --tries=1 --no-check-certificate --spider \ 19 | http://localhost:3000 || exit 1 20 | 21 | #Default mojo server port 22 | EXPOSE 3000 23 | 24 | # Enable UTF-8 (might not do anything extra on alpine tho) 25 | ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 \ 26 | # rootless user id 27 | LRR_UID=9001 LRR_GID=9001 \ 28 | # Environment variables overridable by the user on container deployment 29 | LRR_NETWORK=http://*:3000 \ 30 | # extra variables 31 | EV_EXTRA_DEFS=-DEV_NO_ATFORK \ 32 | # Enable automatic http proxy detection for mojo 33 | MOJO_PROXY=1 \ 34 | # Allow Mojo to automatically pick up the X-Forwarded-For and X-Forwarded-Proto headers 35 | MOJO_REVERSE_PROXY=1 36 | 37 | RUN \ 38 | if [ $(getent group ${LRR_GID}) ]; then \ 39 | adduser -D -u ${LRR_UID} koyomi; \ 40 | else \ 41 | addgroup -g ${LRR_GID} koyomi && \ 42 | adduser -D -u ${LRR_UID} -G koyomi koyomi; \ 43 | fi 44 | 45 | # we use s6-overlay-nobin to just pull in the s6-overlay arch agnostic (shell) 46 | # components, since we apk install the binaries of s6 later which are arch specific 47 | ADD https://github.com/just-containers/s6-overlay/releases/download/v2.0.0.1/s6-overlay-nobin.tar.gz /tmp/s6-overlay-nobin.tar.gz 48 | RUN tar -C / -xzf /tmp/s6-overlay-nobin.tar.gz && rm -f /tmp/s6-overlay-nobin.tar.gz 49 | 50 | WORKDIR /home/koyomi/lanraragi 51 | 52 | #Copy cpanfile and install script before copying the entire context 53 | #This allows for Docker cache to preserve cpan dependencies 54 | COPY --chown=koyomi:koyomi /tools/cpanfile /tools/install.pl /tools/build/docker/install-everything.sh tools/ 55 | COPY --chown=koyomi:koyomi /package.json package.json 56 | 57 | # Run the install script as root 58 | RUN sh ./tools/install-everything.sh 59 | 60 | #Copy remaining LRR files from context 61 | COPY --chown=koyomi:koyomi /lib lib 62 | COPY --chown=koyomi:koyomi /public public 63 | COPY --chown=koyomi:koyomi /script script 64 | COPY --chown=koyomi:koyomi /templates templates 65 | COPY --chown=koyomi:koyomi /tests tests 66 | COPY --chown=koyomi:koyomi /lrr.conf lrr.conf 67 | COPY --chown=koyomi:koyomi /tools/build/docker/redis.conf tools/build/docker/ 68 | COPY /tools/build/docker/wsl.conf /etc/wsl.conf 69 | COPY /tools/build/docker/s6/cont-init.d/ /etc/cont-init.d/ 70 | COPY /tools/build/docker/s6/legacy-services.d/ /etc/services.d/ 71 | #COPY /tools/build/docker/s6/fix-attrs.d/ /etc/fix-attrs.d/ 72 | 73 | # Persistent volumes 74 | VOLUME [ "/home/koyomi/lanraragi/content" ] 75 | VOLUME [ "/home/koyomi/lanraragi/database"] 76 | -------------------------------------------------------------------------------- /tools/build/docker/install-everything.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #Just do everything 4 | apk update 5 | apk add tzdata 6 | apk add perl perl-io-socket-ssl perl-dev redis libarchive-dev libbz2 openssl-dev zlib-dev 7 | apk add imagemagick imagemagick-perlmagick libwebp-tools libheif 8 | apk add g++ make pkgconf gnupg wget curl file 9 | apk add shadow s6 s6-portable-utils 10 | 11 | # Check for alpine version 12 | if [ -f /etc/alpine-release ]; then 13 | alpine_version=$(cat /etc/alpine-release) 14 | if [ "$alpine_version" = "3.12.12" ]; then 15 | apk add nodejs-npm 16 | else # Those packages don't exist on 3.12 17 | apk add nodejs npm s6-overlay s6-overlay-preinit libjxl 18 | fi 19 | fi 20 | 21 | #Hey it's cpanm 22 | curl -L https://cpanmin.us | perl - App::cpanminus 23 | 24 | #Alpine's libffi build comes with AVX instructions enabled 25 | #Rebuild our own libffi with those disabled 26 | if [ $(uname -m) == 'x86_64' ]; then 27 | #Install deps only 28 | cpanm --notest --installdeps Alien::FFI 29 | curl -L -s https://cpan.metacpan.org/authors/id/P/PL/PLICEASE/Alien-FFI-0.25.tar.gz | tar -xz 30 | cd Alien-FFI-0.25 31 | #Patch build script to disable AVX 32 | sed -i 's/--disable-builddir/--disable-builddir --with-gcc-arch=x86-64-v2/' alienfile 33 | perl Makefile.PL && make install 34 | cd ../ && rm -rf Alien-FFI-0.25 35 | fi 36 | 37 | #Install the LRR dependencies proper 38 | cd tools && cpanm --notest --installdeps . -M https://cpan.metacpan.org && cd .. 39 | npm run lanraragi-installer install-full 40 | 41 | #Cleanup to lighten the image 42 | apk del perl-dev g++ make gnupg wget curl nodejs npm openssl-dev file 43 | rm -rf public/js/vendor/*.map public/css/vendor/*.map 44 | rm -rf /root/.cpanm/* /root/.npm/ /usr/local/share/man/* node_modules /var/cache/apk/* 45 | -------------------------------------------------------------------------------- /tools/build/docker/s6/cont-init.d/01-lrr-setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | 5 | #Ensure LRR folder is writable 6 | chown root /root/lanraragi 7 | chmod u+rwx /root/lanraragi 8 | 9 | #Crash with an error if content folder doesn't exist 10 | if [ ! -d "/root/lanraragi/content" ]; then 11 | echo "Content folder doesn't exist! Please ensure your Docker mappings are correct." 12 | exit 1 13 | fi 14 | 15 | #Ensure database is writable 16 | chown -R root /root/lanraragi/database 17 | chmod -R 777 /root/lanraragi/database 18 | 19 | #Ensure thumbnail folder is writable 20 | chmod -R 744 /root/lanraragi/content/thumb 21 | find /root/lanraragi/content/thumb -type f -exec chmod u+rw {} \; 22 | find /root/lanraragi/content/thumb -type d -exec chmod u+rwx {} \; 23 | 24 | #Ensure log folder is writable 25 | mkdir /root/lanraragi/log 26 | chown -R root /root/lanraragi/log 27 | chmod u+rwx /root/lanraragi/log 28 | 29 | #Ensure temp folder is writable 30 | mkdir /root/lanraragi/public/temp 31 | chown -R root /root/lanraragi/public/temp 32 | chmod u+rwx /root/lanraragi/public/temp 33 | 34 | #Remove mojo, minion and shinobu pid files 35 | rm /root/lanraragi/public/temp/server.pid 36 | rm /root/lanraragi/public/temp/shinobu.pid 37 | rm /root/lanraragi/public/temp/minion.pid 38 | 39 | # https://redis.io/topics/faq#background-saving-fails-with-a-fork-error-under-linux-even-if-i-have-a-lot-of-free-ram 40 | OVERCOMMIT=$(cat /proc/sys/vm/overcommit_memory) 41 | if [ $OVERCOMMIT -eq 0 ] 42 | then 43 | echo "WARNING: overcommit_memory is set to 0! This might lead to background saving errors if your database is too large." 44 | echo "Please check https://redis.io/topics/faq#background-saving-fails-with-a-fork-error-under-linux-even-if-i-have-a-lot-of-free-ram for details." 45 | fi 46 | -------------------------------------------------------------------------------- /tools/build/docker/s6/fix-attrs.d/01-lrr-dirs: -------------------------------------------------------------------------------- 1 | # second field in user is fallback uid 2 | # path recurse user fperm dperm 3 | /root/lanraragi false root,0:0 0644 0744 4 | /root/lanraragi/database true root,0:0 0777 0777 5 | /root/lanraragi/content/thumb true root,0:0 0644 0744 6 | /root/lanraragi/log false root,0:0 0644 0744 7 | /root/lanraragi/public/temp false root,0:0 0644 0744 8 | -------------------------------------------------------------------------------- /tools/build/docker/s6/services.d/lanraragi/finish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/execlineb -S0 2 | # (-t)erminate running services to allow container to restart 3 | s6-svscanctl -t /var/run/s6/services 4 | -------------------------------------------------------------------------------- /tools/build/docker/s6/services.d/lanraragi/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /root/lanraragi/ 3 | export HOME=/root 4 | perl /root/lanraragi/script/launcher.pl -f /root/lanraragi/script/lanraragi 5 | -------------------------------------------------------------------------------- /tools/build/docker/s6/services.d/redis/finish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/execlineb -S0 2 | # (-t)erminate running services to allow container to restart 3 | s6-svscanctl -t /var/run/s6/services 4 | -------------------------------------------------------------------------------- /tools/build/docker/s6/services.d/redis/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/redis-server /root/lanraragi/tools/build/docker/redis.conf 3 | -------------------------------------------------------------------------------- /tools/build/docker/wsl.conf: -------------------------------------------------------------------------------- 1 | # Allow launching windows processes through the distro 2 | [interop] 3 | enabled = true 4 | 5 | # Enable DNS – even though these are turned on by default, we’ll specify here just to be explicit. 6 | [network] 7 | generateHosts = true 8 | generateResolvConf = true -------------------------------------------------------------------------------- /tools/build/homebrew/lanraragi: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # prefix/place where lrr is installed 4 | workdir=$(dirname $0)/$(dirname $(readlink $0))/../libexec 5 | 6 | # default locations 7 | if [[ ${OSTYPE} == "darwin"* ]]; then 8 | _LRR_DATA_DIRECTORY="${HOME}/Library/Application Support/LANraragi/content" 9 | _LRR_THUMB_DIRECTORY="${HOME}/Library/Application Support/LANraragi/content/thumb" 10 | _LRR_DATABASE_DIRECTORY="${HOME}/Library/Application Support/LANraragi/database" 11 | _LRR_LOG_DIRECTORY="${HOME}/Library/Logs/LANraragi" 12 | _LRR_TEMP_DIRECTORY="${HOME}/Library/Application Support/LANraragi/temp" 13 | else 14 | _LRR_DATA_DIRECTORY="${HOME}/LANraragi/content" 15 | _LRR_DATABASE_DIRECTORY="${HOME}/LANraragi/database" 16 | _LRR_LOG_DIRECTORY="${HOME}/LANraragi/log" 17 | _LRR_TEMP_DIRECTORY="${HOME}/LANraragi/temp" 18 | fi 19 | 20 | # initialize if not set 21 | if [[ ${LRR_DATA_DIRECTORY} == "" ]]; then 22 | export LRR_DATA_DIRECTORY="${_LRR_DATA_DIRECTORY}" 23 | fi 24 | if [[ ${LRR_THUMB_DIRECTORY} == "" ]]; then 25 | export LRR_THUMB_DIRECTORY="${_LRR_THUMB_DIRECTORY}" 26 | fi 27 | if [[ ${LRR_DATABASE_DIRECTORY} == "" ]]; then 28 | export LRR_DATABASE_DIRECTORY="${_LRR_DATABASE_DIRECTORY}" 29 | fi 30 | if [[ ${LRR_LOG_DIRECTORY} == "" ]]; then 31 | export LRR_LOG_DIRECTORY="${_LRR_LOG_DIRECTORY}" 32 | fi 33 | if [[ ${LRR_TEMP_DIRECTORY} == "" ]]; then 34 | export LRR_TEMP_DIRECTORY="${_LRR_TEMP_DIRECTORY}" 35 | fi 36 | 37 | # output -- Directories are created by LRR's launcher.pl if they don't exist 38 | echo "Data Directory is set to '${LRR_DATA_DIRECTORY}'" 39 | echo "Thumbnail Directory is set to '${LRR_THUMB_DIRECTORY}'" 40 | echo "Database Directory is set to '${LRR_DATABASE_DIRECTORY}'" 41 | echo "Log Directory is set to '${LRR_LOG_DIRECTORY}'" 42 | echo "Temporary Folder is set to '${LRR_TEMP_DIRECTORY}'" 43 | 44 | # create dirs 45 | mkdir -p "${LRR_DATA_DIRECTORY}" 46 | mkdir -p "${LRR_THUMB_DIRECTORY}" 47 | mkdir -p "${LRR_DATABASE_DIRECTORY}" 48 | mkdir -p "${LRR_LOG_DIRECTORY}" 49 | mkdir -p "${LRR_TEMP_DIRECTORY}" 50 | 51 | # @INC export 52 | export PERL5LIB="${workdir}/lib/perl5" 53 | 54 | # launch command 55 | redis-server "${workdir}/redis.conf" --logfile "${LRR_LOG_DIRECTORY}/redis.log" --dir "${LRR_DATABASE_DIRECTORY}" 56 | npm start --prefix "${workdir}" && redis-cli shutdown 57 | -------------------------------------------------------------------------------- /tools/build/windows/build.ps1: -------------------------------------------------------------------------------- 1 | # --- LRR Windows build script --- 2 | 3 | echo "🎌 Building up LRR Windows Package 🎌" 4 | echo "Inferring version from package.json..." 5 | 6 | $json = (Get-Content "package.json" -Raw) | ConvertFrom-Json 7 | $version = $json.version 8 | echo "Version is $version" 9 | $env:LRR_VERSION_NUM=$version 10 | 11 | # Use Docker image 12 | mv .\package\package.tar .\tools\build\windows\Karen\External\package.tar 13 | 14 | # Use Karen master 15 | cd .\tools\build\windows\Karen 16 | echo (Resolve-Path .\).Path 17 | nuget restore 18 | 19 | # Build Karen and Setup 20 | msbuild /p:Configuration=Release 21 | 22 | Get-FileHash .\Setup\bin\LANraragi.msi | Format-List -------------------------------------------------------------------------------- /tools/cpanfile: -------------------------------------------------------------------------------- 1 | 2 | requires 'perl', '5.20.2'; 3 | requires 'local::lib', 2.000024; 4 | 5 | # LRR Core 6 | requires 'Redis', 1.995; 7 | requires 'Encode', 3.01; 8 | requires 'Archive::Libarchive::Extract', 0.03; 9 | requires 'Archive::Libarchive::Peek', 0.03; 10 | requires 'Digest::SHA', 6.02; 11 | 12 | # Not required by LRR itself but needs this version for Alpine support 13 | requires 'Crypt::Rijndael', 1.14; 14 | 15 | # Specifically use native DNS resolver to fix issues on WSL1+Alpine 16 | requires 'Net::DNS::Native', 0.22; 17 | 18 | # Web UI 19 | requires 'Sort::Naturally', 1.03; 20 | requires 'Authen::Passphrase', 0.008; 21 | requires 'File::ReadBackwards', 1.05; 22 | requires 'URI::Escape', 1.74; 23 | requires 'URI', 5.09; 24 | 25 | # Used by Installer 26 | requires 'IPC::Cmd', 1.02; 27 | 28 | # Logfile rotation and compression 29 | requires 'Logfile::Rotate', 1.04; 30 | requires 'Compress::Zlib', 2.087; 31 | 32 | # Test Utils 33 | requires 'Test::Harness', 3.42; 34 | requires 'Test::MockObject', 1.20200122; 35 | requires 'Test::Trap', 0.3.4; 36 | requires 'Test::Deep', 1.130; 37 | 38 | # Mojo stuff 39 | requires 'Mojolicious', 9.27; 40 | requires 'Mojolicious::Plugin::TemplateToolkit', 0.005; 41 | requires 'Mojolicious::Plugin::RenderFile', 0.12; 42 | requires 'Mojolicious::Plugin::Status', 1.15; 43 | requires 'IO::Socket::SSL', 2.067; 44 | requires 'Cpanel::JSON::XS', 4.06; 45 | 46 | # Job Queue (Minion) 47 | requires 'Minion', 10.25; 48 | requires 'Minion::Backend::Redis', 0.002; 49 | 50 | # Background Worker (Shinobu) 51 | requires 'Proc::Simple', 1.32; 52 | requires 'Parallel::Loops', 0.10; 53 | requires 'Sys::CpuAffinity', 1.12; 54 | requires 'File::ChangeNotify', 0.31; 55 | 56 | # Plugin system 57 | requires 'Module::Pluggable', 5.2; 58 | 59 | # Eze plugin 60 | requires 'Time::Local', 1.30; 61 | -------------------------------------------------------------------------------- /tools/lanraragi-systemd.service: -------------------------------------------------------------------------------- 1 | # This systemd service file is provided as an example and might not work out-of-the-box with recent versions of LRR. 2 | # It assumes you already have a standard redis installation on your Linux OS. 3 | # (Provided by jmhickman, thanks!) 4 | 5 | [Unit] 6 | Description=LANraragi web application 7 | After=network.target,redis.service 8 | 9 | [Service] 10 | Type=simple 11 | Environment=LRR_NETWORK=http://*:80 12 | WorkingDirectory=your_install_path 13 | ExecStart=/usr/bin/npm start 14 | #Restart=on-failure 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /tools/repository-open-graph-template.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/tools/repository-open-graph-template.jpg --------------------------------------------------------------------------------