Showing preview only (2,917K chars total). Download the full file or copy to clipboard to get everything.
Repository: hectorqin/reader
Branch: master
Commit: 49e6d4aefed0
Files: 375
Total size: 2.7 MB
Directory structure:
gitextract_mc5_awkk/
├── .dockerignore
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug-report--问题反馈-.md
│ └── workflows/
│ ├── Dockerfile
│ ├── Openj9-Dockerfile
│ ├── build.yml
│ ├── pull-request.yml
│ ├── release-github.yml
│ ├── release-openj9.yml
│ └── release.yml
├── .gitignore
├── Dockerfile
├── Dockerfile.source
├── LICENSE
├── README.md
├── UPDATELOG.md
├── assets/
│ └── mac/
│ └── reader.icns
├── build.gradle.kts
├── build.sh
├── cli.gradle
├── doc.md
├── docker-compose.yaml
├── docker-compose.yml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── nixpacks.toml
├── preview.md
├── reader.sh
├── server/
│ ├── bin/
│ │ ├── shutdown.cmd
│ │ ├── shutdown.sh
│ │ ├── startup.cmd
│ │ └── startup.sh
│ └── conf/
│ └── application.properties
├── settings.gradle
├── src/
│ ├── lib/
│ │ ├── rhino-1.7.13-1.jar
│ │ └── xmlpull-1.1.3.1.jar
│ ├── main/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ └── htmake/
│ │ │ │ └── reader/
│ │ │ │ ├── ReaderApplication.kt
│ │ │ │ ├── ReaderUIApplication.kt
│ │ │ │ ├── SpringEvent.java
│ │ │ │ ├── api/
│ │ │ │ │ ├── ReturnData.kt
│ │ │ │ │ ├── YueduApi.kt
│ │ │ │ │ └── controller/
│ │ │ │ │ ├── BaseController.kt
│ │ │ │ │ ├── BookController.kt
│ │ │ │ │ ├── BookSourceController.kt
│ │ │ │ │ ├── BookmarkController.kt
│ │ │ │ │ ├── ReplaceRuleController.kt
│ │ │ │ │ ├── RssSourceController.kt
│ │ │ │ │ ├── UserController.kt
│ │ │ │ │ └── WebdavController.kt
│ │ │ │ ├── config/
│ │ │ │ │ ├── AppConfig.kt
│ │ │ │ │ └── BookConfig.kt
│ │ │ │ ├── entity/
│ │ │ │ │ ├── BasicError.kt
│ │ │ │ │ ├── Size.kt
│ │ │ │ │ └── User.kt
│ │ │ │ ├── init/
│ │ │ │ │ └── appCtx.kt
│ │ │ │ ├── utils/
│ │ │ │ │ ├── Ext.kt
│ │ │ │ │ ├── SpringContextUtils.java
│ │ │ │ │ └── VertExt.kt
│ │ │ │ └── verticle/
│ │ │ │ └── RestVerticle.kt
│ │ │ ├── io/
│ │ │ │ └── legado/
│ │ │ │ └── app/
│ │ │ │ ├── README.md
│ │ │ │ ├── constant/
│ │ │ │ │ ├── Action.kt
│ │ │ │ │ ├── AppConst.kt
│ │ │ │ │ ├── AppPattern.kt
│ │ │ │ │ ├── BookType.kt
│ │ │ │ │ ├── DeepinkBookSource.kt
│ │ │ │ │ ├── PreferKey.kt
│ │ │ │ │ ├── RSSKeywords.kt
│ │ │ │ │ └── Status.kt
│ │ │ │ ├── data/
│ │ │ │ │ └── entities/
│ │ │ │ │ ├── BaseBook.kt
│ │ │ │ │ ├── BaseSource.kt
│ │ │ │ │ ├── Book.kt
│ │ │ │ │ ├── BookChapter.kt
│ │ │ │ │ ├── BookGroup.kt
│ │ │ │ │ ├── BookSource.kt
│ │ │ │ │ ├── Bookmark.kt
│ │ │ │ │ ├── Cache.kt
│ │ │ │ │ ├── Cookie.kt
│ │ │ │ │ ├── ReplaceRule.kt
│ │ │ │ │ ├── RssArticle.kt
│ │ │ │ │ ├── RssSource.kt
│ │ │ │ │ ├── SearchBook.kt
│ │ │ │ │ ├── SearchKeyword.kt
│ │ │ │ │ ├── SearchResult.kt
│ │ │ │ │ ├── TxtTocRule.kt
│ │ │ │ │ └── rule/
│ │ │ │ │ ├── BookInfoRule.kt
│ │ │ │ │ ├── BookListRule.kt
│ │ │ │ │ ├── ContentRule.kt
│ │ │ │ │ ├── ExploreRule.kt
│ │ │ │ │ ├── SearchRule.kt
│ │ │ │ │ └── TocRule.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ConcurrentException.kt
│ │ │ │ │ ├── ContentEmptyException.kt
│ │ │ │ │ ├── NoStackTraceException.kt
│ │ │ │ │ ├── RegexTimeoutException.kt
│ │ │ │ │ └── TocEmptyException.kt
│ │ │ │ ├── help/
│ │ │ │ │ ├── BookHelp.kt
│ │ │ │ │ ├── CacheManager.kt
│ │ │ │ │ ├── DefaultData.kt
│ │ │ │ │ ├── EncodingDetectHelp.java
│ │ │ │ │ ├── JsExtensions.kt
│ │ │ │ │ ├── coroutine/
│ │ │ │ │ │ ├── CompositeCoroutine.kt
│ │ │ │ │ │ ├── Coroutine.kt
│ │ │ │ │ │ └── CoroutineContainer.kt
│ │ │ │ │ └── http/
│ │ │ │ │ ├── AjaxWebView.kt
│ │ │ │ │ ├── ByteConverter.kt
│ │ │ │ │ ├── CookieStore.kt
│ │ │ │ │ ├── CoroutinesCallAdapterFactory.kt
│ │ │ │ │ ├── EncodeConverter.kt
│ │ │ │ │ ├── HttpHelper.kt
│ │ │ │ │ ├── OkHttpUtils.kt
│ │ │ │ │ ├── RequestMethod.kt
│ │ │ │ │ ├── Res.kt
│ │ │ │ │ ├── SSLHelper.kt
│ │ │ │ │ ├── StrResponse.kt
│ │ │ │ │ └── api/
│ │ │ │ │ └── CookieManager.kt
│ │ │ │ ├── lib/
│ │ │ │ │ └── icu4j/
│ │ │ │ │ ├── CharsetDetector.java
│ │ │ │ │ ├── CharsetMatch.java
│ │ │ │ │ ├── CharsetRecog_2022.java
│ │ │ │ │ ├── CharsetRecog_UTF8.java
│ │ │ │ │ ├── CharsetRecog_Unicode.java
│ │ │ │ │ ├── CharsetRecog_mbcs.java
│ │ │ │ │ ├── CharsetRecog_sbcs.java
│ │ │ │ │ └── CharsetRecognizer.java
│ │ │ │ ├── model/
│ │ │ │ │ ├── Debug.kt
│ │ │ │ │ ├── DebugLog.kt
│ │ │ │ │ ├── Debugger.kt
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── analyzeRule/
│ │ │ │ │ │ ├── AnalyzeByJSonPath.kt
│ │ │ │ │ │ ├── AnalyzeByJSoup.kt
│ │ │ │ │ │ ├── AnalyzeByRegex.kt
│ │ │ │ │ │ ├── AnalyzeByXPath.kt
│ │ │ │ │ │ ├── AnalyzeRule.kt
│ │ │ │ │ │ ├── AnalyzeUrl.kt
│ │ │ │ │ │ ├── QueryTTF.java
│ │ │ │ │ │ ├── RuleAnalyzer.kt
│ │ │ │ │ │ ├── RuleData.kt
│ │ │ │ │ │ └── RuleDataInterface.kt
│ │ │ │ │ ├── localBook/
│ │ │ │ │ │ ├── CbzFile.kt
│ │ │ │ │ │ ├── EpubFile.kt
│ │ │ │ │ │ ├── LocalBook.kt
│ │ │ │ │ │ ├── TextFile.kt
│ │ │ │ │ │ └── UmdFile.kt
│ │ │ │ │ ├── rss/
│ │ │ │ │ │ ├── Rss.kt
│ │ │ │ │ │ ├── RssParserByRule.kt
│ │ │ │ │ │ └── RssParserDefault.kt
│ │ │ │ │ └── webBook/
│ │ │ │ │ ├── BookChapterList.kt
│ │ │ │ │ ├── BookContent.kt
│ │ │ │ │ ├── BookInfo.kt
│ │ │ │ │ ├── BookList.kt
│ │ │ │ │ └── WebBook.kt
│ │ │ │ └── utils/
│ │ │ │ ├── ACache.kt
│ │ │ │ ├── AnkoHelps.kt
│ │ │ │ ├── Base64.java
│ │ │ │ ├── EncoderUtils.kt
│ │ │ │ ├── EncodingDetect.kt
│ │ │ │ ├── FileExtensions.kt
│ │ │ │ ├── FilesUtil.kt
│ │ │ │ ├── GsonExtensions.kt
│ │ │ │ ├── HtmlFormatter.kt
│ │ │ │ ├── JsonExtensions.kt
│ │ │ │ ├── JsoupExtensions.kt
│ │ │ │ ├── LogUtils.kt
│ │ │ │ ├── MD5Utils.kt
│ │ │ │ ├── NetworkUtils.kt
│ │ │ │ ├── SourceAnalyzer.kt
│ │ │ │ ├── StringExtensions.kt
│ │ │ │ ├── StringUtils.kt
│ │ │ │ ├── TextUtils.java
│ │ │ │ ├── ThrowableExtensions.kt
│ │ │ │ ├── UTF8BOMFighter.kt
│ │ │ │ ├── Utf8BomUtils.kt
│ │ │ │ └── ZipUtils.kt
│ │ │ ├── me/
│ │ │ │ └── ag2s/
│ │ │ │ ├── epublib/
│ │ │ │ │ ├── Constants.java
│ │ │ │ │ ├── browsersupport/
│ │ │ │ │ │ ├── NavigationEvent.java
│ │ │ │ │ │ ├── NavigationEventListener.java
│ │ │ │ │ │ ├── NavigationHistory.java
│ │ │ │ │ │ ├── Navigator.java
│ │ │ │ │ │ └── package-info.java
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ ├── Author.java
│ │ │ │ │ │ ├── Date.java
│ │ │ │ │ │ ├── EpubBook.java
│ │ │ │ │ │ ├── EpubResourceProvider.java
│ │ │ │ │ │ ├── FileResourceProvider.java
│ │ │ │ │ │ ├── Guide.java
│ │ │ │ │ │ ├── GuideReference.java
│ │ │ │ │ │ ├── Identifier.java
│ │ │ │ │ │ ├── LazyResource.java
│ │ │ │ │ │ ├── LazyResourceProvider.java
│ │ │ │ │ │ ├── ManifestItemProperties.java
│ │ │ │ │ │ ├── ManifestItemRefProperties.java
│ │ │ │ │ │ ├── ManifestProperties.java
│ │ │ │ │ │ ├── MediaType.java
│ │ │ │ │ │ ├── MediaTypes.java
│ │ │ │ │ │ ├── Metadata.java
│ │ │ │ │ │ ├── Relator.java
│ │ │ │ │ │ ├── Resource.java
│ │ │ │ │ │ ├── ResourceInputStream.java
│ │ │ │ │ │ ├── ResourceReference.java
│ │ │ │ │ │ ├── Resources.java
│ │ │ │ │ │ ├── Spine.java
│ │ │ │ │ │ ├── SpineReference.java
│ │ │ │ │ │ ├── TOCReference.java
│ │ │ │ │ │ ├── TableOfContents.java
│ │ │ │ │ │ └── TitledResourceReference.java
│ │ │ │ │ ├── epub/
│ │ │ │ │ │ ├── BookProcessor.java
│ │ │ │ │ │ ├── BookProcessorPipeline.java
│ │ │ │ │ │ ├── DOMUtil.java
│ │ │ │ │ │ ├── EpubProcessorSupport.java
│ │ │ │ │ │ ├── EpubReader.java
│ │ │ │ │ │ ├── EpubWriter.java
│ │ │ │ │ │ ├── HtmlProcessor.java
│ │ │ │ │ │ ├── NCXDocumentV2.java
│ │ │ │ │ │ ├── NCXDocumentV3.java
│ │ │ │ │ │ ├── PackageDocumentBase.java
│ │ │ │ │ │ ├── PackageDocumentMetadataReader.java
│ │ │ │ │ │ ├── PackageDocumentMetadataWriter.java
│ │ │ │ │ │ ├── PackageDocumentReader.java
│ │ │ │ │ │ ├── PackageDocumentWriter.java
│ │ │ │ │ │ └── ResourcesLoader.java
│ │ │ │ │ └── util/
│ │ │ │ │ ├── CollectionUtil.java
│ │ │ │ │ ├── IOUtil.java
│ │ │ │ │ ├── NoCloseOutputStream.java
│ │ │ │ │ ├── NoCloseWriter.java
│ │ │ │ │ ├── ResourceUtil.java
│ │ │ │ │ ├── StringUtil.java
│ │ │ │ │ └── commons/
│ │ │ │ │ └── io/
│ │ │ │ │ ├── BOMInputStream.java
│ │ │ │ │ ├── ByteOrderMark.java
│ │ │ │ │ ├── IOConsumer.java
│ │ │ │ │ ├── ProxyInputStream.java
│ │ │ │ │ ├── XmlStreamReader.java
│ │ │ │ │ └── XmlStreamReaderException.java
│ │ │ │ └── umdlib/
│ │ │ │ ├── domain/
│ │ │ │ │ ├── UmdBook.java
│ │ │ │ │ ├── UmdChapters.java
│ │ │ │ │ ├── UmdCover.java
│ │ │ │ │ ├── UmdEnd.java
│ │ │ │ │ └── UmdHeader.java
│ │ │ │ ├── tool/
│ │ │ │ │ ├── StreamReader.java
│ │ │ │ │ ├── UmdUtils.java
│ │ │ │ │ └── WrapOutputStream.java
│ │ │ │ └── umd/
│ │ │ │ └── UmdReader.java
│ │ │ └── org/
│ │ │ └── kxml2/
│ │ │ ├── io/
│ │ │ │ ├── KXmlParser.java
│ │ │ │ └── KXmlSerializer.java
│ │ │ ├── kdom/
│ │ │ │ ├── Document.java
│ │ │ │ ├── Element.java
│ │ │ │ └── Node.java
│ │ │ └── wap/
│ │ │ ├── Wbxml.java
│ │ │ ├── WbxmlParser.java
│ │ │ ├── WbxmlSerializer.java
│ │ │ ├── syncml/
│ │ │ │ └── SyncML.java
│ │ │ ├── wml/
│ │ │ │ └── Wml.java
│ │ │ └── wv/
│ │ │ └── WV.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── services/
│ │ │ └── org.xmlpull.v1.XmlPullParserFactory
│ │ ├── application-prod.yml
│ │ ├── application.yml
│ │ ├── banner.txt
│ │ ├── defaultData/
│ │ │ └── txtTocRule.json
│ │ ├── dtd/
│ │ │ ├── openebook.org/
│ │ │ │ └── dtds/
│ │ │ │ └── oeb-1.2/
│ │ │ │ ├── oeb12.ent
│ │ │ │ └── oebpkg12.dtd
│ │ │ ├── www.daisy.org/
│ │ │ │ └── z3986/
│ │ │ │ └── 2005/
│ │ │ │ └── ncx-2005-1.dtd
│ │ │ └── www.w3.org/
│ │ │ └── TR/
│ │ │ ├── ruby/
│ │ │ │ └── xhtml-ruby-1.mod
│ │ │ ├── xhtml-modularization/
│ │ │ │ └── DTD/
│ │ │ │ ├── xhtml-arch-1.mod
│ │ │ │ ├── xhtml-attribs-1.mod
│ │ │ │ ├── xhtml-base-1.mod
│ │ │ │ ├── xhtml-bdo-1.mod
│ │ │ │ ├── xhtml-blkphras-1.mod
│ │ │ │ ├── xhtml-blkpres-1.mod
│ │ │ │ ├── xhtml-blkstruct-1.mod
│ │ │ │ ├── xhtml-charent-1.mod
│ │ │ │ ├── xhtml-csismap-1.mod
│ │ │ │ ├── xhtml-datatypes-1.mod
│ │ │ │ ├── xhtml-datatypes-1.mod.1
│ │ │ │ ├── xhtml-edit-1.mod
│ │ │ │ ├── xhtml-events-1.mod
│ │ │ │ ├── xhtml-form-1.mod
│ │ │ │ ├── xhtml-framework-1.mod
│ │ │ │ ├── xhtml-hypertext-1.mod
│ │ │ │ ├── xhtml-image-1.mod
│ │ │ │ ├── xhtml-inlphras-1.mod
│ │ │ │ ├── xhtml-inlpres-1.mod
│ │ │ │ ├── xhtml-inlstruct-1.mod
│ │ │ │ ├── xhtml-inlstyle-1.mod
│ │ │ │ ├── xhtml-lat1.ent
│ │ │ │ ├── xhtml-link-1.mod
│ │ │ │ ├── xhtml-list-1.mod
│ │ │ │ ├── xhtml-meta-1.mod
│ │ │ │ ├── xhtml-notations-1.mod
│ │ │ │ ├── xhtml-object-1.mod
│ │ │ │ ├── xhtml-param-1.mod
│ │ │ │ ├── xhtml-pres-1.mod
│ │ │ │ ├── xhtml-qname-1.mod
│ │ │ │ ├── xhtml-script-1.mod
│ │ │ │ ├── xhtml-special.ent
│ │ │ │ ├── xhtml-ssismap-1.mod
│ │ │ │ ├── xhtml-struct-1.mod
│ │ │ │ ├── xhtml-style-1.mod
│ │ │ │ ├── xhtml-symbol.ent
│ │ │ │ ├── xhtml-symbol.ent.1
│ │ │ │ ├── xhtml-table-1.mod
│ │ │ │ ├── xhtml-text-1.mod
│ │ │ │ └── xhtml11-model-1.mod
│ │ │ ├── xhtml1/
│ │ │ │ └── DTD/
│ │ │ │ ├── xhtml-lat1.ent
│ │ │ │ ├── xhtml-special.ent
│ │ │ │ ├── xhtml-symbol.ent
│ │ │ │ ├── xhtml1-strict.dtd
│ │ │ │ └── xhtml1-transitional.dtd
│ │ │ └── xhtml11/
│ │ │ └── DTD/
│ │ │ └── xhtml11.dtd
│ │ ├── epub/
│ │ │ ├── chapter.html
│ │ │ ├── cover.html
│ │ │ ├── fonts.css
│ │ │ ├── intro.html
│ │ │ └── main.css
│ │ └── logback-spring.xml
│ └── test/
│ └── java/
│ └── com/
│ └── htmake/
│ └── reader/
│ └── ReaderApplicationTests.java
├── vetur.config.js
└── web/
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── jsconfig.json
├── package.json
├── postcss.config.js
├── public/
│ ├── bookSourceDebug/
│ │ ├── index.css
│ │ ├── index.html
│ │ └── index.js
│ ├── browsertest.html
│ ├── index.html
│ ├── robots.txt
│ └── sw.js
├── src/
│ ├── App.vue
│ ├── assets/
│ │ └── fonts/
│ │ └── iconfont.css
│ ├── components/
│ │ ├── AddUser.vue
│ │ ├── BookGroup.vue
│ │ ├── BookInfo.vue
│ │ ├── BookManage.vue
│ │ ├── BookShelf.vue
│ │ ├── BookSource.vue
│ │ ├── Bookmark.vue
│ │ ├── BookmarkForm.vue
│ │ ├── Content.vue
│ │ ├── Explore.vue
│ │ ├── LocalStore.vue
│ │ ├── MPCode.vue
│ │ ├── PopCatalog.vue
│ │ ├── ReadSettings.vue
│ │ ├── ReplaceRule.vue
│ │ ├── ReplaceRuleForm.vue
│ │ ├── RssArticle.vue
│ │ ├── RssArticleList.vue
│ │ ├── RssSourceList.vue
│ │ ├── SearchBookContent.vue
│ │ ├── UserManage.vue
│ │ └── WebDAV.vue
│ ├── main.js
│ ├── plugins/
│ │ ├── animate.js
│ │ ├── axios.js
│ │ ├── cache.js
│ │ ├── chinese.js
│ │ ├── config.js
│ │ ├── element.js
│ │ ├── eventBus.js
│ │ ├── helper.js
│ │ ├── jump.js
│ │ ├── md5.js
│ │ ├── safe-json-stringify.js
│ │ └── vuex.js
│ ├── registerServiceWorker.js
│ ├── router/
│ │ └── index.js
│ └── views/
│ ├── Index.vue
│ └── Reader.vue
└── vue.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
.vertx
out
#/src/main/resources/application-prod.yml
/src/main/resources/application-default.yml
/src/main/resources/web
/storage*
/logs
/bin
/file-uploads
node_modules
/reader-assets
================================================
FILE: .gitattributes
================================================
*.java linguist-language=Kotlin
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report--问题反馈-.md
================================================
---
name: Bug report (问题反馈)
about: 描述你在使用中遇到的问题(issue语言:1. 中文;2. 英文)
title: ''
labels: ''
assignees: ''
---
**为避免无效问题和冗余问题,提问前请确认**
1. 你确定Google不能解决你的问题
2. 你确定已有的issue不能解决你的问题
3. 你确定issue的title按照格式如下:[web/simple-web/server]:description
**Describe the bug 描述你遇到的问题**
A clear and concise description of what the bug is. 简洁有效的说明。
**To Reproduce 如何重现问题**
Steps to reproduce the behavior: 把你遇到的问题的发生步骤替换掉下面的内容:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior 期待修复的效果**
A clear and concise description of what you expected to happen. 简单描述。
**Screenshots 如有必要,可以截图说明**
If applicable, add screenshots to help explain your problem.
**版本说明**
- OS: [e.g. win] 说明操作系统
- Deploy Method 说明软件部署方式
- Program Version 说明软件版本
- Browser [e.g. chrome, safari] 说明终端、浏览器型号
**Additional context 其他说明**
Add any other context about the problem here. 添加你认为有必要的内容,否则不写。
================================================
FILE: .github/workflows/Dockerfile
================================================
FROM openjdk:8-jdk-alpine
# Install base packages
RUN \
# apk update; \
# apk upgrade; \
# Add CA certs tini tzdata
apk add --no-cache ca-certificates tini tzdata; \
update-ca-certificates; \
# Clean APK cache
rm -rf /var/cache/apk/*;
# 时区
ENV TZ=Asia/Shanghai
EXPOSE 8080
ENTRYPOINT ["/sbin/tini", "--"]
ADD ./reader.jar /app/bin/reader.jar
CMD ["java", "-jar", "/app/bin/reader.jar" ]
================================================
FILE: .github/workflows/Openj9-Dockerfile
================================================
FROM ibm-semeru-runtimes:open-8u332-b09-jre
# Install base packages
RUN \
apt-get update; \
apt-get install -y ca-certificates tini tzdata; \
update-ca-certificates; \
# Clean apt cache
rm -rf /var/lib/apt/lists/*
# 时区
ENV TZ=Asia/Shanghai
EXPOSE 8080
ENTRYPOINT ["/usr/bin/tini", "--"]
ADD ./reader.jar /app/bin/reader.jar
CMD ["java", "-jar", "/app/bin/reader.jar" ]
================================================
FILE: .github/workflows/build.yml
================================================
name: Build Docker Image
on:
push:
branches:
- master
jobs:
build:
if: github.repository == 'hectorqin/reader'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: hectorqin/reader-basic
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
snapshot: true
tags: "test"
================================================
FILE: .github/workflows/pull-request.yml
================================================
name: Pull Request Check
on:
pull_request:
types: [synchronize, reopened, labeled]
branches:
- master
concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
docker:
if: github.repository == 'hectorqin/reader'
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.base.sha }}
clean: false
-
name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
-
name: Build web
run: cd web && npm install && npm run build
-
name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '8'
cache: 'gradle'
-
name: Build Java
run:
mv ./web/dist ./src/main/resources/web && rm src/main/java/com/htmake/reader/ReaderUIApplication.kt && gradle -b cli.gradle assemble --info && mv ./build/libs/*.jar ./reader.jar
================================================
FILE: .github/workflows/release-github.yml
================================================
name: Publish Github Releases
on:
# push:
# tags:
# - 'v**'
# branches:
# - master
workflow_dispatch:
jobs:
buildRelease:
if: github.repository == 'hectorqin/reader'
name: "Build And Release"
runs-on: macos-11
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
-
name: Build web
run: cd web && npm install && npm run build && mv ./dist ../src/main/resources/web
-
name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
-
name: Build MacOS package
run:
JAVAFX_PLATFORM=mac ./gradlew packageReaderMac
-
name: Build Linux package
run:
JAVAFX_PLATFORM=linux ./gradlew packageReaderLinux
-
name: Build Windows package
run:
JAVAFX_PLATFORM=win ./gradlew packageReaderWin
-
name: Build server jar
run:
rm src/main/java/com/htmake/reader/ReaderUIApplication.kt && gradle -b cli.gradle assemble --info
-
name: Show files.
run: |
echo Showing current directory:
ls
echo Showing ./target directory:
ls ./build
echo Showing ./target directory:
ls ./build/libs
-
name: Pre Release
if: ${{contains(github.ref, 'master')}}
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: true
title: "Development Build"
files: |
./build/*.pkg
./build/*.zip
./build/libs/*.jar
-
name: Tagged Release
if: ${{contains(github.ref, 'v')}}
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
./build/*.pkg
./build/*.zip
./build/libs/*.jar
================================================
FILE: .github/workflows/release-openj9.yml
================================================
name: Publish Docker Multi-Platform Images Using Openj9
on:
# push:
# tags:
# - 'v**'
workflow_dispatch:
jobs:
docker:
if: github.repository == 'hectorqin/reader'
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
-
name: Build web
run: cd web && npm install && npm run build
-
name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'adopt-openj9'
java-version: '8'
cache: 'gradle'
-
name: Build Java
run:
mv ./web/dist ./src/main/resources/web && rm src/main/java/com/htmake/reader/ReaderUIApplication.kt && gradle -b cli.gradle assemble --info && mv ./build/libs/*.jar ./reader.jar
-
name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
# list of Docker images to use as base name for tags
images: |
hectorqin/reader-basic
# generate Docker tags based on the following events/attributes
flavor: |
latest=false
prefix=openj9-,onlatest=true
suffix=
tags: |
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ !contains(github.ref, 'beta') }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./.github/workflows/Openj9-Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: |
linux/amd64
linux/arm64/v8
linux/ppc64le
linux/s390x
================================================
FILE: .github/workflows/release.yml
================================================
name: Publish Docker Multi-Platform Images
on:
# push:
# tags:
# - 'v**'
workflow_dispatch:
jobs:
docker:
if: github.repository == 'hectorqin/reader'
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
-
name: Build web
run: cd web && npm install && npm run build
-
name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '8'
cache: 'gradle'
-
name: Build Java
run:
mv ./web/dist ./src/main/resources/web && rm src/main/java/com/htmake/reader/ReaderUIApplication.kt && gradle -b cli.gradle assemble --info && mv ./build/libs/*.jar ./reader.jar
-
name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
# list of Docker images to use as base name for tags
images: |
hectorqin/reader-basic
# generate Docker tags based on the following events/attributes
tags: |
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ !contains(github.ref, 'beta') }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./.github/workflows/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: |
linux/amd64
linux/arm64
linux/arm/v6
linux/arm/v7
linux/386
linux/ppc64le
linux/s390x
================================================
FILE: .gitignore
================================================
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
.vertx
out
#/src/main/resources/application-prod.yml
/src/main/resources/application-default.yml
/src/main/resources/web
/storage*
/logs
/bin
/file-uploads
/reader-assets
/.lh/
/simple-web/
/server/logs
/server/storage
/server/target
================================================
FILE: Dockerfile
================================================
FROM hectorqin/reader
# 时区
ENV TZ=Asia/Shanghai
EXPOSE 8080
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["java", "-jar", "/app/bin/reader.jar" ]
================================================
FILE: Dockerfile.source
================================================
FROM node:lts-alpine3.14 AS build-web
ADD . /app
WORKDIR /app/web
# Build web
RUN yarn && yarn build
# Build jar
FROM gradle:6.1.1-jdk8 AS build-env
ADD --chown=gradle:gradle . /app
WORKDIR /app
COPY --from=build-web /app/web/dist /app/src/main/resources/web
RUN \
rm src/main/java/com/htmake/reader/ReaderUIApplication.kt; \
gradle -b cli.gradle assemble --info; \
mv ./build/libs/*.jar ./build/libs/reader.jar
FROM amazoncorretto:8u332-alpine3.14-jre
# Install base packages
RUN \
# apk update; \
# apk upgrade; \
# Add CA certs tini tzdata
apk add --no-cache ca-certificates tini tzdata; \
update-ca-certificates; \
# Clean APK cache
rm -rf /var/cache/apk/*;
# 时区
ENV TZ=Asia/Shanghai
#RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
# && echo Asia/Shanghai > /etc/timdezone \
# && dpkg-reconfigure -f noninteractive tzdata
EXPOSE 8080
ENTRYPOINT ["/sbin/tini", "--"]
# COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
COPY --from=build-env /app/build/libs/reader.jar /app/bin/reader.jar
CMD ["java", "-jar", "/app/bin/reader.jar" ]
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Reader Copyright (C) 2022 hectorqin
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: README.md
================================================
# reader
阅读3服务器版,不需要手机。
加入TG群(限时开放) 👉 [https://t.me/+pQ8HDlANPZ84ZWNl](https://t.me/+pQ8HDlANPZ84ZWNl)
关注公众号,查看教程和书源👇

[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
> 注意❗️
>
> Reader 完整源码仅开放到 v2.5.4,新版本当前仅开放部分开源源码,见 https://github.com/hectorqin/reader-legado.
<details><summary>免责声明(Disclaimer)</summary>
阅读是一款提供网络文学搜索的工具,为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。
当您搜索一本书的时,阅读会将该书的书名以关键词的形式提交到各个第三方网络文学网站。各第三方网站返回的内容与阅读无关,阅读对其概不负责,亦不承担任何法律责任。任何通过使用阅读而链接到的第三方网页均系他人制作或提供,您可能从第三方网页上获得其他服务,阅读对其合法性概不负责,亦不承担任何法律责任。第三方搜索引擎结果根据您提交的书名自动搜索获得并提供试读,不代表阅读赞成或被搜索链接到的第三方网页上的内容或立场。您应该对使用搜索引擎的结果自行承担风险。
阅读不做任何形式的保证:不保证第三方搜索引擎的搜索结果满足您的要求,不保证搜索服务不中断,不保证搜索结果的安全性、正确性、及时性、合法性。因网络状况、通讯线路、第三方网站等任何原因而导致您不能正常使用阅读,阅读不承担任何法律责任。阅读尊重并保护所有使用阅读用户的个人隐私权,您注册的用户名、电子邮件地址等个人资料,非经您亲自许可或根据相关法律、法规的强制性规定,阅读不会主动地泄露给第三方。
阅读致力于最大程度地减少网络文学阅读者在自行搜寻过程中的无意义的时间浪费,通过专业搜索展示不同网站中网络文学的最新章节。阅读在为广大小说爱好者提供方便、快捷舒适的试读体验的同时,也使优秀网络文学得以迅速、更广泛的传播,从而达到了在一定程度促进网络文学充分繁荣发展之目的。阅读鼓励广大小说爱好者通过阅读发现优秀网络小说及其提供商,并建议阅读正版图书。任何单位或个人认为通过阅读搜索链接到的第三方网页内容可能涉嫌侵犯其信息网络传播权,应该及时向阅读提出书面权力通知,并提供身份证明、权属证明及详细侵权情况证明。阅读在收到上述法律文件后,将会依法尽快断开相关链接内容。
</details>
<details><summary>功能说明</summary>
书源管理 <br/>
- 书架管理 <br/>
- 书架布局 <br/>
- 搜索 <br/>
- 书海 <br/>
- 看书 <br/>
- 移动端适配 <br/>
- 换源 <br/>
- 翻页方式 <br/>
- 手势支持 <br/>
- 自定义主题 <br/>
- 自定义样式 <br/>
- WebDAV同步 <br/>
- 文字替换过滤 <br/>
- 听书<仅部分浏览器支持,手机端会因为锁屏而失效> <br/>
- 用户配置备份恢复 <br/>
- 支持漫画 <br/>
- 支持音频 <br/>
- 书源失效检测 <br/>
- 导入本地TXT、EPUB、UMD、PDF格式的书籍 <br/>
- 书籍分组 <br/>
- RSS订阅 <br/>
- 定时更新书架 <br/>
- 并发搜书 <br/>
- 本地书仓 <br/>
- 支持kindle阅读 <br/>
</details>
## 下载与安装
详见[文档](https://github.com/hectorqin/reader/blob/master/doc.md)
## 问题
- 部分使用了 `Javascript` 的书源可能会报错,如调用原生java等高级Javascript功能
- `webview` 功能需要另外部署接口,不支持 `sourceRegex` 匹配资源响应
- 不支持书源登录功能
## 感谢
- 项目初期参考了 [lightink-小说API](https://github.com/lightink-qingmo/lightink-server)
- [阅读](https://github.com/gedoor/MyBookshelf)
- [阅读3.0](https://github.com/gedoor/legado)
- 项目初期参考了 [阅读3.0Web端](https://github.com/celetor/web-yuedu3)
## 其它
- [帮助文档](https://github.com/hectorqin/reader/blob/master/doc.md)
- [界面预览](https://github.com/hectorqin/reader/blob/master/preview.md)
================================================
FILE: UPDATELOG.md
================================================
# Update Log
## v3.2.6
### Features
- 新增清除最近阅读功能
- 新增编辑文章内容功能
- 优化多个弹窗显示
- 优化书源导入逻辑
- 优化订阅同步逻辑
## v3.2.5
### Features
- 新增simple-web书架排序
- 优化文件读写锁
- 开启连读优化后缓存后三段TTS
### Bug Fixes
- 修改simple-web书架搜索bug
## v3.2.4
### Features
- 修改服务器版本脚本
- 去掉gc,优化文件读写锁
- 测试 iOS 朗读
- 修改接口请求默认超时时间为30秒
## v3.2.3
### Features
- 新增直接添加书签
- 新增simple-web页面设置
- 添加定时gc逻辑
- 优化simple-web页面
- 新增服务器端脚本
### Bug Fixes
- 修改后端链接状态bug
## v3.2.2
### Features
- 优化自动阅读
- 优化书海,书源管理样式
- 优化simple-web兼容性
- 新增PC端设置提示
### Bug Fixes
- 修复windows环境漫画路径问题
- 修复往前翻页bug
## v3.2.1
### Features
- 新增kindle7天试用申请
- 优化epub阅读位置记忆跳转
### Bug Fixes
- 尝试修复PC端启动jvm参数bug
## v3.2.0
### Features
- 新增视频源支持
- epub支持朗读和自动阅读,优化阅读界面
- 新增simple-web搜索及RSS页面,优化simple-web页面样式
- 修改epub导入兼容性
- 新增书源管理搜索功能
- 修改桌面端jvm启动参数
- 新增书源订阅管理
- 优化探索样式
- 优化书源调试页面
### Bug Fixes
- 修复epub书名包含+号时注入js失败问题
- 修复simple-web分页组件
- 修复remote-webview不能访问https问题
- 修改封面上传后弹窗无法关闭的bug
- 修改上下滚动模式目录跳转bug
## v3.1.1
### Features
- 新增在线TTS朗读(Edge大声朗读)
### Bug Fixes
- 修复朗读bug
## v3.1.0
### Features
- 新增下载数据备份功能,新增自动备份功能。通过 --reader.app.autoBackupUserData=true 启用,每天23:50开始会自动备份用户数据到webdav目录
- 延长kindle试用期至 2023-06-30,过期以后需要购买授权来使用kindle页面
- 新增书籍设置pdf图片宽度选项
- 新增朗读时跳过全标点段落,跳过段末标点符号
- 新增更新错误分组排除不追更书籍
### Bug Fixes
- 修复用户管理界面排序bug
## v3.0.5
### Features
- 去掉书籍上限启动参数
- 延长kindle试用期
## v3.0.4
### Features
- 新增封面代理设置
- 优化文件并发读写加锁逻辑
- 优化多源搜索
- 延长kindle试用期
## v3.0.3
### Features
- 新增章节请求超时设置
- 新增simple—web搜索书架功能
- 桌面端新增jvm配置
- 尝试修复音频音量bug,尝试优化pwa
- 页面优化
- 延长kindle试用期
### Bug Fixes
- 修复simple-web端bug
## v3.0.2
### Features
- 支持pdf格式
- 页面优化
- 延长kindle试用期15天
### Bug Fixes
- 修复用户默认书源bug
- simple-web兼容kindle
## v3.0.1
### Features
- 新增书架布局设置,新增分列布局
- 修改书籍分组的字段类型,支持更多分组
- 新增用户更多设置项
- 优化simple-web分页逻辑
- 优化simple-web兼容性
- 优化音频音量设置
### Bug Fixes
- 修复simple-web渲染器bug
- 修复删除书籍未刷新书架bug
## v3.0.0
### Features
- 新增epub iframe模式自定义字体支持
- 新增simple-web端,支持kindle使用(限时免费)
- 新增授权管理,多用户版用户上限降低至 15(已超出的无法再注册,但可以继续使用)
- 新增用户管理、书籍管理、书签管理等分页排序过滤功能
- 新增书源请求头设置
- 新增书籍批量缓存操作
- 新增contextPath设置项
- 新增书架搜索作者及分类
- 优化书籍信息页面
- 优化阅读界面功能按钮
### Bug Fixes
- 修复音频播放bug
- 修复调试书源跳转链接bug
## v2.7.4
### Features
- 新增一键导入本地书籍功能
- 添加新增替换规则入口,优化替换逻辑
- 新增更新错误内置分组
- 优化日志跟踪
- 优化音频时长获取逻辑
### Bug Fixes
- 修复Windows环境webdav路径判断bug
- 尝试修复音频时长获取bug
- 修复书架刷新并发bug
- 修复书籍封面接口bug
## v2.7.3
### Features
- 新增书仓搜索及解析书籍功能
- 修改语音库选择样式
- 优化书源分组
- 优化协程逻辑,拆分解析库和控制层库
- 新增原文阅读模式,优化缓存判断逻辑
### Bug Fixes
- 尝试修复自动切换主题bug
## v2.7.2
### Features
- 新增Epub解析模式, 支持简繁切换、左右翻页
- 新增Epub iframe 模式左右翻页功能
- 修改日志配置,仅保留7天
- 新增书仓文件管理筛选功能
- 修改简繁切换库,样式优化,感谢 [@terry3041](https://github.com/hectorqin/reader/pull/227)
### Bug Fixes
- 修复桌面端bug
## v2.7.1
### Bug Fixes
- 修复书仓下载bug
- 修复书籍追更选项设置bug
## v2.7.0
### Features
- 新增远程webview镜像,支持使用 `hectorqin/remote-webview` 镜像作为远程 `webview`,使用 --reader.app.remoteWebviewApi="http://0.0.0.0:8050" 启用。
- 新增书源`cookie`,`cache`功能支持
- 新增上下左右边距设置
## v2.6.4
### Features
- 新增远程 `webview` 支持,目前仅支持 `scrapinghub/splash` 镜像作为远程 `webview`,使用 --reader.app.remoteWebviewApi="http://0.0.0.0:8050" 启用。
- 优化听书逻辑
### Bug Fixes
- 修复清理用户bug
## v2.6.3
### Features
- 新增清理不活跃用户功能,使用 --reader.app.autoClearInactiveUser=31 (不活跃天数) 启用
- 优化书架更新逻辑
- 新增书架更新间隔设置选项,使用 --reader.app.shelfUpdateInteval=10 (更新间隔分钟,必须是10的倍数) 启用
### Bug Fixes
- 修复加入书架bug
- 修改CI
- 修复配置方案失效bug
---
## v2.6.2
### Features
- 添加用户并发修改锁
### Bug Fixes
- 修改CI
---
## v2.6.1
### Bug Fixes
- 修改CI
---
## v2.6.0
### Bug Fixes
- 修复本地书籍路径问题
---
## v2.5.8
### Features
- 测试CI
---
## v2.5.7
### Features
- 临时书籍使用临时缓存
- 新增支持mongodb存放数据
---
## v2.5.6
### Bug Fixes
- 修复书架路径bug
---
## v2.5.5
### Features
- 统一文件管理
- 优化书籍内容使用缓存图片
### Bug Fixes
- 修复书架绝对路径bug
---
## v2.5.4
### Features
- 新增分组排序功能
- 新增朗读定时功能
- 新增音频音量调整功能
### Bug Fixes
- 修复书架更新并发bug
- 修复window环境问题
- 修复书源调试bug
- 修复音频bug
---
## v2.5.3
### Features
- 新增 webdav 书仓功能,新增修改目录规则功能,优化本地书籍换源功能
---
## v2.5.2
### Bug Fixes
- 修复jdk8编译依赖
---
## v2.5.1
### Features
- 新增自定义字体功能,优化阅读设置功能,新增书签同步功能
---
## v2.5.0
### Features
- 新增全文搜索功能,本地书籍生成封面优化,epub设置增强
- 新增书签功能
---
## v2.4.1
### Features
- 完善注册登录,新增删除用户书源和恢复默认书源功能,测试CI
- 新增本地书仓功能,新增自定义书籍封面功能,新增用户上限和用户书籍上限
- 更新阅读内核
- 新增清空书源功能,新增自动缓存下一章
- 优化本地导入逻辑
- 完成替换规则改版逻辑,新增配置方案设置功能,修复bug,优化页面
- 新增上下滚动翻页模式,优化页面
- 新增简繁转换功能,新增替换规则导入功能,新增设置默认书源功能,新增像素滚动自动翻页,兼容ie
- 新增书源调试功能,优化书海功能,优化缓存功能
- 新增支持CBZ书籍,新增支持卷名,优化调试功能
- 新增书籍管理功能,新增缓存及导出功能,优化书海功能
### Bug Fixes
- 页面优化
- bug修复
---
## v2.0.3
### Features
- 更新阅读解析库
- 优化多源搜索和书源搜索功能
- 新增服务器缓存章节内容功能,优化阅读宽度设置
- 迁移缓存到indexdb
- 新增简洁模式
### Bug Fixes
- 修复书源分组搜索选择bug
- 页面优化
- bug修复
---
## v1.9.4
### Features
- 新增缓存管理,优化缓存逻辑
- 新增页面模式设置
- 新增自定义主题模式设置
- 新增远程书源导入功能
- 新增读取epub封面,优化导入逻辑,优化书源错误标记
- 新增书源导出功能
- 新增按分组搜索书源功能
- 新增刷新章节内容功能
- 新增翻页动画时长设置
### Bug Fixes
- 修复iPad兼容问题
- 修复精确搜索bug,优化json序列化
- 页面优化
- bug修复
---
## V1.8.0
### Features
- 新增点击翻页和选择文字过滤关闭选项
- 支持设置代理(待测试)
- 修复旧版本自动迁移bug
- 修复搜索bug
- 完善失败源标记和恢复逻辑
- 优化ios pwa样式
- 优化书籍标签显示
### Bug Fixes
- 页面优化
- bug修复
================================================
FILE: build.gradle.kts
================================================
import org.openjfx.gradle.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.lang.reflect.*
import io.github.fvarrui.javapackager.model.Platform
import io.github.fvarrui.javapackager.model.WindowsConfig
import de.undercouch.gradle.tasks.download.Download
buildscript {
val kotlin_version: String by extra{"1.5.21"}
// extra["kotlin_version"] = "1.5.21"
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
classpath("io.github.fvarrui:javapackager:1.6.5")
}
}
plugins {
id("org.springframework.boot") version "2.1.6.RELEASE"
id("java")
id("application")
id("org.openjfx.javafxplugin") version "0.0.9"
id("org.jetbrains.kotlin.plugin.spring") version "1.3.61"
}
configure<JavaFXOptions> {
version = "11.0.2"
modules = listOf("javafx.web")
// Set JAVAFX_PLATFORM to "linux", "win", or "mac"
val javafxPlatformOverride = System.getenv("JAVAFX_PLATFORM")
if (javafxPlatformOverride != null) {
val javafxPlatform: JavaFXPlatform = JavaFXPlatform.values()
.firstOrNull { it.classifier == javafxPlatformOverride }
?: throw IllegalArgumentException("JAVAFX_PLATFORM $javafxPlatformOverride not in list:" +
" ${JavaFXPlatform.values().map { it.classifier }}")
logger.info("Overriding JavaFX platform to {}", javafxPlatform)
// Override the private platform field
val platformField: Field = JavaFXOptions::class.java.getDeclaredField("platform")
platformField.isAccessible = true
platformField.set(this, javafxPlatform)
// Invoke the private updateJavaFXDependencies() method
val updateDeps: Method = JavaFXOptions::class.java.getDeclaredMethod("updateJavaFXDependencies")
updateDeps.isAccessible = true
updateDeps.invoke(this)
}
}
apply(plugin = "io.spring.dependency-management")
apply(plugin = "kotlin")
apply(plugin = "io.github.fvarrui.javapackager.plugin")
group = "com.htmake"
version = "2.5.4"
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
repositories {
mavenCentral()
maven("https://jitpack.io")
maven("https://gitlab.com/api/v4/projects/26729549/packages/maven")
google()
jcenter()
}
val compileOnly by configurations.getting {
extendsFrom(configurations["annotationProcessor"])
}
dependencies {
val kotlin_version: String by extra{"1.5.21"}
// val kotlin_version: String by extra
implementation("org.springframework.boot:spring-boot-starter")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
// vertx
implementation("io.vertx:vertx-core:3.8.1")
implementation("io.vertx:vertx-lang-kotlin:3.8.1")
implementation("io.vertx:vertx-lang-kotlin-coroutines:3.8.1")
implementation("io.vertx:vertx-web:3.8.1")
implementation("io.vertx:vertx-web-client:3.8.1")
// json
implementation("com.google.code.gson:gson:2.8.5")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.+")
// log
implementation("io.github.microutils:kotlin-logging:1.6.24")
implementation("uk.org.lidalia:sysout-over-slf4j:1.0.2")
implementation("com.google.guava:guava:28.0-jre")
// 网络
implementation("com.squareup.okhttp3:okhttp:4.9.1")
implementation("com.squareup.okhttp3:logging-interceptor:4.1.0")
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.6.1")
implementation("com.julienviet:retrofit-vertx:1.1.3")
//JS rhino
// implementation("com.github.gedoor:rhino-android:1.6")
implementation(fileTree("src/lib").include("rhino-*.jar"))
// 规则相关
implementation("org.jsoup:jsoup:1.14.1")
implementation("cn.wanghaomiao:JsoupXpath:2.5.0")
implementation("com.jayway.jsonpath:json-path:2.6.0")
// xml
// 弃用 xmlpull-1.1.4.0,因为它需要 Java9
// implementation("org.xmlpull:xmlpull:1.1.4.0")
implementation(fileTree("src/lib").include("xmlpull-*.jar"))
// implementation("com.github.stefanhaustein:kxml2:2.5.0")
//加解密类库
implementation("cn.hutool:hutool-crypto:5.8.0.M1")
// 转换繁体
// implementation("com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core:0.2.1")
}
// val compileKotlin: KotlinCompile by tasks
// val compileTestKotlin: KotlinCompile by tasks
// compileKotlin.kotlinOptions {
// jvmTarget = "1.8"
// }
// compileTestKotlin.kotlinOptions {
// jvmTarget = "1.8"
// }
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
application {
// Define the main class for the application
mainClassName = "com.htmake.reader.ReaderUIApplicationKt"
}
tasks.create<io.github.fvarrui.javapackager.gradle.PackageTask>("buildReader"){
dependsOn("build")
// mandatory
mainClass = "com.htmake.reader.ReaderUIApplicationKt"
// optional
setBundleJre(false)
vmArgs = arrayListOf<String>("-Dreader.app.showUI=true", "-Dspring.profiles.active=prod", "-Dreader.app.packaged=true", "-Dreader.app.debug=true")
}
tasks.create<io.github.fvarrui.javapackager.gradle.PackageTask>("packageReaderMac") {
dependsOn("build")
// mandatory
mainClass = "com.htmake.reader.ReaderUIApplicationKt"
// optional
setBundleJre(false)
// bundleJre = false
// setCreateZipball(true)
platform = Platform.mac
vmArgs = arrayListOf<String>("-Dreader.app.showUI=true", "-Dspring.profiles.active=prod", "-Dreader.app.packaged=true", "-Dreader.app.debug=false", "-Dlogging.path=\$HOME/.reader/logs")
}
tasks.create<io.github.fvarrui.javapackager.gradle.PackageTask>("packageReaderWin") {
dependsOn("build")
// mandatory
mainClass = "com.htmake.reader.ReaderUIApplicationKt"
// optional
setBundleJre(false)
// bundleJre = true
// jrePath = File(buildDir, "win64-jre")
setCreateZipball(true)
platform = Platform.windows
vmArgs = arrayListOf<String>("-Dreader.app.showUI=true", "-Dspring.profiles.active=prod", "-Dreader.app.debug=false")
withGroovyBuilder {
"winConfig" {
"setWrapJar"(false)
}
}
// winConfig {
// wrapJar = false
// }
}
tasks.create<io.github.fvarrui.javapackager.gradle.PackageTask>("packageReaderLinux") {
dependsOn("build")
// mandatory
mainClass = "com.htmake.reader.ReaderUIApplicationKt"
// optional
setBundleJre(false)
// bundleJre = false
setCreateZipball(true)
platform = Platform.linux
vmArgs = arrayListOf<String>("-Dreader.app.showUI=true", "-Dspring.profiles.active=prod", "-Dreader.app.debug=false")
withGroovyBuilder {
"linuxConfig" {
"setWrapJar"(false)
}
}
}
tasks {
val downloadWinJre by registering(Download::class) {
src("https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.8%2B10/OpenJDK11U-jre_x64_windows_hotspot_11.0.8_10.zip")
dest(File(buildDir, "win64-jre.zip"))
onlyIfModified(true)
}
}
tasks.register<Copy>("unpackWinJre") {
dependsOn("downloadWinJre")
from(zipTree("$buildDir/win64-jre.zip")) {
include("jdk*/**")
eachFile {
relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray())
}
includeEmptyDirs = false
}
into(File(buildDir, "win64-jre"))
}
// javafx {
// version = "11.0.2"
// modules = [ 'javafx.web' ]
// }
================================================
FILE: build.sh
================================================
#!/bin/bash
oldJAVAHome=$JAVA_HOME
task=$1
version=""
checkJava()
{
if [ -d /Library/Java/JavaVirtualMachines/openjdk-11.jdk/Contents/Home ]; then
export JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-11.jdk/Contents/Home
fi
javaVersion=$(java -version 2>&1 | sed -n ';s/.* version "\(.*\)\.\(.*\)\..*".*/\1\2/p;')
if [[ "$javaVersion" -lt "110" ]]; then
echo "Java version must not lower than 11.0"
exit 1
fi
}
getVersion()
{
version=$(grep -Eo "^version = .*" $1 | grep -Eo "['\"].*['\"]" | tr -d "'\"")
}
getVersion ./build.gradle.kts
case $task in
build)
checkJava
# 调试打包
./gradlew buildReader
;;
run)
checkJava
# 运行 javafx UI
port=$2
if [[ -z "$port" ]]; then
port=8080
fi
./gradlew assemble --info
if test $? -eq 0; then
shift
shift
java -jar build/libs/reader-$version.jar --reader.app.showUI=true --reader.server.port=$port $@
fi
;;
win)
checkJava
# 打包 windows 安装包
JAVAFX_PLATFORM=win ./gradlew packageReaderWin
;;
linux)
checkJava
# 打包 linux 安装包
JAVAFX_PLATFORM=linux ./gradlew packageReaderLinux
;;
mac)
checkJava
# 打包 mac 安装包
JAVAFX_PLATFORM=mac ./gradlew packageReaderMac
;;
serve)
# 服务端一键运行
port=$2
if [[ -z "$port" ]]; then
port=8080
fi
mv src/main/java/com/htmake/reader/ReaderUIApplication.kt src/main/java/com/htmake/reader/ReaderUIApplication.kt.back
getVersion ./cli.gradle
./gradlew -b cli.gradle assemble --info
if test $? -eq 0; then
shift
shift
mv src/main/java/com/htmake/reader/ReaderUIApplication.kt.back src/main/java/com/htmake/reader/ReaderUIApplication.kt
java -jar build/libs/reader-$version.jar --reader.server.port=$port $@
else
mv src/main/java/com/htmake/reader/ReaderUIApplication.kt.back src/main/java/com/htmake/reader/ReaderUIApplication.kt
fi
;;
cli)
# 服务端打包命令
shift
export JAVA_HOME=$oldJAVAHome
mv src/main/java/com/htmake/reader/ReaderUIApplication.kt src/main/java/com/htmake/reader/ReaderUIApplication.kt.back
getVersion ./cli.gradle
./gradlew -b cli.gradle $@
mv src/main/java/com/htmake/reader/ReaderUIApplication.kt.back src/main/java/com/htmake/reader/ReaderUIApplication.kt
;;
yarn)
# yarn 快捷命令,默认 install
shift
cd web
yarn $@
;;
web)
# 开发web页面
cd web
yarn serve
;;
sync)
# 编译同步web资源
cd web
yarn sync
;;
*)
echo "
USAGE: ./build.sh build|run|win|linux|mac|serve|cli|yarn|web|sync
build 调试打包
run 桌面端编译运行,需要先执行 sync 命令编译同步web资源
win 打包 windows 安装包
linux 打包 linux 安装包
mac 打包 mac 安装包
serve 服务端编译运行
cli 服务端打包命令
yarn web页面 yarn 快捷命令,默认 install
web 开发web页面
sync 编译同步web资源
"
;;
esac
export JAVA_HOME=$oldJAVAHome
================================================
FILE: cli.gradle
================================================
buildscript {
ext.kotlin_version = '1.5.21'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'java'
id "org.jetbrains.kotlin.plugin.spring" version "1.3.61"
}
apply plugin: 'io.spring.dependency-management'
apply plugin: 'kotlin'
group = 'com.htmake'
version = '2.5.4'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
maven { url "https://gitlab.com/api/v4/projects/26729549/packages/maven" }
google()
jcenter()
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// vertx
implementation "io.vertx:vertx-core:3.8.1"
implementation "io.vertx:vertx-lang-kotlin:3.8.1"
implementation "io.vertx:vertx-lang-kotlin-coroutines:3.8.1"
implementation 'io.vertx:vertx-web:3.8.1'
implementation 'io.vertx:vertx-web-client:3.8.1'
// json
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.+"
// log
implementation "io.github.microutils:kotlin-logging:1.6.24"
implementation "uk.org.lidalia:sysout-over-slf4j:1.0.2"
implementation "com.google.guava:guava:28.0-jre"
// 网络
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.1.0"
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.6.1"
implementation "com.julienviet:retrofit-vertx:1.1.3"
//JS rhino
// implementation "com.github.gedoor:rhino-android:1.6"
implementation(fileTree(dir: 'src/lib', include: ['rhino-*.jar']))
// 规则相关
implementation "org.jsoup:jsoup:1.14.1"
implementation "cn.wanghaomiao:JsoupXpath:2.5.0"
implementation "com.jayway.jsonpath:json-path:2.6.0"
// xml
// 弃用 xmlpull-1.1.4.0,因为它需要 Java9
// implementation "org.xmlpull:xmlpull:1.1.4.0"
implementation(fileTree(dir: 'src/lib', include: ['xmlpull-*.jar']))
// implementation "com.github.stefanhaustein:kxml2:2.4.2"
//加解密类库
implementation "cn.hutool:hutool-crypto:5.8.0.M1"
// 转换繁体
// implementation "com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core:0.2.1"
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
================================================
FILE: doc.md
================================================
# 文档
- [文档](#文档)
- [免责声明(Disclaimer)](#免责声明disclaimer)
- [数据存储](#数据存储)
- [本地书仓](#本地书仓)
- [阅读页面地址](#阅读页面地址)
- [全功能web端](#全功能web端)
- [适配kindle的 `simple-web`](#适配kindle的-simple-web)
- [自定义阅读主题](#自定义阅读主题)
- [自定义样式](#自定义样式)
- [接口服务配置](#接口服务配置)
- [WebDAV同步配置](#webdav同步配置)
- [客户端](#客户端)
- [Windows / MacOS / Linux](#windows--macos--linux)
- [手机端](#手机端)
- [服务器版](#服务器版)
- [Docker版](#docker版)
- [Docker-Compose版(推荐)](#docker-compose版推荐)
- [通过脚本一键部署](#通过脚本一键部署)
- [Arch Linux 安装](#Arch-Linux-安装)
- [配置文件](#配置文件)
- [Nginx反向代理(如果有域名可以考虑80端口复用)](#nginx反向代理如果有域名可以考虑80端口复用)
- [开发编译](#开发编译)
- [编译脚本](#编译脚本)
- [编译前端](#编译前端)
- [编译接口](#编译接口)
- [接口文档](#接口文档)
- [新增接口](#新增接口)
- [加入书架](#加入书架)
- [获取书籍书源](#获取书籍书源)
- [搜索书籍更多书源](#搜索书籍更多书源)
- [书籍换源](#书籍换源)
## 免责声明(Disclaimer)
阅读是一款提供网络文学搜索的工具,为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。
当您搜索一本书的时,阅读会将该书的书名以关键词的形式提交到各个第三方网络文学网站。各第三方网站返回的内容与阅读无关,阅读对其概不负责,亦不承担任何法律责任。任何通过使用阅读而链接到的第三方网页均系他人制作或提供,您可能从第三方网页上获得其他服务,阅读对其合法性概不负责,亦不承担任何法律责任。第三方搜索引擎结果根据您提交的书名自动搜索获得并提供试读,不代表阅读赞成或被搜索链接到的第三方网页上的内容或立场。您应该对使用搜索引擎的结果自行承担风险。
阅读不做任何形式的保证:不保证第三方搜索引擎的搜索结果满足您的要求,不保证搜索服务不中断,不保证搜索结果的安全性、正确性、及时性、合法性。因网络状况、通讯线路、第三方网站等任何原因而导致您不能正常使用阅读,阅读不承担任何法律责任。阅读尊重并保护所有使用阅读用户的个人隐私权,您注册的用户名、电子邮件地址等个人资料,非经您亲自许可或根据相关法律、法规的强制性规定,阅读不会主动地泄露给第三方。
阅读致力于最大程度地减少网络文学阅读者在自行搜寻过程中的无意义的时间浪费,通过专业搜索展示不同网站中网络文学的最新章节。阅读在为广大小说爱好者提供方便、快捷舒适的试读体验的同时,也使优秀网络文学得以迅速、更广泛的传播,从而达到了在一定程度促进网络文学充分繁荣发展之目的。阅读鼓励广大小说爱好者通过阅读发现优秀网络小说及其提供商,并建议阅读正版图书。任何单位或个人认为通过阅读搜索链接到的第三方网页内容可能涉嫌侵犯其信息网络传播权,应该及时向阅读提出书面权力通知,并提供身份证明、权属证明及详细侵权情况证明。阅读在收到上述法律文件后,将会依法尽快断开相关链接内容。
## 数据存储
接口服务使用文件存储书源及目录等信息,存储位置为 storage 目录(可通过运行时添加 `-Dreader.app.storagePath=/path/to/storage` 修改)。
> MacOS客户端的存储目录是 `~/.reader/storage`,Window和Linux客户端为 `运行目录/storage`
数据存储目录结构如下:
```bash
storage
├── assets # 静态资源
│ ├── hector # 用户 hector 的资源目录
│ | |── covers # 本地 epub 书籍的封面图片目录
│ │ ├── background # 自定义阅读背景图片保存目录
│ │ │ └── 6.jpg
│ └── reader.css # 自定义CSS样式文件
├── cache # 缓存目录
│ ├── 6190ac40068e74c2c82624e91a5f8a0c.jpg # 书籍封面缓存
│ ├── bookInfoCache # 书籍搜索缓存 ACache 目录
│ └── ea11967236129bdae6133c3c9ff8c2dd.jpg
├── data # 数据目录
│ ├── default # 系统默认用户的数据目录 (reader.app.secure为false时)
│ │ ├── bookSource.json # 书源列表
│ │ ├── bookshelf.json # 书架书籍列表
│ │ ├── 斗罗大陆_唐家三少 # 书籍缓存目录
│ │ │ ├── 5d01bc88d6b19ebbe974acaac1675811 # A书源章节缓存目录
│ │ │ ├── 5d01bc88d6b19ebbe974acaac1675811.json # A书源目录列表
│ │ │ ├── 7e5ca1cc2a1ea2e09fdec4ee2e150f02 # B书源章节缓存目录
│ │ │ ├── 7e5ca1cc2a1ea2e09fdec4ee2e150f02.json # B书源目录列表
│ │ │ └── bookSource.json # 书籍书源列表
│ ├── hector # 用户 hector 的数据目录 (reader.app.secure为true时的用户目录)
│ │ ├── bookSource.json # 书源列表
│ │ ├── bookshelf.json # 书架书籍列表
│ │ ├── webdav # webdav 存储目录 可能会存在 legado 子目录
│ │ │ ├── backup2021-09-15.zip # 阅读3备份文件
│ │ │ └── bookProgress # 阅读3书籍进度备份目录
│ │ │ └── 斗罗大陆_唐家三少.json # 阅读3书籍进度
│ │ └── 斗罗大陆_唐家三少 # 书籍缓存目录
│ │ |── 2d44d0ec2397b6c1d4010b97d914031e # A书源章节缓存目录
│ │ └── 2d44d0ec2397b6c1d4010b97d914031e.json # A书源目录列表
│ └── users.json # 用户列表
├── localStore # 本地书仓,所有用户共享(用户需要开启书仓权限,才能访问)
│ |── 斗破苍穹.txt # 本地书仓书籍
│ └── 斗罗大陆.txt # 本地书仓书籍
└── windowConfig.json # 窗口配置文件
```
### 本地书仓
在 `storage/localStore` 中可以集中存放管理本地书籍,开启访问权限的用户可以在 `页面-浏览书仓` 中选择批量导入到自己的书架进行阅读。
## 阅读页面地址
### 全功能web端
`http://ip:端口/`
### 适配kindle的 `simple-web`
`http://ip:端口/simple-web`
> 注意,加入TG群了解详情
## 自定义阅读主题
书架页面仅支持白天模式和黑夜模式。
阅读页面支持设置多款主题,还可以自定义主题。自定义阅读主题包括:
- 自定义页面背景颜色
- 自定义浮窗背景颜色
- 自定义阅读背景颜色
- 自定义阅读背景图片
## 自定义样式
页面还会加载应用目录下的 `reader-assets/reader.css` 这个CSS样式文件,在这个文件中可以自定义页面样式。
> 自定义样式可能需要配合 `!important` 来设定属性
## 接口服务配置
```yml
reader:
app:
workDir: "" # 工作目录
secure: false # 是否需要登录鉴权,开启后将支持多用户模式
inviteCode: "" # 注册邀请码,为空时则开放注册,否则注册时需要输入邀请码
secureKey: "" # 管理密码,开启鉴权时,前端管理用户空间的管理密码
cacheChapterContent: false # 是否缓存章节内容
debugLog: false # 是否打开调试日志
autoClearInactiveUser: 0 # 是否自动清理不活跃用户,为0不清理,大于0为清理超过 autoClearInactiveUser 天未登录的用户
mongoUri: "" # mongodb uri 用于备份数据
mongoDbName: "reader" # mongodb 数据库名称
shelfUpdateInteval: 10 # 书架自动更新间隔时间,单位分钟,必须是10的倍数
userLimit: 15 # 用户上限,最大 15
remoteWebviewApi: "" # remote-webview 地址
defaultUserEnableWebdav: true # 新用户是否默认启用webdav
defaultUserEnableLocalStore: true # 新用户是否默认启用localStore
defaultUserEnableBookSource: true # 新用户是否默认可编辑书源,如果为false,则只能使用默认书源,不能新增/修改/删除
defaultUserEnableRssSource: true # 新用户是否默认可编辑RSS源
defaultUserBookSourceLimit: 100 # 新用户默认书源上限
defaultUserBookLimit: 200 # 新用户默认书籍上限
autoBackupUserData: false # 是否自动备份用户数据
minUserPasswordLength: 8 # 用户密码最小长度
remoteBookSourceUpdateInterval: 720 # 远程书源定时更新间隔时间,单位分钟,必须是10的倍数
server:
port: 8080 # 监听端口
contextPath: "" # 二级目录,为空则不使用二级目录
webUrl: http://localhost:${reader.server.port} # web链接
```
## WebDAV同步配置
1. 首先需要在阅读App里面配置 `WebDAV备份`
服务器地址: `http://IP:端口/reader3/webdav/`
如果开启了 `reader.app.secure` 选项,那么使用网页注册的用户名和密码登录,否则使用用户名 `default` 和 密码 `123456` 登录
2. 然后在阅读App里面点击备份
3. 在网页里面查看WebDAV文件,确认是否备份成功
4. 备份成功之后
- 服务器会自动同步书籍阅读进度(暂不支持章节内阅读位置,也不会自动同步书架信息变更)
- 可以直接选择阅读App的备份文件进行恢复,这样会直接覆盖书源和书架信息
- 可以备份当前书源和书架信息到WebDAV,但是必须要先备份成功
- 需要通过恢复备份文件来同步书籍和书源信息
5. PS: 本地书源的书籍同步后无法打开,除非换源
## 客户端
### Windows / MacOS / Linux
从 [releases](https://github.com/hectorqin/reader/releases) 下载对应平台安装包安装即可,需要安装java8及以上环境
MacOS 版 `storage` 默认是 `用户目录/.reader/storage`,其它版本 `storage` 默认是 `程序目录/storage`
#### 配置文件
`storage/windowConfig.json`
包含图形界面和接口服务的相关配置,JSON格式,修改后,程序重启才会生效
> 请仔细检查配置内容,不支持注释,此处注释只是为了方便理解
```json
{
"serverPort": 8080, // web服务端口,默认为 8080
"showUI": true, // 是否显示UI界面,默认为显示
"debug": false, // 是否调试模式,默认为否
"positionX": 0.0, // 窗口位置 横坐标
"positionY": 0.0, // 窗口位置 纵坐标
"width": 1280.0, // 窗口大小 宽度
"height": 800.0, // 窗口大小 高度
"rememberSize": true, // 改变窗口大小时,是否记住窗口大小,默认记住
"rememberPosition": false, // 移动窗口时,是否记住窗口位置,默认不记住
"setWindowPosition": false, // 启动时是否设置窗口位置,默认不设置,窗口默认居中
"setWindowSize": true, // 启动时是否设置窗口大小,默认按照配置文件进行设置
"serverConfig": { // 接口服务配置,此处配置会被 `serverPort|showUI|debug` 等覆盖
"reader.app.secure": false, // 是否需要登录鉴权,开启后将支持多用户模式
"reader.app.inviteCode": "", // 注册邀请码,为空时则开放注册,否则注册时需要输入邀请码。仅多用户模式下有效
"reader.app.secureKey": "", // 管理密码,开启鉴权时,前端管理用户空间的管理密码。仅多用户模式下有效
}
}
```
### 手机端
使用docker版本或者服务器版本,访问web页面
可以添加为桌面应用
### 服务器版
从 [releases](https://github.com/hectorqin/reader/releases) 下载 `reader-server-$version.zip` 解压后运行即可,需要安装java8及以上环境
```bash
# 安装jdk10以上环境...
# 解压文件
unzip reader-server-$version.zip
# 运行
cd reader-server-$version
./bin/startup.sh
# windows 上直接点击 bin/startup.cmd 文件
# startup 脚本支持以下选项,这些选项如果使用命令行参数修改,则会覆盖配置文件的设置
# -m single|multi 选择单用户/多用户模式,默认 以配置文件 conf/application.properties 为准
# -s reader-xx 选择 jar 文件名(不含.jar后缀),默认使用target目录里最新的jar
# -i inviteCode 设置多用户模式下的邀请码,默认 以配置文件 conf/application.properties 为准
# -k secureKey 设置多用户模式下的管理密码,默认 以配置文件 conf/application.properties 为准
# 注意!!!startup 脚本在单用户模式下 默认占用 256m 内存,在多用户模式下 默认占用 1g 内存,如果内存不够,请自行修改脚本
# web端 http://localhost:8080/
# 接口地址 http://localhost:8080/reader3/
```
### Docker版
```bash
# 自行编译
# docker build -t reader:latest .
# 使用环境变量覆盖服务配置,环境变量采用大写字母,不允许使用.-符号,采用下划线“_”取代点“.” 减号“-”直接删除
# docker run -d --restart=always --name=reader -e "SPRING_PROFILES_ACTIVE=prod" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 reader:latest
# 跨平台镜像
# 新建构建器
# docker buildx create --use --name mybuilder
# 启动构建器
# docker buildx inspect mybuilder --bootstrap
# 查看构建器及其所支持的cpu架构
# docker buildx ls
# 构建跨平台镜像
# docker buildx build -t reader:latest --platform=linux/arm,linux/arm64,linux/amd64 . --push
# 使用预编译的镜像
# 自用版(建议修改映射端口)
docker run -d --restart=always --name=reader -e "SPRING_PROFILES_ACTIVE=prod" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader
# 多用户版(建议修改映射端口)
docker run -d --restart=always --name=reader -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader java -jar /app/bin/reader.jar --spring.profiles.active=prod --reader.app.secure=true --reader.app.secureKey=管理密码 --reader.app.inviteCode=注册邀请码
# 多用户版 使用环境变量(建议修改映射端口)
docker run -d --restart=always --name=reader -e "SPRING_PROFILES_ACTIVE=prod" -e "READER_APP_SECURE=true" -e "READER_APP_SECUREKEY=管理密码" -e "READER_APP_INVITECODE=注册邀请码" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader
# 更新docker镜像
# docker pull hectorqin/reader
#:后面的端口修改为映射端口
# web端 http://localhost:8080/
# 接口地址 http://localhost:8080/reader3/
# 通过watchtower手动更新
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower --cleanup --run-once reader
# 使用 remote-webview 功能
# 1.创建 remote-webview 容器
docker run -d --network host --restart=always hectorqin/remote-webview
# 2.重建 reader 容器
reader使用宿主机网络:--network host
reader添加环境变量:-e "READER_APP_REMOTEWEBVIEWAPI=http://localhost:8050"
获取reader添加参数:--reader.app.remoteWebviewApi=http://localhost:8050"
```
### Docker-Compose版(推荐)
```shell
#腾讯云,阿里云,华为云,甲骨文等服务器提供商需在控制台面板手动关闭防火墙并放行端口
#安装docker 及 docker-compose
#Debian/Ubuntu
apt install docker-compose -y
#CentOS
curl -fsSL https://get.docker.com | bash -s docker #国外服务器
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun #国内服务器
# 下载项目里的 docker-compose.yaml
wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/hectorqin/reader/master/docker-compose.yaml
# 根据 docker-compose.yaml 里面的注释编辑所需配置
vim docker-compose.yaml
# 保存
esc
:wq
# 启动 docker-compose
docker-compose up -d
# 停止 docker-compose
docker-compose stop
# 查看实时日志
docker logs -f reader
# 自行导入远程书源(打开链接后复制网址导入即可)
https://legado.aoaostar.com/
# 手动更新
docker-compose pull && docker-compose up -d
```
### 通过脚本一键部署
```shell
# 此脚本对甲骨文非Ubuntu系统,CentOS9可能不兼容。建议网上手动搜索
#curl
bash <(curl -L -s https://mirror.ghproxy.com/https://raw.githubusercontent.com/hectorqin/reader/master/reader.sh)
#wget
bash <(wget -qO- --no-check-certificate https://mirror.ghproxy.com/https://raw.githubusercontent.com/hectorqin/reader/master/reader.sh)
```
### Arch Linux 安装
> 注意,此软件源并非官方提供,后果自负
从 [AUR 仓库](https://aur.archlinux.org/packages/reader-pro-bin)安装或[自建软件源](https://github.com/taotieren/aur-repo)
```bash
yay -Syu reader-pro
# 开启开机自启
sudo systemctl enable reader-pro-single
sudo systemctl enable reader-pro-multi
# 运行
sudo systemctl start reader-pro-single
sudo systemctl start reader-pro-multi
# 状态
sudo systemctl status reader-pro-single
sudo systemctl status reader-pro-multi
# 停止
sudo systemctl stop reader-pro-single
sudo systemctl stop reader-pro-multi
# 停止开机自启
sudo systemctl disable reader-pro-single
sudo systemctl disable reader-pro-multi
```
> Arch Linux 的存储目录是 `/var/lib/reader-pro/`
> Arch Linux 的配置文件是 `/usr/share/java/reader-pro/conf/application.properties`
## Nginx反向代理(如果有域名可以考虑80端口复用)
```shell
# 宝塔等各种面板不适用下列教程
# Debian/Ubuntu
apt install nginx -y
# CentOS
yum install nginx -y
vim /etc/nginx/conf.d/reader.conf
将下面代码复制进reader.conf后,修改域名输入
esc
:wq
保持即可
```
```nginx
server {
listen 80;
server_name 域名;
#开启ssl解除注释
# SSL证书获取
# https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E
#listen 443 ssl;
#ssl_certificate 证书.cer;
#ssl_certificate_key 证书.key;
#ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
#ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
#ssl_prefer_server_ciphers on;
#ssl_session_cache shared:SSL:10m;
#ssl_session_timeout 10m;
#if ($server_port !~ 443){
# rewrite ^(/.*)$ https://$host$1 permanent;
#}
#error_page 497 https://$host$request_uri;
gzip on; #开启gzip压缩
gzip_min_length 1k; #设置对数据启用压缩的最少字节数
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 6; #设置数据的压缩等级,等级为1-9,压缩比从小到大
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml; #设置需要压缩的数据格式
gzip_vary on;
client_max_body_size 50m; #允许上传50MB文件,上传本地书籍需要修改此项大小.如nginx主配置文件已添加,删除此行并修改主配置即可
location / {
proxy_pass http://127.0.0.1:4396; #端口自行修改为映射端口
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
}
```
## 开发编译
### 编译脚本
```bash
$ ./build.sh
USAGE: ./build.sh build|run|win|linux|mac|serve|cli|yarn|web|sync
build 调试打包
run 桌面端编译运行,需要先执行 sync 命令编译同步web资源
win 打包 windows 安装包
linux 打包 linux 安装包
mac 打包 mac 安装包
serve 服务端编译运行
cli 服务端打包命令
yarn web页面 yarn 快捷命令,默认 install
web 开发web页面
sync 编译同步web资源
```
### 编译前端
```bash
cd web
# 启动开发服务 访问 http://localhost:8081/
# yarn serve
# 编译,并拷贝到 src/main/resources/web 目录
yarn sync
```
### 编译接口
```bash
./gradlew assemble --info
java -jar build/libs/reader-${version}.jar
# 指定 storage 路径 默认为相对路径 storage
# java -Dreader.app.storagePath=cacheStorage -jar build/libs/reader-${version}.jar
# web端 http://localhost:8080/
# 接口地址 http://localhost:8080/reader3/
```
## 接口文档
与 [阅读3Web接口](https://github.com/gedoor/legado/blob/master/api.md) 基本一致,只是多了接口前缀 `/reader3/`
### 新增接口
#### 加入书架
- URL `http://localhost:8080/reader3/saveBook`
- Method `POST`
- Body `json 格式`
```JSON
{
"infoHtml": "",
"tocHtml": "",
"bookUrl": "https://www.damixs.com/book/dmfz.html",
"origin": "https://www.damixs.com",
"originName": "🎉大米小说",
"type": 0,
"name": "道门法则",
"author": "八宝饭",
"kind": "02-14",
"intro": "在道门掌控的天下,应该怎么修炼?符箓、丹药、道士、灵妖、斋醮科仪......想要修仙,很好,请从扫厕所开始做起!符诏到来的时候,你需要站在什么位置?Q群:1701556(需验证订阅截图)、954782460“盟主群”",
"wordCount": "",
"latestChapterTitle": "番外四(贺消脱止-M荣升盟主)",
"tocUrl": "",
"time": 1628756214810,
"originOrder": 16
}
```
- Response Body
[Book字段参考](https://github.com/gedoor/legado/blob/master/app/src/main/java/io/legado/app/data/entities/Book.kt)
```JSON
{
"isSuccess": true,
"errorMsg": "",
"data": Book
}
```
#### 获取书籍书源
- URL `http://localhost:8080/reader3/getBookSource?url=xxx`
- Method `GET`
获取指定URL对应的书源信息, 和 `阅读3Web接口` 的 `getSource` 接口相同
- Response Body
[SearchBook字段参考](https://github.com/gedoor/legado/blob/master/app/src/main/java/io/legado/app/data/entities/SearchBook.kt)
```JSON
{
"isSuccess": true,
"errorMsg": "",
"data": [SearchBook]
}
```
#### 搜索书籍更多书源
- URL `http://localhost:8080/reader3/searchBookSource?name=xxx&lastIndex=0`
- Method `GET`
搜索指定name对应的书源列表信息
lastIndex 是上次搜索结果中返回的字段,默认为 0,可以传入 `getBookSource` 接口返回的SearchBook列表长度
- Response Body
[SearchBook字段参考](https://github.com/gedoor/legado/blob/master/app/src/main/java/io/legado/app/data/entities/SearchBook.kt)
```JSON
{
"isSuccess": true,
"errorMsg": "",
"data": [SearchBook]
}
```
#### 书籍换源
- URL `http://localhost:8080/reader3/setBookSource`
- Method `POST`
- Body `json 格式`
```JSON
{
"newUrl": "新源书籍链接",
"name": "书籍名称",
"bookSourceUrl": "书源链接"
}
```
- Response Body
[Book字段参考](https://github.com/gedoor/legado/blob/master/app/src/main/java/io/legado/app/data/entities/Book.kt)
```JSON
{
"isSuccess": true,
"errorMsg": "",
"data": Book
}
```
================================================
FILE: docker-compose.yaml
================================================
version: '3.1'
services:
# reader 在线阅读
# 公开服务器(服务器位于日本):[https://reader.nxnow.top](https://reader.nxnow.top) 测试账号/密码分别为guest/guest123,也可自行创建账号添加书源,不定期删除长期未登录账号(2周)
# 书源集合 : [https://legado.aoaostar.com/](https://legado.aoaostar.com/) 点击打开连接,添加远程书源即可
# 公众号汇总 : [https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MjM5MzMyMDgyMA==&action=getalbum&album_id=2397535253763801090#wechat_redirect](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MjM5MzMyMDgyMA==&action=getalbum&album_id=2397535253763801090#wechat_redirect)
# 手动更新方式 : docker-compose pull && docker-compose up -d
reader:
image: hectorqin/reader
#image: hectorqin/reader:openj9-latest #docker镜像,arm64架构或小内存机器优先使用此镜像.启用需删除上一行
container_name: reader #容器名 可自行修改
restart: always
ports:
- 4396:8080 #4396端口映射可自行修改
networks:
- share_net
volumes:
- /home/reader/logs:/logs #log映射目录 /home/reader/logs 映射目录可自行修改
- /home/reader/storage:/storage #数据映射目录 /home/reader/storage 映射目录可自行修改
environment:
- SPRING_PROFILES_ACTIVE=prod
- READER_APP_USERLIMIT=50 #用户上限,默认50
- READER_APP_USERBOOKLIMIT=200 #用户书籍上限,默认200
- READER_APP_CACHECHAPTERCONTENT=true #开启缓存章节内容 V2.0
# 如果启用远程webview,需要取消注释下面的 remote-webview 服务
# - READER_APP_REMOTEWEBVIEWAPI=http://remote-webview:8050 #开启远程webview
# 下面都是多用户模式配置
- READER_APP_SECURE=true #开启登录鉴权,开启后将支持多用户模式
- READER_APP_SECUREKEY=adminpwd #管理员密码 建议修改
- READER_APP_INVITECODE=registercode #注册邀请码 建议修改,如不需要可注释或删除
# remote-webview:
# image: hectorqin/remote-webview
# container_name: remote-webview #容器名 可自行修改
# restart: always
# ports:
# - 8050:8050
# networks:
# - share_net
# 自动更新docker镜像
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: always
# 环境变量,设置为上海时区
environment:
- TZ=Asia/Shanghai
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: reader watchtower --cleanup --schedule "0 0 4 * * *"
networks:
- share_net
# 仅更新reader与watchtower容器,如需其他自行添加 '容器名' ,如:reader watchtower nginx
# --cleanup 更新后清理旧版本镜像
# --schedule 自动检测更新 crontab定时(限定6位crontab) 此处代表凌晨4点整
networks:
share_net:
driver: bridge
================================================
FILE: docker-compose.yml
================================================
version: '3.1'
services:
# reader 在线阅读
# 公开服务器(服务器位于日本):[https://reader.nxnow.top](https://reader.nxnow.top) 测试账号/密码分别为guest/guest123,也可自行创建账号添加书源,不定期删除长期未登录账号(2周)
# 书源集合 : [https://legado.aoaostar.com/](https://legado.aoaostar.com/) 点击打开连接,添加远程书源即可
# 公众号汇总 : [https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MjM5MzMyMDgyMA==&action=getalbum&album_id=2397535253763801090#wechat_redirect](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MjM5MzMyMDgyMA==&action=getalbum&album_id=2397535253763801090#wechat_redirect)
# 手动更新方式 : docker-compose pull && docker-compose up -d
reader:
#image: hectorqin/reader # 普通镜像
image: hectorqin/reader:openj9-latest # Openj9镜像,arm64架构或小内存机器优先使用
container_name: reader #容器名 可自行修改
restart: always
ports:
- 4396:8080 #4396端口映射可自行修改,8080请勿修改
volumes:
- /home/reader/logs:/logs #log映射目录 /home/reader/logs 映射目录可自行修改
- /home/reader/storage:/storage #数据映射目录 /home/reader/storage 映射目录可自行修改
environment:
- SPRING_PROFILES_ACTIVE=prod
#- READER_APP_USERLIMIT=50 #用户上限,默认且最大值为50
- READER_APP_USERBOOKLIMIT=200 #用户书籍上限,默认200
- READER_APP_CACHECHAPTERCONTENT=true #开启缓存章节内容
- READER_APP_REMOTEWEBVIEWAPI=http://readerwebview:8050 #启用webview(若下方readerwebview容器不开启需注释此行
# ↓多用户模式配置↓
- READER_APP_SECURE=true #开启登录鉴权,开启后将支持多用户模式
- READER_APP_SECUREKEY=adminpwd #管理员密码 建议修改
- READER_APP_INVITECODE=registercode #注册邀请码 建议修改,如不需要可注释或删除
# 如需支持webview书源,打开(占用较大,不需要可加 # 注释)
readerwebview:
image: hectorqin/remote-webview
container_name: readerwebview
restart: always
environment:
- TZ=Asia/Shanghai
# 自动更新docker镜像
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: always
environment:
- TZ=Asia/Shanghai
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: reader readerwebview watchtower --cleanup --schedule "0 0 4 * * *"
# 仅更新reader与watchtower容器,如需其他自行添加 '容器名' ,如:reader watchtower nginx
# --cleanup 更新后清理旧版本镜像
# --schedule 自动检测更新 crontab定时(限定6位crontab) 此处代表凌晨4点整
volumes:
reader:
readerwebview:
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
org.gradle.jvmargs=-Xmx2048m
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: nixpacks.toml
================================================
[phases.build]
cmds = ['railway run']
[start]
runImage = 'hectorqin/reader'
onlyIncludeFiles = ['Dockerfile']
cmd = '/sbin/tini --'
================================================
FILE: preview.md
================================================
# 预览










================================================
FILE: reader.sh
================================================
#!/bin/bash
red='\033[0;31m'
green="\033[32m"
yellow='\033[0;33m'
plain='\033[0m'
file_dir=""
remotePort=""
isMultiUser=""
adminPassword=""
registerCode=""
strTrue="true"
dockerImages=""
# CheckRoot
if [[ $EUID -ne 0 ]]; then
echo "请使用root用户登录!" 1>&2
exit 1
fi
# CheckSystem
if [[ -f /etc/redhat-release ]]; then
release="centos"
elif cat /etc/issue | grep -q -E -i "debian"; then
release="debian"
elif cat /etc/issue | grep -q -E -i "ubuntu"; then
release="ubuntu"
elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat"; then
release="centos"
elif cat /proc/version | grep -q -E -i "debian"; then
release="debian"
elif cat /proc/version | grep -q -E -i "ubuntu"; then
release="ubuntu"
elif cat /proc/version | grep -q -E -i "centos|red hat|redhat"; then
release="centos"
fi
bit=$(uname -m)
if test "$bit" != "x86_64"; then
bit="arm64"
else bit="amd64"
fi
os_version=""
# os version
if [[ -f /etc/os-release ]]; then
os_version=$(awk -F'[= ."]' '/VERSION_ID/{print $3}' /etc/os-release)
fi
if [[ -z "$os_version" && -f /etc/lsb-release ]]; then
os_version=$(awk -F'[= ."]+' '/DISTRIB_RELEASE/{print $2}' /etc/lsb-release)
fi
if [[ x"${release}" == x"centos" ]]; then
if [[ ${os_version} -le 6 ]]; then
echo -e "${red}请使用 CentOS 7 或更高版本的系统!${plain}\n" && exit 1
fi
elif [[ x"${release}" == x"ubuntu" ]]; then
if [[ ${os_version} -lt 16 ]]; then
echo -e "${red}请使用 Ubuntu 16 或更高版本的系统!${plain}\n" && exit 1
fi
elif [[ x"${release}" == x"debian" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red}请使用 Debian 9 或更高版本的系统!${plain}\n" && exit 1
fi
fi
install_dockercompose() {
if [[ x"${release}" == x"centos" ]]; then
yum install wget curl -y
echo -e "${green} 正在移除CentOS遗留无效Docker文件 ${plain}"
yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine -y
echo -e "${green} 正在安装Docker ${plain}"
yum install yum-utils device-mapper-persistent-data lvm2 -y
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
echo -e "${green} 正在启动Docker ${plain}"
systemctl start docker
systemctl restart docker
systemctl enable docker
echo -e "${green} 正在安装docker-compose ${plain}"
curl -L "https://mirror.ghproxy.com/https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
else
echo -e "${green} 正在安装docker-compose ${plain}"
apt update && apt install wget curl docker-compose -y
fi
}
install_reader() {
mkdir -p ${orgin_file_dir}/storage/data/default
cd ${orgin_file_dir}
rm docker-compose*
wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/hectorqin/reader/master/docker-compose.yml
echo -e "${green} 正在配置默认书源 ${plain}"
wget https://jihulab.com/aoaostar/legado/-/raw/release/cache/6c35d84798ddbf4aad3fe3f0fd6cec53dd788be8.json -O storage/data/default/bookSource.json
# 判断是否合法json
local first_character=$(head -c 1 "storage/data/default/bookSource.json")
if [[ x"$first_character" == x"[" ]]; then
#
echo ""
else
echo -e "${red} 书源错误,已为您删除,请自行导入书源 ${plain}"
echo "[]" > storage/data/default/bookSource.json
fi
echo -e "${green} 正在配置docker变量 ${plain}"
sed -i "s/\/home\/reader/${file_dir}/" docker-compose.yml
sed -i "s/4396/${remotePort}/" docker-compose.yml
sed -i "s/openj9-latest/${dockerImages}/" docker-compose.yml
# 多用户
sed -i "s/READER_APP_SECURE\=true/READER_APP_SECURE\=${isMultiUser}/" docker-compose.yml
sed -i "s/adminpwd/${adminPassword}/" docker-compose.yml
sed -i "s/registercode/${registerCode}/" docker-compose.yml
echo -e "${green} 准备启动 ${plain}"
# 远程webview
docker-compose up -d
}
getRemotePort () {
echo "请输入部署端口,例如 4396"
read -p "不填默认为4396: " remotePort
if [[ -z "$remotePort" ]];then
remotePort="4396"
fi
if [ "$remotePort" -gt 0 ] 2>/dev/null;then
if [[ $remotePort -lt 0 || $remotePort -gt 65535 ]];then
echo -e "${red} 端口号不正确,请输入0-65535${plain}"
getRemotePort
exit 0
fi
else
echo -e "${red} 端口号不正确,请输入0-65535${plain}"
getRemotePort
exit 0
fi
}
getfileDir () {
echo -e "${green} 请输入数据存放目录,例如 /home/reader : ${plain}"
read -p "不填默认为/home/reader : " file_dir
if [[ -z "$file_dir" ]];then
file_dir="/home/reader"
fi
orgin_file_dir=$file_dir
file_dir=${file_dir//\//\\\/}
}
getMultiUser () {
echo -e "${green} 是否需要开启多用户 : ${plain}"
read -p "填0不开启,不填开启 : " isMultiUser
if [[ -z "$isMultiUser" ]];then
isMultiUser="true"
else
isMultiUser="false"
fi
}
getPwdOrCode () {
echo -e "${green} 请输入管理密码,用于加载用户空间 : ${plain}"
read -p "建议修改此参数,默认为adminpwd : " adminPassword
if [[ -z "$adminPassword" ]];then
adminPassword="adminpwd"
fi
echo -e "${green} 请输入邀请码,用于注册使用 : ${plain}"
read -p "不填默认为空 : " registerCode
if [[ -z "$registerCode" ]];then
registerCode=""
fi
}
getDockerImages () {
echo -e "${green} 请输入需要的镜像 arm或者小内存(1G)机器建议openj9,其余建议基础镜像 : ${plain}"
read -p "不输入为基础镜像,输入其他值为openj9 : " dockerImages
if [[ -z "$dockerImages" ]];then
dockerImages="latest"
else
dockerImages="openj9-latest"
fi
}
Server_IP=''
Public_IP=''
getIpaddr () {
Server_IP=$(hostname -I | awk -F " " '{printf $1}')
Public_IP=$(curl http://pv.sohu.com/cityjson 2>> /dev/null | awk -F '"' '{print $4}')
}
echo -e "${green}准备部署reader${plain}"
echo -e "${green}甲骨文官方系统可能并不适用此脚本,本脚本仅测试CentOS7,8,Ubuntu20+,Debian10+${plain}"
install_dockercompose
getfileDir
getRemotePort
getMultiUser
if [ $isMultiUser == "true" ]; then
getPwdOrCode
fi
getDockerImages
install_reader
getIpaddr
echo -e "${green}初步部署完成,已配置默认书源,国内服务器等有控制台面板的服务器厂商请手动在控制台打开reader所需的端口${remotePort}${plain}"
if [ $Server_IP == $Public_IP ];then
echo -e "${green}网址:${plain} http://${Server_IP}:${remotePort}"
else
echo -e "${green}内网网址:${plain} http://${Server_IP}:${remotePort}"
echo -e "${green}公网网址:${plain} http://${Public_IP}:${remotePort}"
fi
echo -e "${green}如需修改其他配置请前往 cd${orgin_file_dir} 根据注释修改 vim docker-compose.yml文件后${plain}"
echo -e "${green}先自行学习vim用法,否则建议使用sftp或WindTerm等ssh自带sftp的软件直接打开编辑${plain}"
echo -e "${green}修改后前往 cd${orgin_file_dir} 后通过命令docker-compose up -d 重启即可${plain}"
================================================
FILE: server/bin/shutdown.cmd
================================================
@echo off
rem Copyright 1999-2018 Alibaba Group Holding Ltd.
rem Licensed under the Apache License, Version 2.0 (the "License");
rem you may not use this file except in compliance with the License.
rem You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
if not exist "%JAVA_HOME%\bin\java.exe" (
rem find java_home from reg
for /f "tokens=2*" %%i in ('reg query "HKLM\SOFTWARE\JavaSoft\Java Runtime Environment" /s ^| findstr "JavaHome"') do (
set "JAVA_HOME=%%j"
)
)
if exist "%JAVA_HOME%\bin\java.exe" (
set "JAVA=%JAVA_HOME%\bin\java.exe"
) else (
echo Please set the JAVA_HOME variable in your environment, We need jdk8 or later!
pause
EXIT /B 1
)
setlocal
set "PATH=%JAVA_HOME%\bin;%PATH%"
echo killing reader server
for /f "tokens=1" %%i in ('jps -m ^| find "reader.server"') do ( taskkill /F /PID %%i )
echo Done!
================================================
FILE: server/bin/shutdown.sh
================================================
#!/bin/bash
# Copyright 1999-2018 Alibaba Group Holding Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cd `dirname $0`/../target
target_dir=`pwd`
pid=`ps ax | grep -i 'reader.server' | grep ${target_dir} | grep java | grep -v grep | awk '{print $1}'`
if [ -z "$pid" ] ; then
echo "No reader running."
exit -1;
fi
echo "The reader(${pid}) is running..."
kill ${pid}
echo "Send shutdown request to reader(${pid}) OK"
================================================
FILE: server/bin/startup.cmd
================================================
@echo off
REM Copyright 1999-2018 Alibaba Group Holding Ltd.
REM Licensed under the Apache License, Version 2.0 (the "License");
REM you may not use this file except in compliance with the License.
REM You may obtain a copy of the License at
REM
REM http://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
if not exist "%JAVA_HOME%\bin\java.exe" (
rem find java_home from reg
for /f "tokens=2*" %%i in ('reg query "HKLM\SOFTWARE\JavaSoft\Java Runtime Environment" /s ^| findstr "JavaHome"') do (
set "JAVA_HOME=%%j"
)
)
if exist "%JAVA_HOME%\bin\java.exe" (
set "JAVA=%JAVA_HOME%\bin\java.exe"
) else (
rem check java command
for /f "usebackq delims=" %%i in (`where java`) do (
set JAVA=%%i
)
if not "%JAVA%" == "" if exist "%JAVA%" (
rem java path is exist
) else (
echo Please set the JAVA_HOME variable in your environment, We need jdk8 or later!
pause
EXIT /B 1
)
)
setlocal enabledelayedexpansion
set BASE_DIR=%~dp0
rem added double quotation marks to avoid the issue caused by the folder names containing spaces.
rem removed the last 5 chars(which means \bin\) to get the base DIR.
set BASE_DIR="%BASE_DIR:~0,-5%"
set CUSTOM_SEARCH_LOCATIONS=file:%BASE_DIR%/conf/
set SERVER=reader
for /f "delims=" %%i in ('dir /b /o:-n %BASE_DIR%\target\reader*.jar') do set NEWEST_JAR=%%i
if not "%NEWEST_JAR%"=="" (
set SERVER=%NEWEST_JAR:.jar=%
)
set MODE=""
set INVITE_CODE=""
set SECURE_KEY=""
set MODE_INDEX=-1
set INVITE_CODE_INDEX=-1
set SERVER_INDEX=-1
set SECURE_KEY_INDEX=-1
set EMBEDDED_STORAGE=""
set i=0
for %%a in (%*) do (
if "%%a" == "-m" ( set /a MODE_INDEX=!i!+1 )
if "%%a" == "-i" ( set /a INVITE_CODE_INDEX=!i!+1 )
if "%%a" == "-s" ( set /a SERVER_INDEX=!i!+1 )
if "%%a" == "-k" ( set /a SECURE_KEY_INDEX=!i!+1 )
set /a i+=1
)
set i=0
for %%a in (%*) do (
if %MODE_INDEX% == !i! ( set MODE="%%a" )
if %INVITE_CODE_INDEX% == !i! ( set INVITE_CODE="%%a" )
if %SERVER_INDEX% == !i! (set SERVER="%%a")
if %SECURE_KEY_INDEX% == !i! (set SECURE_KEY="%%a")
set /a i+=1
)
rem if reader startup mode is single
if %MODE% == "" (
echo The running mode of the Reader is determined by the configuration file conf/application.properties. Please note that there is currently no memory limit set for the JVM.
)
if %MODE% == "single" (
echo The running mode of the Reader is determined by the configuration file conf/application.properties. Please note that the current memory limit is set to 256m.
set "READER_JVM_OPTS=-Xms256m -Xmx256m -Xmn128m"
)
rem if reader startup mode is multi-user
if not %MODE% == "" if not %MODE% == "single" (
set READER_TIPS=""
set "READER_OPTS=-Dreader.app.secure=true"
if not "%INVITE_CODE%" == "" {
set "READER_OPTS=%READER_OPTS% -Dreader.app.inviteCode=%INVITE_CODE%"
set "READER_TIPS=%READER_TIPS% inviteCode: %INVITE_CODE%"
}
if not "%SECURE_KEY%" == "" {
set "READER_OPTS=%READER_OPTS% -Dreader.app.secureKey=%SECURE_KEY%"
set "READER_TIPS=%READER_TIPS% secureKey: %SECURE_KEY%"
}
if "%READER_TIPS%" == "" {
set "READER_TIPS=The invitation code and administrator password are determined by the configuration file conf\application.properties."
}
set "READER_TIPS=%READER_TIPS%. Please note that the current memory limit is set to 1g."
echo The Reader will running in multi-user mode. %READER_TIPS%
set "READER_JVM_OPTS=-server -Xms1g -Xmx1g -Xmn512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=160m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%BASE_DIR%\logs\java_heapdump.hprof -XX:-UseLargePages"
)
rem set reader options
@REM set "READER_OPTS=%READER_OPTS% -Dloader.path=%BASE_DIR%/plugins,%BASE_DIR%/plugins/health,%BASE_DIR%/plugins/cmdb,%BASE_DIR%/plugins/selector"
set "READER_OPTS=%READER_OPTS% -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Dspring.profiles.active=prod -Dreader.app.workDir=%BASE_DIR%"
set "READER_OPTS=%READER_OPTS% -jar %BASE_DIR%\target\%SERVER%.jar"
rem set reader spring config location
set "READER_CONFIG_OPTS=--spring.config.additional-location=%CUSTOM_SEARCH_LOCATIONS%"
rem set reader log4j file location
@REM set "READER_LOG4J_OPTS=--logging.config=%BASE_DIR%/conf/reader-logback.xml"
set COMMAND="%JAVA%" %READER_JVM_OPTS% %READER_OPTS% %READER_CONFIG_OPTS% %READER_LOG4J_OPTS% reader.server %*
echo Run command:
echo %COMMAND%
echo
echo Reader is starting, you can check the %BASE_DIR%\logs
rem start reader command
%COMMAND%
================================================
FILE: server/bin/startup.sh
================================================
#!/bin/bash
# Copyright 1999-2018 Alibaba Group Holding Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cygwin=false
darwin=false
os400=false
case "`uname`" in
CYGWIN*) cygwin=true;;
Darwin*) darwin=true;;
OS400*) os400=true;;
esac
error_exit ()
{
echo "ERROR: $1 !!"
exit 1
}
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
if [ -z "$JAVA_HOME" ]; then
if $darwin; then
if [ -x '/usr/libexec/java_home' ] ; then
export JAVA_HOME=`/usr/libexec/java_home`
elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
fi
else
JAVA_PATH=`dirname $(readlink -f $(which javac))`
if [ "x$JAVA_PATH" != "x" ]; then
export JAVA_HOME=`dirname $JAVA_PATH 2>/dev/null`
fi
fi
if [ -z "$JAVA_HOME" ]; then
error_exit "请设置 JAVA_HOME 环境变量,需要jdk8及以上的java环境!"
fi
fi
export BASE_DIR=`cd $(dirname $0)/..; pwd`
SERVER="reader"
NEWEST_JAR=$(ls $BASE_DIR/target | grep -Eo 'reader.*\.jar' | sort -nr | head -1)
if [ -n "$NEWEST_JAR" ]; then
SERVER=${NEWEST_JAR/.jar/}
fi
MODE=""
INVITE_CODE=""
SECURE_KEY=""
while getopts ":m:s:i:k:" opt
do
case $opt in
m)
MODE=$OPTARG;;
s)
SERVER=$OPTARG;;
i)
INVITE_CODE=$OPTARG;;
k)
SECURE_KEY=$OPTARG;;
?)
echo "未知的参数: $opt"
exit 1;;
esac
done
export JAVA_HOME
export JAVA="$JAVA_HOME/bin/java"
export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/
#===========================================================================================
# JVM Configuration
#===========================================================================================
if [[ "${MODE}" == "" ]]; then
echo "Reader 的运行模式以配置文件 conf/application.properties 为准。注意,当前未限制jvm内存"
elif [[ "${MODE}" == "single" ]]; then
JAVA_OPT="${JAVA_OPT} -Xms256m -Xmx256m -Xmn128m"
JAVA_OPT="${JAVA_OPT} -Dreader.app.secure=false"
echo "Reader 将以单用户模式运行。注意,当前内存限制为256m"
else
JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=160m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/logs/java_heapdump.hprof"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"
JAVA_OPT="${JAVA_OPT} -Dreader.app.secure=true"
TIPS=""
if [[ "${INVITE_CODE}" != "" ]]; then
JAVA_OPT="${JAVA_OPT} -Dreader.app.inviteCode=${INVITE_CODE}"
TIPS="${TIPS} 邀请码:${INVITE_CODE}"
fi
if [[ "${SECURE_KEY}" != "" ]]; then
JAVA_OPT="${JAVA_OPT} -Dreader.app.secureKey=${SECURE_KEY}"
TIPS="${TIPS} 管理员密码:${SECURE_KEY}"
fi
if [[ "${TIPS}" == "" ]]; then
TIPS="邀请码和管理员密码以配置文件 conf/application.properties 为准"
fi
TIPS="${TIPS}。注意,当前内存限制为1g"
echo "Reader 将以多用户模式运行。${TIPS}"
fi
JAVA_MAJOR_VERSION=$($JAVA -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p')
if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; then
JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${BASE_DIR}/logs/reader_gc.log:time,tags:filecount=10,filesize=100m"
else
JAVA_OPT_EXT_FIX="-Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext"
JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/reader_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M"
fi
# JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins,${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb,${BASE_DIR}/plugins/selector"
JAVA_OPT="${JAVA_OPT} -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Dreader.app.workDir=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}"
# JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/nacos-logback.xml"
JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288"
if [ ! -d "${BASE_DIR}/logs" ]; then
mkdir ${BASE_DIR}/logs
fi
echo "启动命令:"
echo "$JAVA $JAVA_OPT_EXT_FIX ${JAVA_OPT}"
echo
# check the start.out log output file
if [ ! -f "${BASE_DIR}/logs/start.out" ]; then
touch "${BASE_DIR}/logs/start.out"
else
mv ${BASE_DIR}/logs/start.out ${BASE_DIR}/logs/start-$(date +'%Y-%m-%d_%H_%M').out
fi
# start
echo "$JAVA $JAVA_OPT_EXT_FIX ${JAVA_OPT}" > ${BASE_DIR}/logs/start.out 2>&1 &
if [[ "$JAVA_OPT_EXT_FIX" == "" ]]; then
nohup "$JAVA" ${JAVA_OPT} reader.server >> ${BASE_DIR}/logs/start.out 2>&1 &
else
nohup "$JAVA" "$JAVA_OPT_EXT_FIX" ${JAVA_OPT} reader.server >> ${BASE_DIR}/logs/start.out 2>&1 &
fi
echo "Reader 正在启动中,你可以在 ${BASE_DIR}/logs/start.out 查看日志"
================================================
FILE: server/conf/application.properties
================================================
# 是否多用户模式,如果启动 startup 脚本时使用了 -m 1 选择多用户模式,-m single 运行单用户模式,否则根据此处的参数选择模式
reader.app.secure=true
# 邀请码,如果启动 startup 脚本时使用了参数 -i 邀请码,则会覆盖此处
reader.app.inviteCode=
# 管理密码,如果启动 startup 脚本时使用了参数 -k 管理密码,则会覆盖此处
reader.app.secureKey=
# 书源代理可通过 header 设置
# 是否缓存章节内容
reader.app.cacheChapterContent=true
# 用户上限,免费版用户上限最大15
reader.app.userLimit=15
# 是否开启书源调试日志
reader.app.debugLog=false
# 自动清理不活跃用户,单位天,0为不清理,大于0的数字为清理多少天未登录用户
reader.app.autoClearInactiveUser=0
# mongodb数据备份,mongodb链接地址
reader.app.mongoUri=
# mongodb数据备份,mongodb数据库名
reader.app.mongoDbName=reader
# 书架自动更新间隔,单位分钟
reader.app.shelfUpdateInteval=10
# 远程webview接口地址,可通过部署 hectorqin/remote-webview 来设置,http://IP:8050
reader.app.remoteWebviewApi=
# 新用户默认是否启用 webdav
reader.app.defaultUserEnableWebdav=true
# 新用户是否默认启用 本地书仓
reader.app.defaultUserEnableLocalStore=true
# 新用户是否默认启用 书源编辑
reader.app.defaultUserEnableBookSource=true
# 新用户是否默认启用 RSS源编辑
reader.app.defaultUserEnableRssSource=true
# 新用户是否默认书源数量上限
reader.app.defaultUserBookSourceLimit=100
# 新用户是否默认书籍数量上限
reader.app.defaultUserBookLimit=200
# 是否自动备份用户数据
reader.app.autoBackupUserData=false
# reader服务监听端口
reader.server.port=8080
# reader接口目录
reader.server.contextPath=
================================================
FILE: settings.gradle
================================================
pluginManagement {
repositories {
gradlePluginPortal()
}
}
rootProject.name = 'reader'
================================================
FILE: src/main/java/com/htmake/reader/ReaderApplication.kt
================================================
package com.htmake.reader
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import io.vertx.core.Future
import io.vertx.core.Vertx
import io.vertx.core.http.*
import io.vertx.core.json.Json
import io.vertx.ext.web.client.WebClient
import io.vertx.ext.web.client.WebClientOptions
import mu.KotlinLogging
import com.htmake.reader.api.YueduApi
import com.htmake.reader.verticle.RestVerticle
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.context.annotation.Bean
import javax.annotation.PostConstruct
private val logger = KotlinLogging.logger {}
@SpringBootApplication
@EnableScheduling
class ReaderApplication {
@Autowired
private lateinit var yueduApi: YueduApi
companion object {
val vertx by lazy { Vertx.vertx() }
fun vertx() = vertx
}
@PostConstruct
fun deployVerticle() {
Json.mapper.apply {
registerKotlinModule()
}
Json.prettyMapper.apply {
registerKotlinModule()
}
Json.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
vertx().deployVerticle(yueduApi)
}
@Bean
fun webClient(): WebClient {
val webClientOptions = WebClientOptions()
webClientOptions.isTryUseCompression = true
webClientOptions.logActivity = true
webClientOptions.isFollowRedirects = true
webClientOptions.isTrustAll = true
val httpClient = vertx().createHttpClient(HttpClientOptions().setTrustAll(true))
// val webClient = WebClient.wrap(HttpClient(delegateHttpClient), webClientOptions)
val webClient = WebClient.wrap(httpClient, webClientOptions)
return webClient
}
}
fun main(args: Array<String>) {
SpringApplication.run(ReaderApplication::class.java, *args)
}
================================================
FILE: src/main/java/com/htmake/reader/ReaderUIApplication.kt
================================================
package com.htmake.reader
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import io.vertx.core.Future
import io.vertx.core.Vertx
import io.vertx.core.http.*
import io.vertx.core.http.impl.HttpUtils
import io.vertx.core.json.Json
import io.vertx.ext.web.client.WebClient
import io.vertx.ext.web.client.WebClientOptions
import io.vertx.core.json.JsonObject
import mu.KotlinLogging
import com.htmake.reader.api.YueduApi
import com.htmake.reader.entity.Size
import com.htmake.reader.verticle.RestVerticle
import com.htmake.reader.utils.SpringContextUtils
import com.htmake.reader.SpringEvent
import com.htmake.reader.utils.getStorage
import com.htmake.reader.utils.saveStorage
import com.htmake.reader.utils.asJsonObject
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.annotation.Bean
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.ApplicationListener
import org.springframework.context.ApplicationEvent
import uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J
import javax.annotation.PostConstruct
import javafx.application.Application
import javafx.application.Platform
import javafx.scene.Scene
import javafx.scene.web.WebView
import javafx.scene.web.WebErrorEvent
import javafx.stage.Stage
import javafx.stage.WindowEvent
import javafx.stage.StageStyle
import javafx.event.EventHandler
import com.sun.javafx.application.LauncherImpl
import com.sun.javafx.scene.text.FontHelper
import javafx.scene.text.Font
import javafx.scene.control.ProgressBar
import javafx.scene.control.Dialog
import javafx.scene.control.ButtonType
import javafx.scene.image.ImageView
import javafx.scene.layout.VBox
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
import javafx.concurrent.Worker
import org.springframework.core.env.Environment
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.MapPropertySource
import java.util.concurrent.CompletableFuture
private val logger = KotlinLogging.logger {}
private var launchArgs = arrayOf<String>()
class ReaderUIApplication: Application() {
private lateinit var primaryStage: Stage;
private lateinit var splashStage: Stage;
lateinit var webUrl: String
lateinit var env: ConfigurableEnvironment
var windowConfigMap = mutableMapOf<String, Any>()
var isSpringBootLaunched = false
var springBootError = ""
var showUI = false
var defaultIcons = arrayOf<Image>();
fun boot() {
launch(*launchArgs)
}
override fun init() {
Thread() {
var app = SpringApplication(ReaderApplication::class.java)
var envListener = object: ApplicationListener<ApplicationEnvironmentPreparedEvent> {
override fun onApplicationEvent(event: ApplicationEnvironmentPreparedEvent) {
env = event.getEnvironment()
// 加载 windowConfig
var windowConfigSource = loadPropertySourceFromWindowConfig()
env.getPropertySources().addFirst(windowConfigSource)
// 获取应用相关配置
showUI = env.getProperty("reader.app.showUI", Boolean::class.java) ?: false
logger.info("showUI: {}", showUI)
var debug = env.getProperty("reader.app.debug", Boolean::class.java)
logger.info("debug: {}", debug)
var serverPort = env.getProperty("reader.server.port", Int::class.java)
logger.info("serverPort: {}", serverPort)
var port = 8080
if (serverPort != null && serverPort > 0) {
port = serverPort;
}
webUrl = env.getProperty("reader.server.webUrl") ?: ("http://localhost:" + port)
var sep = if(webUrl.contains("?")) {
"&"
} else {
"?"
}
if (debug != null && debug) {
webUrl = webUrl + sep + "debug=1&nopwa=1"
} else {
webUrl = webUrl + sep + "nopwa=1"
}
logger.info("webUrl: {}", webUrl)
// System.setProperty("reader.system.fonts", Font.getFontNames().joinToString(separator = ","))
if (showUI && ::primaryStage.isInitialized){
Platform.runLater(object : Runnable {
override fun run() {
showSplashScreen()
}
})
}
}
}
app.addListeners(envListener)
var springListener = object: ApplicationListener<SpringEvent> {
override fun onApplicationEvent(event: SpringEvent) {
val eventType = event.getEvent()
if (eventType == "READY") {
isSpringBootLaunched = true
if (showUI && ::primaryStage.isInitialized && ::webUrl.isInitialized){
Platform.runLater(object : Runnable {
override fun run() {
splashStage.hide()
splashStage.setScene(null)
showWebScreen(primaryStage, webUrl)
}
})
}
} else if (eventType == "START_ERROR") {
springBootError = event.getMessage()
if (showUI){
Platform.runLater(object : Runnable {
override fun run() {
if (::splashStage.isInitialized) {
splashStage.hide()
splashStage.setScene(null)
}
showAlert(springBootError);
stop();
}
})
} else {
logger.error(springBootError);
stop();
}
}
}
}
app.addListeners(springListener)
app.run(*launchArgs)
}.start()
}
override fun start(stage: Stage) {
try {
logger.info("javafx start: {}", stage)
primaryStage = stage
if (showUI) {
defaultIcons = arrayOf<Image>(
Image(ReaderUIApplication::class.java.getResource("/icons/16x16.png").toExternalForm()),
Image(ReaderUIApplication::class.java.getResource("/icons/24x24.png").toExternalForm()),
Image(ReaderUIApplication::class.java.getResource("/icons/32x32.png").toExternalForm()),
Image(ReaderUIApplication::class.java.getResource("/icons/48x48.png").toExternalForm()),
Image(ReaderUIApplication::class.java.getResource("/icons/64x64.png").toExternalForm()),
Image(ReaderUIApplication::class.java.getResource("/icons/128x128.png").toExternalForm())
)
if (isSpringBootLaunched) {
showWebScreen(stage, webUrl)
} else {
if (springBootError.isNotEmpty()) {
showAlert(springBootError)
stop()
} else {
showSplashScreen()
}
}
}
} catch(e: Exception) {
e.printStackTrace()
}
}
fun showSplashScreen() {
splashStage = Stage()
var imageView = ImageView(ReaderUIApplication::class.java.getResource("/images/loading.gif").toExternalForm());
// var splashProgressBar = ProgressBar();
// splashProgressBar.setPrefWidth(imageView.getImage().getWidth());
// splashProgressBar.setPrefHeight(10.0);
var vbox = VBox();
vbox.getChildren().addAll(imageView);
// vbox.setStyle("-fx-background-color: transparent;" +
// "-fx-padding: 0;" +
// "-fx-border-style: solid inside;" +
// "-fx-border-width: 1;" +
// "-fx-border-insets: 0;" +
// "-fx-border-radius: 0;" +
// "-fx-border-color: #999;");
var splashScene = Scene(vbox, Color.TRANSPARENT);
splashStage.setScene(splashScene);
splashStage.getIcons().addAll(defaultIcons);
splashStage.initStyle(StageStyle.TRANSPARENT);
logger.info("showSplashScreen: {}", splashStage)
splashStage.show()
}
fun showAlert(message: String, wait: Boolean = true) {
var alert = Dialog<Any>();
alert.getDialogPane().setContentText(message);
alert.getDialogPane().getButtonTypes().add(ButtonType.OK);
if (wait) {
alert.showAndWait();
} else {
alert.show();
}
}
fun showConfirm(message: String): Boolean {
var confirm = Dialog<Any>();
confirm.getDialogPane().setContentText(message);
confirm.getDialogPane().getButtonTypes().addAll(ButtonType.YES, ButtonType.NO);
val result = confirm.showAndWait().filter(ButtonType.YES::equals).isPresent();
return result
}
fun loadPropertySourceFromWindowConfig(): MapPropertySource {
loadWindowConfig()
var windowConfigPort = 0
var windowConfigSource = mutableMapOf<String, Any>()
try {
// 支持配置 接口服务
val serverConfig = windowConfigMap.getOrDefault("serverConfig", null) as? MutableMap<String, Any>
if (serverConfig != null) {
windowConfigSource = serverConfig
}
val serverPort = windowConfigMap.getOrDefault("serverPort", null)
if (serverPort != null) {
windowConfigPort = serverPort as Int
if (windowConfigPort > 0) {
windowConfigSource.put("reader.server.port", windowConfigPort)
}
}
val showUI = windowConfigMap.getOrDefault("showUI", true) as Boolean? ?: true
windowConfigSource.put("reader.app.showUI", showUI)
val debug = windowConfigMap.getOrDefault("debug", null)
if (debug != null) {
windowConfigSource.put("reader.app.debug", debug as Boolean)
}
} catch(e: Exception) {
e.printStackTrace()
}
logger.info("windowConfigSource: {}", windowConfigSource)
return MapPropertySource("windowConfig", windowConfigSource)
}
fun loadWindowConfig() {
val windowConfigObject = asJsonObject(getStorage("windowConfig"))
if (windowConfigObject != null) {
windowConfigMap = windowConfigObject.map
}
logger.info("windowConfigMap: {}", windowConfigMap)
}
fun getWindowConfigDoubleProperty(name: String, defaultVal: Double): Double {
var value = windowConfigMap.getOrDefault(name, defaultVal)
return when(value) {
is Int -> value.toDouble()
is Double -> value
else -> defaultVal
}
}
fun applyWindowConfig(stage: Stage): Size {
var width = 1280.0;
var height = 800.0;
try {
loadWindowConfig()
val setWindowPosition = windowConfigMap.getOrDefault("setWindowPosition", false) as Boolean? ?: false
if (setWindowPosition) {
var positionX = getWindowConfigDoubleProperty("positionX", 0.0)
var positionY = getWindowConfigDoubleProperty("positionY", 0.0)
stage.setX(positionX)
stage.setY(positionY)
}
val rememberSize = windowConfigMap.getOrDefault("rememberSize", true) as Boolean? ?: true
val rememberPosition = windowConfigMap.getOrDefault("rememberPosition", false) as Boolean? ?: false
if (rememberSize) {
stage.widthProperty().addListener{_, _, w ->
windowConfigMap.put("width", w)
}
// stage.heightProperty().addListener{_, _, h ->
// windowConfigMap.put("height", h)
// }
stage.sceneProperty().addListener{_, _, s ->
s.heightProperty().addListener{_, _, h ->
windowConfigMap.put("height", h)
}
}
}
if (rememberPosition) {
stage.xProperty().addListener{_, _, x ->
windowConfigMap.put("positionX", x)
}
stage.yProperty().addListener{_, _, y ->
windowConfigMap.put("positionY", y)
}
}
val setWindowSize = windowConfigMap.getOrDefault("setWindowSize", true) as Boolean? ?: true
if (setWindowSize) {
width = getWindowConfigDoubleProperty("width", width)
height = getWindowConfigDoubleProperty("height", height)
}
} catch(e: Exception) {
showAlert("窗口配置加载失败,请检查窗口配置文件(windowConfig.json)", false)
e.printStackTrace()
}
return Size(width, height)
}
fun showWebScreen(stage: Stage, url: String) {
// 配置主窗口
var windowSize = applyWindowConfig(stage);
System.setProperty("sun.net.http.allowRestrictedHeaders", "true")
// logger.info("Font.getFontNames: {}", Font.getFontNames())
// logger.info("showWebScreen: {}", url)
var webView = WebView();
var webEngine = webView.getEngine();
webEngine.setOnError{ event ->
logger.info("error: {}", event)
};
webEngine.setOnAlert{ event ->
showAlert(event.data.toString())
};
webEngine.setConfirmHandler{ message ->
showConfirm(message)
};
var reloadCount = 0;
webEngine.getLoadWorker().stateProperty().addListener{_, oldState, newState ->
logger.info("State from {} to {} , exception: {}", oldState, newState, webEngine.getLoadWorker().getException());
if (newState == Worker.State.FAILED) {
if (reloadCount < 5) {
reloadCount += 1
logger.info("reload {}", url)
webEngine.load(url);
}
}
}
webEngine.titleProperty().addListener{_, _, t ->
if (t != null && t.isNotEmpty()) {
stage.setTitle(t)
}
}
webEngine.load(url);
val scene = Scene(webView, windowSize.width, windowSize.height)
stage.setScene(scene)
stage.setTitle("阅读")
stage.getIcons().addAll(defaultIcons);
stage.initStyle(StageStyle.UNIFIED);
stage.show()
}
override fun stop() {
saveStorage("windowConfig", value = windowConfigMap, pretty = true)
super.stop()
var context = SpringContextUtils.getApplicationContext()
logger.info("application stop: {}", context)
System.exit(SpringApplication.exit(context))
}
}
fun main(args: Array<String>) {
logger.info("args: {}", args)
launchArgs = args
val app = ReaderUIApplication()
app.boot()
}
================================================
FILE: src/main/java/com/htmake/reader/SpringEvent.java
================================================
package com.htmake.reader;
import org.springframework.context.ApplicationEvent;
public class SpringEvent extends ApplicationEvent {
private String event;
private String message;
public SpringEvent(Object source, String event, String message) {
super(source);
this.event = event;
this.message = message;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
================================================
FILE: src/main/java/com/htmake/reader/api/ReturnData.kt
================================================
package com.htmake.reader.api
class ReturnData {
var isSuccess: Boolean = false
private set
var errorMsg: String = "未知错误,请联系开发者!"
private set
var data: Any? = null
private set
fun setErrorMsg(errorMsg: String): ReturnData {
this.isSuccess = false
this.errorMsg = errorMsg
return this
}
fun setData(data: Any): ReturnData {
this.isSuccess = true
this.errorMsg = ""
this.data = data
return this
}
}
================================================
FILE: src/main/java/com/htmake/reader/api/YueduApi.kt
================================================
package com.htmake.reader.api
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.BookGroup
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.RssArticle
import io.legado.app.model.webBook.WebBook
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.StaticHandler;
import mu.KotlinLogging
import com.htmake.reader.config.AppConfig
import com.htmake.reader.config.BookConfig
import io.legado.app.constant.DeepinkBookSource
import com.htmake.reader.api.controller.BookController
import com.htmake.reader.api.controller.BookSourceController
import com.htmake.reader.api.controller.RssSourceController
import com.htmake.reader.api.controller.UserController
import com.htmake.reader.api.controller.WebdavController
import com.htmake.reader.api.controller.ReplaceRuleController
import com.htmake.reader.api.controller.BookmarkController
import com.htmake.reader.utils.error
import com.htmake.reader.utils.success
import com.htmake.reader.utils.getStorage
import com.htmake.reader.utils.saveStorage
import com.htmake.reader.utils.asJsonArray
import com.htmake.reader.utils.asJsonObject
import com.htmake.reader.utils.toDataClass
import com.htmake.reader.utils.toMap
import com.htmake.reader.utils.fillData
import com.htmake.reader.utils.getWorkDir
import com.htmake.reader.utils.getRandomString
import com.htmake.reader.utils.genEncryptedPassword
import com.htmake.reader.entity.User
import com.htmake.reader.utils.SpringContextUtils
import com.htmake.reader.utils.deleteRecursively
import com.htmake.reader.utils.unzip
import com.htmake.reader.utils.zip
import com.htmake.reader.utils.jsonEncode
import com.htmake.reader.utils.getRelativePath
import com.htmake.reader.verticle.RestVerticle
import com.htmake.reader.SpringEvent
import org.springframework.stereotype.Component
import io.vertx.core.json.JsonObject
import io.vertx.core.json.JsonArray
import io.vertx.core.http.HttpMethod
import com.htmake.reader.api.ReturnData
import io.legado.app.utils.MD5Utils
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.URL;
import java.util.UUID;
import io.vertx.ext.web.client.WebClient
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.core.env.Environment
import java.io.File
import java.lang.Runtime
import kotlin.collections.mutableMapOf
import kotlin.system.measureTimeMillis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat;
import io.legado.app.utils.EncoderUtils
import io.legado.app.model.rss.Rss
import org.springframework.scheduling.annotation.Scheduled
import io.legado.app.model.localBook.LocalBook
import java.nio.file.Paths
import kotlinx.coroutines.withContext
import kotlinx.coroutines.async
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.CoroutineScope
private val logger = KotlinLogging.logger {}
@Component
class YueduApi : RestVerticle() {
@Autowired
private lateinit var appConfig: AppConfig
@Autowired
private lateinit var env: Environment
override suspend fun initRouter(router: Router) {
setupPort()
// 旧版数据迁移
migration()
// web界面
router.route("/*").handler(StaticHandler.create("web").setDefaultContentEncoding("UTF-8"));
// assets
var assetsDir = getWorkDir("storage", "assets");
var assetsDirFile = File(assetsDir);
if (!assetsDirFile.exists()) {
assetsDirFile.mkdirs();
}
var assetsCss = getWorkDir("storage", "assets", "reader.css");
var assetsCssFile = File(assetsCss);
if (!assetsCssFile.exists()) {
assetsCssFile.writeText("/* 在此处可以编写CSS样式来自定义页面 */");
}
router.route("/assets/*").handler(StaticHandler.create().setAllowRootFileSystemAccess(true).setWebRoot(assetsDir).setDefaultContentEncoding("UTF-8"));
// epub资源
var dataDir = getWorkDir("storage", "data");
router.route("/epub/*").handler {
var path = it.request().path().replace("/epub/", "/", true)
path = URLDecoder.decode(path, "UTF-8")
if (path.endsWith("html", true)) {
var filePath = File(dataDir + path)
if (filePath.exists()) {
// 处理 js 注入脚本
BookConfig.injectJavascriptToEpubChapter(filePath.toString())
}
}
it.next()
}
router.route("/epub/*").handler(StaticHandler.create().setAllowRootFileSystemAccess(true).setWebRoot(dataDir).setDefaultContentEncoding("UTF-8"));
// 获取系统信息
router.get("/reader3/getSystemInfo").coroutineHandler { getSystemInfo(it) }
////////// 接口部分
val bookController = BookController(coroutineContext)
val bookSourceController = BookSourceController(coroutineContext)
val rssSourceController = RssSourceController(coroutineContext)
val userController = UserController(coroutineContext)
val webdavController = WebdavController(coroutineContext, router) { ctx, error ->
onHandlerError(ctx, error)
}
val replaceRuleController = ReplaceRuleController(coroutineContext)
val bookmarkController = BookmarkController(coroutineContext)
/** 书源模块 */
router.post("/reader3/saveBookSource").coroutineHandler { bookSourceController.saveBookSource(it) }
router.post("/reader3/saveBookSources").coroutineHandler { bookSourceController.saveBookSources(it) }
router.get("/reader3/getBookSource").coroutineHandler { bookSourceController.getBookSource(it) }
router.post("/reader3/getBookSource").coroutineHandler { bookSourceController.getBookSource(it) }
router.get("/reader3/getBookSources").coroutineHandler { bookSourceController.getBookSources(it) }
router.post("/reader3/getBookSources").coroutineHandler { bookSourceController.getBookSources(it) }
router.post("/reader3/deleteAllBookSources").coroutineHandler { bookSourceController.deleteAllBookSources(it) }
router.post("/reader3/deleteBookSource").coroutineHandler { bookSourceController.deleteBookSource(it) }
router.post("/reader3/deleteBookSources").coroutineHandler { bookSourceController.deleteBookSources(it) }
// 上传书源文件
router.post("/reader3/readSourceFile").coroutineHandler { bookSourceController.readSourceFile(it) }
// 读取远程书源文件
router.post("/reader3/readRemoteSourceFile").coroutineHandlerWithoutRes { bookSourceController.readRemoteSourceFile(it) }
// 设置默认书源
router.post("/reader3/setAsDefaultBookSources").coroutineHandler { bookSourceController.setAsDefaultBookSources(it) }
router.post("/reader3/deleteUserBookSource").coroutineHandler { bookSourceController.deleteUserBookSource(it) }
router.post("/reader3/deleteBookSourcesFile").coroutineHandler { bookSourceController.deleteBookSourcesFile(it) }
/** 书籍模块 */
// 书架
router.get("/reader3/getBookshelf").coroutineHandler { bookController.getBookshelf(it) }
router.get("/reader3/getShelfBook").coroutineHandler { bookController.getShelfBook(it) }
router.post("/reader3/saveBook").coroutineHandler { bookController.saveBook(it) }
router.post("/reader3/deleteBook").coroutineHandler { bookController.deleteBook(it) }
router.post("/reader3/deleteBooks").coroutineHandler { bookController.deleteBooks(it) }
// 失效书源
router.post("/reader3/getInvalidBookSources").coroutineHandler { bookController.getInvalidBookSources(it) }
// 探索
router.post("/reader3/exploreBook").coroutineHandler { bookController.exploreBook(it) }
router.get("/reader3/exploreBook").coroutineHandler { bookController.exploreBook(it) }
// 搜索
router.get("/reader3/searchBook").coroutineHandler { bookController.searchBook(it) }
router.post("/reader3/searchBook").coroutineHandler { bookController.searchBook(it) }
router.get("/reader3/searchBookMulti").coroutineHandler { bookController.searchBookMulti(it) }
router.post("/reader3/searchBookMulti").coroutineHandler { bookController.searchBookMulti(it) }
router.get("/reader3/searchBookMultiSSE").coroutineHandlerWithoutRes { bookController.searchBookMultiSSE(it) }
// 书籍详情
router.get("/reader3/getBookInfo").coroutineHandler { bookController.getBookInfo(it) }
router.post("/reader3/getBookInfo").coroutineHandler { bookController.getBookInfo(it) }
// 章节列表
router.get("/reader3/getChapterList").coroutineHandler { bookController.getChapterList(it) }
router.post("/reader3/getChapterList").coroutineHandler { bookController.getChapterList(it) }
// 内容
router.get("/reader3/getBookContent").coroutineHandler { bookController.getBookContent(it) }
router.post("/reader3/getBookContent").coroutineHandler { bookController.getBookContent(it) }
// 保存阅读进度
router.post("/reader3/saveBookProgress").coroutineHandler { bookController.saveBookProgress(it) }
// 封面
router.get("/reader3/cover").coroutineHandlerWithoutRes { bookController.getBookCover(it) }
// 搜索其它来源
router.get("/reader3/searchBookSource").coroutineHandler { bookController.searchBookSource(it) }
router.post("/reader3/searchBookSource").coroutineHandler { bookController.searchBookSource(it) }
router.get("/reader3/getAvailableBookSource").coroutineHandler { bookController.getAvailableBookSource(it) }
router.post("/reader3/getAvailableBookSource").coroutineHandler { bookController.getAvailableBookSource(it) }
router.get("/reader3/searchBookSourceSSE").coroutineHandlerWithoutRes { bookController.searchBookSourceSSE(it) }
// 换源
router.get("/reader3/setBookSource").coroutineHandler { bookController.setBookSource(it) }
router.post("/reader3/setBookSource").coroutineHandler { bookController.setBookSource(it) }
// 修改分组
router.post("/reader3/saveBookGroupId").coroutineHandler { bookController.saveBookGroupId(it) }
router.post("/reader3/addBookGroupMulti").coroutineHandler { bookController.addBookGroupMulti(it) }
router.post("/reader3/removeBookGroupMulti").coroutineHandler { bookController.removeBookGroupMulti(it) }
// 导入本地文件
router.post("/reader3/importBookPreview").coroutineHandler { bookController.importBookPreview(it) }
router.post("/reader3/refreshLocalBook").coroutineHandler { bookController.refreshLocalBook(it) }
// 获取txt章节规则
router.get("/reader3/getTxtTocRules").coroutineHandler { bookController.getTxtTocRules(it) }
router.post("/reader3/getChapterListByRule").coroutineHandler { bookController.getChapterListByRule(it) }
// 书籍分组
router.get("/reader3/getBookGroups").coroutineHandler { bookController.getBookGroups(it) }
router.post("/reader3/saveBookGroup").coroutineHandler { bookController.saveBookGroup(it) }
router.post("/reader3/deleteBookGroup").coroutineHandler { bookController.deleteBookGroup(it) }
router.post("/reader3/saveBookGroupOrder").coroutineHandler { bookController.saveBookGroupOrder(it) }
// 书仓功能
// 获取书仓文件列表
router.get("/reader3/getLocalStoreFileList").coroutineHandler { bookController.getLocalStoreFileList(it) }
// 下载书仓文件
router.get("/reader3/getLocalStoreFile").coroutineHandlerWithoutRes { bookController.getLocalStoreFile(it) }
// 删除书仓文件
router.post("/reader3/deleteLocalStoreFile").coroutineHandler { bookController.deleteLocalStoreFile(it) }
router.post("/reader3/deleteLocalStoreFileList").coroutineHandler { bookController.deleteLocalStoreFileList(it) }
// 从本地书仓/webdav导入
router.post("/reader3/importFromLocalPathPreview").coroutineHandler { bookController.importFromLocalPathPreview(it) }
// 上传文件到书仓
router.post("/reader3/uploadFileToLocalStore").coroutineHandler { bookController.uploadFileToLocalStore(it) }
// 调试书源
router.get("/reader3/bookSourceDebugSSE").coroutineHandlerWithoutRes { bookController.bookSourceDebugSSE(it) }
// 缓存书籍章节
router.get("/reader3/cacheBookSSE").coroutineHandlerWithoutRes { bookController.cacheBookSSE(it) }
// 获取书籍缓存信息
router.get("/reader3/getShelfBookWithCacheInfo").coroutineHandler { bookController.getShelfBookWithCacheInfo(it) }
// 删除书籍章节缓存
router.post("/reader3/deleteBookCache").coroutineHandler { bookController.deleteBookCache(it) }
// 导出书籍
router.post("/reader3/exportBook").coroutineHandlerWithoutRes { bookController.exportBook(it) }
router.get("/reader3/exportBook").coroutineHandlerWithoutRes { bookController.exportBook(it) }
// 全文搜索
router.get("/reader3/searchBookContent").coroutineHandler { bookController.searchBookContent(it) }
router.post("/reader3/searchBookContent").coroutineHandler { bookController.searchBookContent(it) }
/** 用户模块 */
// 上传文件
router.post("/reader3/uploadFile").coroutineHandler { userController.uploadFile(it) }
// 删除文件
router.post("/reader3/deleteFile").coroutineHandler { userController.deleteFile(it) }
// 登录
router.post("/reader3/login").coroutineHandler { userController.login(it) }
// 注销登录
router.post("/reader3/logout").coroutineHandler { userController.logout(it) }
// 获取用户信息
router.get("/reader3/getUserInfo").coroutineHandler { userController.getUserInfo(it) }
// 用户备份本地配置
router.post("/reader3/saveUserConfig").coroutineHandler { userController.saveUserConfig(it) }
// 用户恢复本地配置
router.get("/reader3/getUserConfig").coroutineHandler { userController.getUserConfig(it) }
// 获取用户列表
router.get("/reader3/getUserList").coroutineHandler { userController.getUserList(it) }
// 删除用户
router.post("/reader3/deleteUsers").coroutineHandler { userController.deleteUsers(it) }
// 添加用户
router.post("/reader3/addUser").coroutineHandler { userController.addUser(it) }
// 重置用户密码
router.post("/reader3/resetPassword").coroutineHandler { userController.resetPassword(it) }
// 更新用户
router.post("/reader3/updateUser").coroutineHandler { userController.updateUser(it) }
/** webdav模块 */
// 获取webdav备份列表
router.get("/reader3/getWebdavFileList").coroutineHandler { webdavController.getWebdavFileList(it) }
// 下载webdav文件
router.get("/reader3/getWebdavFile").coroutineHandlerWithoutRes { webdavController.getWebdavFile(it) }
// 上传webdav文件
router.post("/reader3/uploadFileToWebdav").coroutineHandler { webdavController.uploadFileToWebdav(it) }
// 删除webdav文件
router.get("/reader3/deleteWebdavFile").coroutineHandler { webdavController.deleteWebdavFile(it) }
router.post("/reader3/deleteWebdavFile").coroutineHandler { webdavController.deleteWebdavFile(it) }
router.post("/reader3/deleteWebdavFileList").coroutineHandler { webdavController.deleteWebdavFileList(it) }
// 从webdav备份恢复
router.post("/reader3/restoreFromWebdav").coroutineHandler { webdavController.restoreFromWebdav(it) }
// 备份到webdav
router.post("/reader3/backupToWebdav").coroutineHandler { webdavController.backupToWebdav(it) }
/** rss模块 */
// rss
router.get("/reader3/getRssSources").coroutineHandler { rssSourceController.getRssSources(it) }
router.post("/reader3/saveRssSource").coroutineHandler { rssSourceController.saveRssSource(it) }
router.post("/reader3/saveRssSources").coroutineHandler { rssSourceController.saveRssSources(it) }
router.post("/reader3/deleteRssSource").coroutineHandler { rssSourceController.deleteRssSource(it) }
// rss 列表
router.get("/reader3/getRssArticles").coroutineHandler { rssSourceController.getRssArticles(it) }
router.post("/reader3/getRssArticles").coroutineHandler { rssSourceController.getRssArticles(it) }
// rss 内容
router.get("/reader3/getRssContent").coroutineHandler { rssSourceController.getRssContent(it) }
router.post("/reader3/getRssContent").coroutineHandler { rssSourceController.getRssContent(it) }
/** 替换规则模块 */
router.get("/reader3/getReplaceRules").coroutineHandler { replaceRuleController.getReplaceRules(it) }
router.post("/reader3/saveReplaceRule").coroutineHandler { replaceRuleController.saveReplaceRule(it) }
router.post("/reader3/saveReplaceRules").coroutineHandler { replaceRuleController.saveReplaceRules(it) }
router.post("/reader3/deleteReplaceRule").coroutineHandler { replaceRuleController.deleteReplaceRule(it) }
router.post("/reader3/deleteReplaceRules").coroutineHandler { replaceRuleController.deleteReplaceRules(it) }
/** 书签模块 */
router.get("/reader3/getBookmarks").coroutineHandler { bookmarkController.getBookmarks(it) }
router.post("/reader3/saveBookmark").coroutineHandler { bookmarkController.saveBookmark(it) }
router.post("/reader3/saveBookmarks").coroutineHandler { bookmarkController.saveBookmarks(it) }
router.post("/reader3/deleteBookmark").coroutineHandler { bookmarkController.deleteBookmark(it) }
router.post("/reader3/deleteBookmarks").coroutineHandler { bookmarkController.deleteBookmarks(it) }
}
suspend fun setupPort() {
logger.info("port: {}", port)
var serverPort = env.getProperty("reader.server.port", Int::class.java)
logger.info("serverPort: {}", serverPort)
if (serverPort != null && serverPort > 0) {
port = serverPort;
}
}
suspend fun migration() {
try {
var storageDir = File(getWorkDir("storage"))
var dataDir = File(getWorkDir("storage", "data", "default"))
if (!storageDir.exists()) {
// 直接使用新版本,则创建 default 目录,防止重启之后被迁移
dataDir.mkdirs()
} else if (!dataDir.exists()) {
// 旧版本不管了
dataDir.mkdirs()
// 可能存在旧版本,尝试迁移
// var backupDir = File(getWorkDir("storage-backup"))
// storageDir.renameTo(backupDir)
// dataDir.parentFile.mkdirs()
// backupDir.copyRecursively(dataDir)
}
} catch(e: Exception) {
e.printStackTrace()
}
}
override fun started() {
SpringContextUtils.getApplicationContext().publishEvent(SpringEvent(this as java.lang.Object, "READY", ""));
}
override fun onStartError() {
SpringContextUtils.getApplicationContext().publishEvent(SpringEvent(this as java.lang.Object, "START_ERROR", "应用启动失败,请检查" + port + "端口是否被占用"));
}
override fun onHandlerError(ctx: RoutingContext, error: Exception) {
val returnData = ReturnData()
logger.error("onHandlerError: ", error)
if (!ctx.response().headWritten()) {
ctx.success(returnData.setErrorMsg(error.toString()))
} else {
ctx.response().end(error.toString())
}
}
private suspend fun getSystemInfo(context: RoutingContext): ReturnData {
val returnData = ReturnData()
var systemFont = System.getProperty("reader.system.fonts")
var freeMemory = "" + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "M"
var totalMemory = "" + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "M"
var maxMemory = "" + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "M"
return returnData.setData(mapOf(
"fonts" to systemFont,
"freeMemory" to freeMemory,
"totalMemory" to totalMemory,
"maxMemory" to maxMemory
))
}
/**
* 定时任务
*/
/**
* 每十分钟检查一次书架书籍更新
*/
@Scheduled(cron = "0 0/10 * * * ?")
fun shelfUpdateJob()
{
launch(Dispatchers.IO) {
try {
val bookController = BookController(coroutineContext)
logger.info("开始检查书架书籍更新")
// 刷新系统默认书架
bookController.getBookShelfBooks(true, "default")
// 刷新用户书架
if (appConfig.secure) {
var userMap = mutableMapOf<String, Map<String, Any>>()
var userMapJson: JsonObject? = asJsonObject(getStorage("data", "users"))
if (userMapJson != null) {
userMap = userMapJson.map as MutableMap<String, Map<String, Any>>
}
userMap.forEach{
try {
var ns = it.value.getOrDefault("username", "") as String? ?: ""
if (ns.isNotEmpty()) {
bookController.getBookShelfBooks(true, ns)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
logger.info("书架书籍更新检查结束")
} catch (e: Exception) {
e.printStackTrace()
}
}
}
/**
* 每天清理不活跃用户
*/
@Scheduled(cron = "0 59 23 * * ?")
fun clearUser()
{
if (appConfig.autoClearInactiveUser <= 0 || !appConfig.secure) {
return
}
launch(Dispatchers.IO) {
try {
logger.info("开始清理 {} 天未登录用户", appConfig.autoClearInactiveUser)
var userMap = mutableMapOf<String, Map<String, Any>>()
var userMapJson: JsonObject? = asJsonObject(getStorage("data", "users"))
if (userMapJson != null) {
userMap = userMapJson.map as MutableMap<String, Map<String, Any>>
}
val expireTime = System.currentTimeMillis() - appConfig.autoClearInactiveUser * 86400L * 1000L
userMap.keys.forEach{
try {
var user = userMap.get(it)
if (user != null) {
var username = user.getOrDefault("username", "") as String? ?: ""
var last_login_at = user.getOrDefault("last_login_at", 0) as Long? ?: 0L
if (username.isNotEmpty() && last_login_at < expireTime) {
logger.info("delete user: {}", user)
// 删除用户信息
userMap.remove(username)
// 移除用户目录
var userHome = File(getWorkDir("storage", "data", username))
logger.info("delete userHome: {}", userHome)
if (userHome.exists()) {
userHome.deleteRecursively()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
logger.info("不活跃用户自动清理结束")
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
================================================
FILE: src/main/java/com/htmake/reader/api/controller/BaseController.kt
================================================
package com.htmake.reader.api.controller
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.BookGroup
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.RssArticle
import io.legado.app.model.webBook.WebBook
import io.vertx.ext.web.Route
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.StaticHandler;
import mu.KotlinLogging
import com.htmake.reader.config.AppConfig
import com.htmake.reader.config.BookConfig
import io.legado.app.constant.DeepinkBookSource
import com.htmake.reader.utils.error
import com.htmake.reader.utils.success
import com.htmake.reader.utils.getStorage
import com.htmake.reader.utils.saveStorage
import com.htmake.reader.utils.asJsonArray
import com.htmake.reader.utils.asJsonObject
import com.htmake.reader.utils.toDataClass
import com.htmake.reader.utils.toMap
import com.htmake.reader.utils.fillData
import com.htmake.reader.utils.getWorkDir
import com.htmake.reader.utils.getRandomString
import com.htmake.reader.utils.genEncryptedPassword
import com.htmake.reader.entity.User
import com.htmake.reader.utils.SpringContextUtils
import com.htmake.reader.utils.deleteRecursively
import com.htmake.reader.utils.unzip
import com.htmake.reader.utils.zip
import com.htmake.reader.utils.jsonEncode
import com.htmake.reader.utils.getRelativePath
import com.htmake.reader.utils.getFileExtetion
import com.htmake.reader.verticle.RestVerticle
import com.htmake.reader.SpringEvent
import org.springframework.stereotype.Component
import io.vertx.core.json.JsonObject
import io.vertx.core.json.JsonArray
import io.vertx.core.http.HttpMethod
import com.htmake.reader.api.ReturnData
import io.legado.app.utils.MD5Utils
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.URL;
import java.util.UUID;
import io.vertx.ext.web.client.WebClient
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.core.env.Environment
import java.io.File
import java.lang.Runtime
import kotlin.collections.mutableMapOf
import kotlin.system.measureTimeMillis
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat;
import io.legado.app.utils.EncoderUtils
import io.legado.app.model.rss.Rss
import org.springframework.scheduling.annotation.Scheduled
import io.legado.app.model.localBook.LocalBook
import java.nio.file.Paths
import kotlinx.coroutines.withContext
import kotlinx.coroutines.async
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import io.legado.app.help.coroutine.Coroutine
private val logger = KotlinLogging.logger {}
open class BaseController(override val coroutineContext: CoroutineContext): CoroutineScope {
var loginExpireDays = 7
val appConfig: AppConfig
val env: Environment
init {
appConfig = SpringContextUtils.getBean("appConfig", AppConfig::class.java)
env = SpringContextUtils.getBean(Environment::class.java)
}
suspend fun saveUserSession(context: RoutingContext, userMap: MutableMap<String, Map<String, Any>>, user: User, regenerateToken: Boolean = true): Map<String, Any> {
user.last_login_at = System.currentTimeMillis()
if (regenerateToken) {
user.token = genEncryptedPassword(user.username, System.currentTimeMillis().toString())
var tokenMap: MutableMap<String, Long>? = null
var expire = System.currentTimeMillis() + loginExpireDays * 86400 * 1000
if (user.token_map != null) {
tokenMap = user.token_map as? MutableMap<String, Long>
}
if (tokenMap == null) {
tokenMap = mutableMapOf(user.token to expire)
} else {
tokenMap.put(user.token, expire)
}
// 删除已过期token
tokenMap.values.removeAll { it < user.last_login_at }
user.token_map = tokenMap
}
userMap.put(user.username, user.toMap())
saveStorage("data", "users", value = userMap)
val loginData = formatUser(user)
context.session().put("username", user.username)
context.put("username", user.username)
return loginData
}
suspend fun checkAuth(context: RoutingContext): Boolean {
if (!appConfig.secure) {
return true
}
var username = context.session().get("username") as String? ?: ""
var userInfo = getUserInfoClass(username)
if (userInfo != null) {
context.put("username", userInfo.username)
context.put("userInfo", userInfo)
return true
}
// 自动登录
var accessToken = context.queryParam("accessToken").firstOrNull() ?: ""
if (accessToken.isNotEmpty()) {
var userMap = mutableMapOf<String, Map<String, Any>>()
var userMapJson: JsonObject? = asJsonObject(getStorage("data", "users"))
if (userMapJson != null) {
userMap = userMapJson.map as? MutableMap<String, Map<String, Any>> ?: mutableMapOf<String, Map<String, Any>>()
}
var tmp = accessToken.split(":", limit=2)
if (tmp.size >= 2) {
var _username = tmp[0]
var token = tmp[1]
var existedUser: User? = userMap.getOrDefault(_username, null)?.toDataClass()
if (existedUser != null && token.isNotEmpty()) {
var isLogin = false
if (existedUser.token.isNotEmpty() && existedUser.token.equals(token)) {
isLogin = true
}
// 查找历史有效会话
if (!isLogin && existedUser.token_map != null) {
var tokenMap = existedUser.token_map as? MutableMap<String, Long>
if (tokenMap != null &&
tokenMap.containsKey(token)) {
if (tokenMap.getOrDefault(token, 0L) > System.currentTimeMillis()) {
isLogin = true
// 延长有效期
tokenMap.put(token, System.currentTimeMillis() + loginExpireDays * 86400 * 1000)
} else {
// 删除过期token
tokenMap.remove(token)
}
existedUser.token_map = tokenMap
}
}
if (isLogin) {
// 保存用户session
saveUserSession(context, userMap, existedUser, false)
context.put("username", existedUser.username)
context.put("userInfo", existedUser)
}
return isLogin
}
}
}
return false
}
fun checkManagerAuth(context: RoutingContext): Boolean {
if (!appConfig.secure) {
return true
}
if (appConfig.secureKey.isEmpty()) {
return true
}
var secureKey = context.queryParam("secureKey").firstOrNull() ?: ""
if (secureKey.equals(appConfig.secureKey)) {
// 判断是否需要修改 userNameSpace
var userNS = context.queryParam("userNS").firstOrNull()
if (userNS != null && userNS.isNotEmpty()) {
context.put("userNameSpace", userNS)
}
return true
}
return false
}
fun getUserNameSpace(context: RoutingContext): String {
if (!appConfig.secure) {
return "default"
}
// 管理权限,可以修改 userNameSpace 来获取任意用户信息
checkManagerAuth(context)
var userNS = context.get("userNameSpace") as String?
if (userNS != null && userNS.isNotEmpty()) {
return userNS
}
var username = context.get("username") as String?
if (username != null) {
return username;
}
return "default"
}
fun getUserStorage(context: Any, vararg path: String): String? {
var userNameSpace = ""
when(context) {
is RoutingContext -> userNameSpace = getUserNameSpace(context)
is String -> userNameSpace = context
}
if (userNameSpace.isEmpty()) {
return getStorage("data", *path)
}
return getStorage("data", userNameSpace, *path)
}
fun saveUserStorage(context: Any, path: String, value: Any) {
var userNameSpace = ""
when(context) {
is RoutingContext -> userNameSpace = getUserNameSpace(context)
is String -> userNameSpace = context
}
if (userNameSpace.isEmpty()) {
return saveStorage("data", path, value = value)
}
return saveStorage("data", userNameSpace, path, value = value)
}
fun getUserInfoClass(username: String): User? {
var user: User? = getUserInfoMap(username)?.toDataClass()
return user
}
fun getUserInfoMap(username: String): Map<String, Any>? {
if (username.isEmpty()) {
return null
}
var userMap = mutableMapOf<String, Map<String, Any>>()
var userMapJson: JsonObject? = asJsonObject(getStorage("data", "users"))
if (userMapJson != null) {
userMap = userMapJson.map as MutableMap<String, Map<String, Any>>
}
return userMap.getOrDefault(username, null)
}
fun formatUser(userInfo: Any): MutableMap<String, Any> {
var user: User? = null
if (userInfo !is User) {
var userMap = userInfo as? Map<String, Any>
if (userMap != null) {
user = userMap.toDataClass()
}
} else {
user = userInfo
}
if (user == null) {
return mutableMapOf()
}
return mutableMapOf(
"username" to user.username,
"lastLoginAt" to user.last_login_at,
"accessToken" to user.username + ":" + user.token,
"enableWebdav" to user.enable_webdav,
"enableLocalStore" to user.enable_local_store,
"createdAt" to user.created_at
)
}
fun getUserWebdavHome(context: Any): String {
var prefix = getWorkDir("storage", "data")
var userNameSpace = ""
when(context) {
is RoutingContext -> userNameSpace = getUserNameSpace(context)
is String -> userNameSpace = context
}
if (userNameSpace.isNotEmpty()) {
prefix = prefix + File.separator + userNameSpace
}
prefix = prefix + File.separator + "webdav"
var file = File(prefix)
if (!file.exists()) {
file.mkdirs()
}
return prefix
}
fun getFileExt(url: String, defaultExt: String=""): String {
return getFileExtetion(url, defaultExt)
}
suspend fun limitConcurrent(concurrentCount: Int, startIndex: Int, endIndex: Int, handler: suspend CoroutineScope.(Int) -> Any) {
limitConcurrent(concurrentCount, startIndex, endIndex, handler) {_, _ ->
true
}
}
suspend fun limitConcurrent(concurrentCount: Int, startIndex: Int, endIndex: Int, handler: suspend CoroutineScope.(Int) -> Any, needContinue: (ArrayList<Any>, Int) -> Boolean) {
var lastIndex = startIndex
var loopCount = 0
var resultCount = 0
var loopStart = System.currentTimeMillis()
var costTime = 0L
var deferredList = arrayListOf<Deferred<Any>>()
while(true) {
var croutineCount = deferredList.size;
if (croutineCount < concurrentCount) {
for(i in lastIndex until endIndex) {
croutineCount += 1;
deferredList.add(async {
handler(i)
})
lastIndex = i
if (croutineCount >= concurrentCount) {
break;
}
}
}
var resultList = arrayListOf<Any>()
// 等待任何一个完成
while (resultList.size <= 0) {
delay(10)
var stillDeferredList = arrayListOf<Deferred<Any>>()
for (i in 0 until deferredList.size) {
try {
var deferred = deferredList.get(i)
if (deferred.isCompleted) {
resultCount++
resultList.add(deferred.getCompleted())
} else if (!deferred.isCancelled) {
stillDeferredList.add(deferred)
} else {
resultCount++
}
} catch(e: Exception) {
}
}
deferredList.clear()
deferredList.addAll(stillDeferredList)
}
if (resultCount / concurrentCount > loopCount) {
loopCount = resultCount / concurrentCount
costTime = System.currentTimeMillis() - loopStart
logger.info("Loop: {} concurrentCount: {} lastIndex: {} endIndex: {} costTime: {} ms deferredList size: {}", loopCount, croutineCount, lastIndex, endIndex, costTime, deferredList.size)
}
if (lastIndex >= endIndex - 1) {
// 搞完了,等待所有结束
for (i in 0 until deferredList.size) {
try {
resultList.add(deferredList.get(i).await())
} catch(e: Exception) {
}
}
deferredList.clear()
needContinue(resultList, loopCount)
break;
}
if (resultList.size > 0) {
if (!needContinue(resultList, loopCount)) {
break;
}
}
lastIndex = lastIndex + 1
}
// for (i in 0 until concurrentCount) {
// runBlocking(concurrentCount, startIndex + i , endIndex, handler, needContinue)
// }
}
suspend fun runBlocking(concurrentCount: Int, startIndex: Int, endIndex: Int, handler: suspend CoroutineScope.(Int) -> Any, needContinue: (ArrayList<Any>, Int) -> Boolean) {
var lastIndex = startIndex
Coroutine.async(this, coroutineContext) {
handler(lastIndex)
}.timeout(30000L)
.onSuccess(Dispatchers.IO) {
if (lastIndex < endIndex - concurrentCount && needContinue(arrayListOf(it), 0)) {
lastIndex += concurrentCount
runBlocking(concurrentCount, lastIndex, endIndex, handler, needContinue)
}
}
.onError(Dispatchers.IO) {
if (lastIndex < endIndex - concurrentCount) {
lastIndex += concurrentCount
runBlocking(concurrentCount, lastIndex, endIndex, handler, needContinue)
} else {
needContinue(arrayListOf(), 0)
}
}
}
}
================================================
FILE: src/main/java/com/htmake/reader/api/controller/BookController.kt
================================================
package com.htmake.reader.api.controller
import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.BookGroup
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.SearchResult
import io.legado.app.exception.TocEmptyException
import io.legado.app.model.webBook.WebBook
import io.legado.app.help.DefaultData
import io.vertx.ext.web.Route
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.StaticHandler;
import mu.KotlinLogging
import com.htmake.reader.config.AppConfig
import com.htmake.reader.config.BookConfig
import io.legado.app.constant.DeepinkBookSource
import com.htmake.reader.utils.error
import com.htmake.reader.utils.success
import com.htmake.reader.utils.getStorage
import com.htmake.reader.utils.saveStorage
import com.htmake.reader.utils.asJsonArray
import com.htmake.reader.utils.asJsonObject
import com.htmake.reader.utils.toDataClass
import com.htmake.reader.utils.toMap
import com.htmake.reader.utils.fillData
import com.htmake.reader.utils.getWorkDir
import com.htmake.reader.utils.getRandomString
import com.htmake.reader.utils.genEncryptedPassword
import com.htmake.reader.entity.User
import com.htmake.reader.utils.SpringContextUtils
import com.htmake.reader.utils.deleteRecursively
import com.htmake.reader.utils.unzip
import com.htmake.reader.utils.zip
import com.htmake.reader.utils.jsonEncode
import com.htmake.reader.utils.getRelativePath
import com.htmake.reader.verticle.RestVerticle
import com.htmake.reader.SpringEvent
import org.springframework.stereotype.Component
import io.vertx.core.json.JsonObject
import io.vertx.core.json.JsonArray
import io.vertx.core.http.HttpMethod
import com.htmake.reader.api.ReturnData
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.FileUtils
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.URL;
import java.nio.charset.Charset
import java.util.UUID;
import io.vertx.ext.web.client.WebClient
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.core.env.Environment
import java.io.File
import java.io.FileOutputStream
import java.lang.Runtime
import kotlin.collections.mutableMapOf
import kotlin.system.measureTimeMillis
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat;
import io.legado.app.utils.EncoderUtils
import io.legado.app.utils.ACache
import io.legado.app.utils.HtmlFormatter
import io.legado.app.utils.NetworkUtils
import io.legado.app.model.rss.Rss
import io.legado.app.model.Debugger
import io.legado.app.help.BookHelp
import org.springframework.scheduling.annotation.Scheduled
import io.legado.app.model.localBook.LocalBook
import io.legado.app.model.analyzeRule.AnalyzeUrl
import java.nio.file.Paths
import kotlinx.coroutines.withContext
import kotlinx.coroutines.async
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.CoroutineScope
import me.ag2s.epublib.domain.*
import me.ag2s.epublib.epub.EpubWriter
import me.ag2s.epublib.util.ResourceUtil
// import io.legado.app.help.coroutine.Coroutine
private val logger = KotlinLogging.logger {}
class BookController(coroutineContext: CoroutineContext): BaseController(coroutineContext) {
var bookInfoCache = ACache.get("bookInfoCache", 1000 * 1000 * 2L, 10000) // 缓存 2M 的书籍信息
val concurrentLoopCount = 8
private var webClient: WebClient
init {
webClient = SpringContextUtils.getBean("webClient", WebClient::class.java)
}
private fun getInvalidBookSourceCache(userNameSpace: String): ACache {
val cacheDir = File(getWorkDir("storage", "cache", "invalidBookSourceCache", userNameSpace))
// 缓存 5M 的失效书源信息
var invalidBookSourceCache = ACache.get(cacheDir, 1000 * 1000 * 5L, 1000000)
return invalidBookSourceCache
}
private fun isInvalidBookSource(bookSource: BookSource, userNameSpace: String): Boolean {
return getInvalidBookSourceCache(userNameSpace).getAsString(bookSource.bookSourceUrl) != null
}
private fun addInvalidBookSource(sourceUrl: String, invalidInfo: Map<String, Any>, userNameSpace: String) {
// 保存600秒时间
getInvalidBookSourceCache(userNameSpace).put(sourceUrl, jsonEncode(invalidInfo), 600)
}
suspend fun getInvalidBookSources(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
var userNameSpace = getUserNameSpace(context)
val invalidBookSourceCache = getInvalidBookSourceCache(userNameSpace)
val cacheDir = File(getWorkDir("storage", "cache", "invalidBookSourceCache", userNameSpace))
val files = cacheDir.listFiles()
val invalidBookSourceList = arrayListOf<Map<String, Any>>()
if (files != null) {
for (f in files) {
invalidBookSourceCache.getByHashCode(f.name)?.let { info ->
invalidBookSourceList.add(info.toMap())
}
}
}
return returnData.setData(invalidBookSourceList)
}
suspend fun getBookInfo(context: RoutingContext): ReturnData {
val returnData = ReturnData()
var bookUrl: String
if (context.request().method() == HttpMethod.POST) {
// post 请求
bookUrl = context.bodyAsJson.getString("url") ?: context.bodyAsJson.getJsonObject("searchBook").getString("bookUrl") ?: ""
} else {
// get 请求
bookUrl = context.queryParam("url").firstOrNull() ?: ""
}
if (bookUrl.isNullOrEmpty()) {
return returnData.setErrorMsg("请输入书籍链接")
}
logger.info("getBookInfo with bookUrl: {}", bookUrl)
var bookInfo: Book? = null
if (checkAuth(context)) {
bookInfo = getShelfBookByURL(bookUrl, getUserNameSpace(context))
}
if (bookInfo == null) {
// 看看有没有缓存数据
var bookSource: String? = null
var cacheInfo: Book? = bookInfoCache.getAsString(bookUrl)?.toMap()?.toDataClass()
if (cacheInfo != null) {
// 使用缓存的书籍信息包含的书源
bookSource = getBookSourceString(context, cacheInfo.origin)
} else {
bookSource = getBookSourceString(context)
}
if (bookSource.isNullOrEmpty()) {
return returnData.setErrorMsg("未配置书源")
}
bookInfo = mergeBookCacheInfo(WebBook(bookSource, appConfig.debugLog).getBookInfo(bookUrl))
}
// 缓存书籍信息
saveBookInfoCache(arrayListOf<Book>(bookInfo))
return returnData.setData(bookInfo)
}
suspend fun getBookCover(context: RoutingContext) {
var coverUrl = context.queryParam("path").firstOrNull() ?: ""
if (coverUrl.isNullOrEmpty()) {
context.response().setStatusCode(404).end()
return
}
var ext = getFileExt(coverUrl, "png")
val md5Encode = MD5Utils.md5Encode(coverUrl).toString()
var cachePath = getWorkDir("storage", "cache", md5Encode + "." + ext)
var cacheFile = File(cachePath)
if (cacheFile.exists()) {
logger.info("send cache: {}", cacheFile)
context.response().putHeader("Cache-Control", "86400").sendFile(cacheFile.toString())
return;
}
if (!cacheFile.parentFile.exists()) {
cacheFile.parentFile.mkdirs()
}
launch(Dispatchers.IO) {
webClient.getAbs(coverUrl).timeout(3000).send {
var bodyBytes = it.result()?.bodyAsBuffer()?.getBytes()
if (bodyBytes != null) {
var res = context.response().putHeader("Cache-Control", "86400")
cacheFile.writeBytes(bodyBytes)
res.sendFile(cacheFile.toString())
} else {
context.response().setStatusCode(404).end()
}
}
}
}
suspend fun importBookPreview(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
if (context.fileUploads() == null || context.fileUploads().isEmpty()) {
return returnData.setErrorMsg("请上传书籍文件")
}
var userNameSpace = getUserNameSpace(context)
var fileList = arrayListOf<Map<String, Any>>()
context.fileUploads().forEach {
var file = File(it.uploadedFileName())
logger.info("uploadFile: {} {} {}", it.uploadedFileName(), it.fileName(), file)
if (file.exists()) {
var fileName = it.fileName()
val ext = getFileExt(fileName)
if (ext != "txt" && ext != "epub" && ext != "umd" && ext != "cbz") {
file.deleteRecursively()
return returnData.setErrorMsg("不支持导入" + ext + "格式的书籍文件")
}
// 文件名格式化
fileName = FileUtils.getNameExcludeExtension(fileName)
fileName = fileName.replace(AppPattern.fileNameRegex, "")
fileName = fileName.substring(0, Math.min(50, fileName.length)) + "." + ext
val localFilePath = Paths.get("storage", "assets", userNameSpace, "book", fileName).toString()
val localFileUrl = "/assets/" + userNameSpace + "/book/" + fileName
var filePath = localFilePath
if (fileName.endsWith(".epub", true)) {
filePath = filePath + File.separator + "index.epub"
}
if (fileName.endsWith(".cbz", true)) {
filePath = filePath + File.separator + "index.cbz"
}
var newFile = File(getWorkDir(filePath))
if (!newFile.parentFile.exists()) {
newFile.parentFile.mkdirs()
}
if (newFile.exists()) {
newFile.delete()
}
logger.info("moveTo: {}", newFile)
if (file.copyRecursively(newFile)) {
val book = Book.initLocalBook(localFileUrl, localFilePath, getWorkDir())
book.setUserNameSpace(userNameSpace)
try {
val chapters = LocalBook.getChapterList(book)
fileList.add(mapOf("book" to book, "chapters" to chapters))
} catch(e: TocEmptyException) {
fileList.add(mapOf("book" to book, "chapters" to arrayListOf<Int>()))
}
}
file.deleteRecursively()
}
}
return returnData.setData(fileList)
}
suspend fun getTxtTocRules(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
return returnData.setData(DefaultData.txtTocRules)
}
suspend fun getChapterListByRule(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
var book = context.bodyAsJson.mapTo(Book::class.java)
if (book.origin.isNullOrEmpty()) {
return returnData.setErrorMsg("未找到书源信息")
}
if (!book.isLocalTxt() && !book.isLocalEpub()) {
return returnData.setErrorMsg("非本地txt/epub书籍")
}
book.setRootDir(getWorkDir())
book.setUserNameSpace(getUserNameSpace(context))
val chapters = LocalBook.getChapterList(book)
return returnData.setData(mapOf("book" to book, "chapters" to chapters))
}
suspend fun refreshLocalBook(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
var bookUrl: String
if (context.request().method() == HttpMethod.POST) {
// post 请求
bookUrl = context.bodyAsJson.getString("bookUrl")
} else {
// get 请求
bookUrl = context.queryParam("bookUrl").firstOrNull() ?: ""
}
if (bookUrl.isNullOrEmpty()) {
return returnData.setErrorMsg("请输入书籍链接")
}
// 根据书籍url获取书本信息
var userNameSpace = getUserNameSpace(context)
var bookInfo = getShelfBookByURL(bookUrl, userNameSpace)
if (bookInfo == null) {
return returnData.setErrorMsg("书籍信息错误")
}
bookInfo.updateFromLocal(true)
editShelfBook(bookInfo, userNameSpace) { existBook ->
existBook.coverUrl = bookInfo.coverUrl
logger.info("refreshLocalBook: {}", existBook)
existBook
}
return returnData.setData(bookInfo)
}
suspend fun getChapterList(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
var bookUrl: String
var refresh: Int = 0
if (context.request().method() == HttpMethod.POST) {
// post 请求
bookUrl = context.bodyAsJson.getString("url") ?: context.bodyAsJson.getJsonObject("book").getString("bookUrl") ?: ""
refresh = context.bodyAsJson.getInteger("refresh", 0)
} else {
// get 请求
bookUrl = context.queryParam("url").firstOrNull() ?: ""
refresh = context.queryParam("refresh").firstOrNull()?.toInt() ?: 0
}
if (bookUrl.isNullOrEmpty()) {
return returnData.setErrorMsg("请输入书籍链接")
}
// 根据书籍url获取书本信息
var userNameSpace = getUserNameSpace(context)
var bookInfo = getShelfBookByURL(bookUrl, userNameSpace)
var bookSource: String? = null
if (bookInfo == null) {
// 看看有没有缓存数据
var cacheInfo: Book? = bookInfoCache.getAsString(bookUrl)?.toMap()?.toDataClass()
if (cacheInfo != null) {
// 使用缓存的书籍信息包含的书源
bookSource = getBookSourceString(context, cacheInfo.origin)
} else {
// 看看有没有传入书源
bookSource = getBookSourceString(context)
}
if (bookSource.isNullOrEmpty()) {
return returnData.setErrorMsg("未配置书源")
}
bookInfo = mergeBookCacheInfo(WebBook(bookSource, appConfig.debugLog).getBookInfo(bookUrl))
// 缓存书籍信息
saveBookInfoCache(arrayListOf<Book>(bookInfo))
} else {
bookSource = getBookSourceString(context, bookInfo.origin)
}
if (!bookInfo.isLocalBook() && bookSource.isNullOrEmpty()) {
return returnData.setErrorMsg("未配置书源")
}
bookInfo.setRootDir(getWorkDir())
bookInfo.setUserNameSpace(userNameSpace)
if (bookInfo.isLocalBook()) {
val localFile = bookInfo.getLocalFile()
if (!localFile.exists()) {
logger.info("localFile: {} not exists", localFile)
return returnData.setErrorMsg("本地书籍源文件不存在")
}
}
// 缓存章节列表
logger.info("bookInfo: {}", bookInfo)
var chapterList = getLocalChapterList(bookInfo, bookSource ?: "", refresh > 0, getUserNameSpace(context))
return returnData.setData(chapterList)
}
suspend fun saveBookProgress(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
var bookUrl: String
var chapterIndex: Int
if (context.request().method() == HttpMethod.POST) {
// post 请求
bookUrl = context.bodyAsJson.getString("url") ?: context.bodyAsJson.getJsonObject("searchBook").getString("bookUrl") ?: ""
chapterIndex = context.bodyAsJson.getInteger("index", -1)
} else {
// get 请求
bookUrl = context.queryParam("url").firstOrNull() ?: ""
chapterIndex = context.queryParam("index").firstOrNull()?.toInt() ?: -1
}
if (bookUrl.isNullOrEmpty()) {
return returnData.setErrorMsg("请输入书籍链接")
}
var userNameSpace = getUserNameSpace(context)
// 看看有没有加入书架
var bookInfo = getShelfBookByURL(bookUrl, userNameSpace)
if (bookInfo == null || bookInfo.origin.isNullOrEmpty()) {
return returnData.setErrorMsg("书籍未加入书架")
}
var bookSource = getBookSourceStringBySourceURL(bookInfo.origin, userNameSpace)
if (!bookInfo.isLocalBook() && bookSource.isNullOrEmpty()) {
return returnData.setErrorMsg("未配置书源")
}
var chapterList = getLocalChapterList(bookInfo, bookSource ?: "", false, userNameSpace)
if (chapterIndex >= chapterList.size) {
return returnData.setErrorMsg("章节不存在")
}
var chapterInfo = chapterList.get(chapterIndex)
// 书架书籍保存阅读进度
saveShelfBookProgress(bookInfo, chapterInfo, userNameSpace)
// 保存到 webdav
saveBookProgressToWebdav(bookInfo, chapterInfo, userNameSpace)
return returnData.setData("")
}
suspend fun getBookContent(context: RoutingContext): ReturnData {
val returnData = ReturnData()
if (!checkAuth(context)) {
return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用")
}
var chapterUrl: String
var bookUrl: String
var chapterIndex: Int
var cache: Int
var refresh: Int
if (context.request().method() == HttpMethod.POST) {
// post 请求
chapterUrl = context.bodyAsJson.getString("chapterUrl") ?: context.bodyAsJson.getJsonObject("bookChapter")?.getString("url") ?: ""
bookUrl = context.bodyAsJson.getString("url") ?: context.bodyAsJson.getJsonObject("searchBook")?.getString("bookUrl") ?: ""
chapterIndex = context.bodyAsJson.getInte
gitextract_mc5_awkk/
├── .dockerignore
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug-report--问题反馈-.md
│ └── workflows/
│ ├── Dockerfile
│ ├── Openj9-Dockerfile
│ ├── build.yml
│ ├── pull-request.yml
│ ├── release-github.yml
│ ├── release-openj9.yml
│ └── release.yml
├── .gitignore
├── Dockerfile
├── Dockerfile.source
├── LICENSE
├── README.md
├── UPDATELOG.md
├── assets/
│ └── mac/
│ └── reader.icns
├── build.gradle.kts
├── build.sh
├── cli.gradle
├── doc.md
├── docker-compose.yaml
├── docker-compose.yml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── nixpacks.toml
├── preview.md
├── reader.sh
├── server/
│ ├── bin/
│ │ ├── shutdown.cmd
│ │ ├── shutdown.sh
│ │ ├── startup.cmd
│ │ └── startup.sh
│ └── conf/
│ └── application.properties
├── settings.gradle
├── src/
│ ├── lib/
│ │ ├── rhino-1.7.13-1.jar
│ │ └── xmlpull-1.1.3.1.jar
│ ├── main/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ └── htmake/
│ │ │ │ └── reader/
│ │ │ │ ├── ReaderApplication.kt
│ │ │ │ ├── ReaderUIApplication.kt
│ │ │ │ ├── SpringEvent.java
│ │ │ │ ├── api/
│ │ │ │ │ ├── ReturnData.kt
│ │ │ │ │ ├── YueduApi.kt
│ │ │ │ │ └── controller/
│ │ │ │ │ ├── BaseController.kt
│ │ │ │ │ ├── BookController.kt
│ │ │ │ │ ├── BookSourceController.kt
│ │ │ │ │ ├── BookmarkController.kt
│ │ │ │ │ ├── ReplaceRuleController.kt
│ │ │ │ │ ├── RssSourceController.kt
│ │ │ │ │ ├── UserController.kt
│ │ │ │ │ └── WebdavController.kt
│ │ │ │ ├── config/
│ │ │ │ │ ├── AppConfig.kt
│ │ │ │ │ └── BookConfig.kt
│ │ │ │ ├── entity/
│ │ │ │ │ ├── BasicError.kt
│ │ │ │ │ ├── Size.kt
│ │ │ │ │ └── User.kt
│ │ │ │ ├── init/
│ │ │ │ │ └── appCtx.kt
│ │ │ │ ├── utils/
│ │ │ │ │ ├── Ext.kt
│ │ │ │ │ ├── SpringContextUtils.java
│ │ │ │ │ └── VertExt.kt
│ │ │ │ └── verticle/
│ │ │ │ └── RestVerticle.kt
│ │ │ ├── io/
│ │ │ │ └── legado/
│ │ │ │ └── app/
│ │ │ │ ├── README.md
│ │ │ │ ├── constant/
│ │ │ │ │ ├── Action.kt
│ │ │ │ │ ├── AppConst.kt
│ │ │ │ │ ├── AppPattern.kt
│ │ │ │ │ ├── BookType.kt
│ │ │ │ │ ├── DeepinkBookSource.kt
│ │ │ │ │ ├── PreferKey.kt
│ │ │ │ │ ├── RSSKeywords.kt
│ │ │ │ │ └── Status.kt
│ │ │ │ ├── data/
│ │ │ │ │ └── entities/
│ │ │ │ │ ├── BaseBook.kt
│ │ │ │ │ ├── BaseSource.kt
│ │ │ │ │ ├── Book.kt
│ │ │ │ │ ├── BookChapter.kt
│ │ │ │ │ ├── BookGroup.kt
│ │ │ │ │ ├── BookSource.kt
│ │ │ │ │ ├── Bookmark.kt
│ │ │ │ │ ├── Cache.kt
│ │ │ │ │ ├── Cookie.kt
│ │ │ │ │ ├── ReplaceRule.kt
│ │ │ │ │ ├── RssArticle.kt
│ │ │ │ │ ├── RssSource.kt
│ │ │ │ │ ├── SearchBook.kt
│ │ │ │ │ ├── SearchKeyword.kt
│ │ │ │ │ ├── SearchResult.kt
│ │ │ │ │ ├── TxtTocRule.kt
│ │ │ │ │ └── rule/
│ │ │ │ │ ├── BookInfoRule.kt
│ │ │ │ │ ├── BookListRule.kt
│ │ │ │ │ ├── ContentRule.kt
│ │ │ │ │ ├── ExploreRule.kt
│ │ │ │ │ ├── SearchRule.kt
│ │ │ │ │ └── TocRule.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ConcurrentException.kt
│ │ │ │ │ ├── ContentEmptyException.kt
│ │ │ │ │ ├── NoStackTraceException.kt
│ │ │ │ │ ├── RegexTimeoutException.kt
│ │ │ │ │ └── TocEmptyException.kt
│ │ │ │ ├── help/
│ │ │ │ │ ├── BookHelp.kt
│ │ │ │ │ ├── CacheManager.kt
│ │ │ │ │ ├── DefaultData.kt
│ │ │ │ │ ├── EncodingDetectHelp.java
│ │ │ │ │ ├── JsExtensions.kt
│ │ │ │ │ ├── coroutine/
│ │ │ │ │ │ ├── CompositeCoroutine.kt
│ │ │ │ │ │ ├── Coroutine.kt
│ │ │ │ │ │ └── CoroutineContainer.kt
│ │ │ │ │ └── http/
│ │ │ │ │ ├── AjaxWebView.kt
│ │ │ │ │ ├── ByteConverter.kt
│ │ │ │ │ ├── CookieStore.kt
│ │ │ │ │ ├── CoroutinesCallAdapterFactory.kt
│ │ │ │ │ ├── EncodeConverter.kt
│ │ │ │ │ ├── HttpHelper.kt
│ │ │ │ │ ├── OkHttpUtils.kt
│ │ │ │ │ ├── RequestMethod.kt
│ │ │ │ │ ├── Res.kt
│ │ │ │ │ ├── SSLHelper.kt
│ │ │ │ │ ├── StrResponse.kt
│ │ │ │ │ └── api/
│ │ │ │ │ └── CookieManager.kt
│ │ │ │ ├── lib/
│ │ │ │ │ └── icu4j/
│ │ │ │ │ ├── CharsetDetector.java
│ │ │ │ │ ├── CharsetMatch.java
│ │ │ │ │ ├── CharsetRecog_2022.java
│ │ │ │ │ ├── CharsetRecog_UTF8.java
│ │ │ │ │ ├── CharsetRecog_Unicode.java
│ │ │ │ │ ├── CharsetRecog_mbcs.java
│ │ │ │ │ ├── CharsetRecog_sbcs.java
│ │ │ │ │ └── CharsetRecognizer.java
│ │ │ │ ├── model/
│ │ │ │ │ ├── Debug.kt
│ │ │ │ │ ├── DebugLog.kt
│ │ │ │ │ ├── Debugger.kt
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── analyzeRule/
│ │ │ │ │ │ ├── AnalyzeByJSonPath.kt
│ │ │ │ │ │ ├── AnalyzeByJSoup.kt
│ │ │ │ │ │ ├── AnalyzeByRegex.kt
│ │ │ │ │ │ ├── AnalyzeByXPath.kt
│ │ │ │ │ │ ├── AnalyzeRule.kt
│ │ │ │ │ │ ├── AnalyzeUrl.kt
│ │ │ │ │ │ ├── QueryTTF.java
│ │ │ │ │ │ ├── RuleAnalyzer.kt
│ │ │ │ │ │ ├── RuleData.kt
│ │ │ │ │ │ └── RuleDataInterface.kt
│ │ │ │ │ ├── localBook/
│ │ │ │ │ │ ├── CbzFile.kt
│ │ │ │ │ │ ├── EpubFile.kt
│ │ │ │ │ │ ├── LocalBook.kt
│ │ │ │ │ │ ├── TextFile.kt
│ │ │ │ │ │ └── UmdFile.kt
│ │ │ │ │ ├── rss/
│ │ │ │ │ │ ├── Rss.kt
│ │ │ │ │ │ ├── RssParserByRule.kt
│ │ │ │ │ │ └── RssParserDefault.kt
│ │ │ │ │ └── webBook/
│ │ │ │ │ ├── BookChapterList.kt
│ │ │ │ │ ├── BookContent.kt
│ │ │ │ │ ├── BookInfo.kt
│ │ │ │ │ ├── BookList.kt
│ │ │ │ │ └── WebBook.kt
│ │ │ │ └── utils/
│ │ │ │ ├── ACache.kt
│ │ │ │ ├── AnkoHelps.kt
│ │ │ │ ├── Base64.java
│ │ │ │ ├── EncoderUtils.kt
│ │ │ │ ├── EncodingDetect.kt
│ │ │ │ ├── FileExtensions.kt
│ │ │ │ ├── FilesUtil.kt
│ │ │ │ ├── GsonExtensions.kt
│ │ │ │ ├── HtmlFormatter.kt
│ │ │ │ ├── JsonExtensions.kt
│ │ │ │ ├── JsoupExtensions.kt
│ │ │ │ ├── LogUtils.kt
│ │ │ │ ├── MD5Utils.kt
│ │ │ │ ├── NetworkUtils.kt
│ │ │ │ ├── SourceAnalyzer.kt
│ │ │ │ ├── StringExtensions.kt
│ │ │ │ ├── StringUtils.kt
│ │ │ │ ├── TextUtils.java
│ │ │ │ ├── ThrowableExtensions.kt
│ │ │ │ ├── UTF8BOMFighter.kt
│ │ │ │ ├── Utf8BomUtils.kt
│ │ │ │ └── ZipUtils.kt
│ │ │ ├── me/
│ │ │ │ └── ag2s/
│ │ │ │ ├── epublib/
│ │ │ │ │ ├── Constants.java
│ │ │ │ │ ├── browsersupport/
│ │ │ │ │ │ ├── NavigationEvent.java
│ │ │ │ │ │ ├── NavigationEventListener.java
│ │ │ │ │ │ ├── NavigationHistory.java
│ │ │ │ │ │ ├── Navigator.java
│ │ │ │ │ │ └── package-info.java
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ ├── Author.java
│ │ │ │ │ │ ├── Date.java
│ │ │ │ │ │ ├── EpubBook.java
│ │ │ │ │ │ ├── EpubResourceProvider.java
│ │ │ │ │ │ ├── FileResourceProvider.java
│ │ │ │ │ │ ├── Guide.java
│ │ │ │ │ │ ├── GuideReference.java
│ │ │ │ │ │ ├── Identifier.java
│ │ │ │ │ │ ├── LazyResource.java
│ │ │ │ │ │ ├── LazyResourceProvider.java
│ │ │ │ │ │ ├── ManifestItemProperties.java
│ │ │ │ │ │ ├── ManifestItemRefProperties.java
│ │ │ │ │ │ ├── ManifestProperties.java
│ │ │ │ │ │ ├── MediaType.java
│ │ │ │ │ │ ├── MediaTypes.java
│ │ │ │ │ │ ├── Metadata.java
│ │ │ │ │ │ ├── Relator.java
│ │ │ │ │ │ ├── Resource.java
│ │ │ │ │ │ ├── ResourceInputStream.java
│ │ │ │ │ │ ├── ResourceReference.java
│ │ │ │ │ │ ├── Resources.java
│ │ │ │ │ │ ├── Spine.java
│ │ │ │ │ │ ├── SpineReference.java
│ │ │ │ │ │ ├── TOCReference.java
│ │ │ │ │ │ ├── TableOfContents.java
│ │ │ │ │ │ └── TitledResourceReference.java
│ │ │ │ │ ├── epub/
│ │ │ │ │ │ ├── BookProcessor.java
│ │ │ │ │ │ ├── BookProcessorPipeline.java
│ │ │ │ │ │ ├── DOMUtil.java
│ │ │ │ │ │ ├── EpubProcessorSupport.java
│ │ │ │ │ │ ├── EpubReader.java
│ │ │ │ │ │ ├── EpubWriter.java
│ │ │ │ │ │ ├── HtmlProcessor.java
│ │ │ │ │ │ ├── NCXDocumentV2.java
│ │ │ │ │ │ ├── NCXDocumentV3.java
│ │ │ │ │ │ ├── PackageDocumentBase.java
│ │ │ │ │ │ ├── PackageDocumentMetadataReader.java
│ │ │ │ │ │ ├── PackageDocumentMetadataWriter.java
│ │ │ │ │ │ ├── PackageDocumentReader.java
│ │ │ │ │ │ ├── PackageDocumentWriter.java
│ │ │ │ │ │ └── ResourcesLoader.java
│ │ │ │ │ └── util/
│ │ │ │ │ ├── CollectionUtil.java
│ │ │ │ │ ├── IOUtil.java
│ │ │ │ │ ├── NoCloseOutputStream.java
│ │ │ │ │ ├── NoCloseWriter.java
│ │ │ │ │ ├── ResourceUtil.java
│ │ │ │ │ ├── StringUtil.java
│ │ │ │ │ └── commons/
│ │ │ │ │ └── io/
│ │ │ │ │ ├── BOMInputStream.java
│ │ │ │ │ ├── ByteOrderMark.java
│ │ │ │ │ ├── IOConsumer.java
│ │ │ │ │ ├── ProxyInputStream.java
│ │ │ │ │ ├── XmlStreamReader.java
│ │ │ │ │ └── XmlStreamReaderException.java
│ │ │ │ └── umdlib/
│ │ │ │ ├── domain/
│ │ │ │ │ ├── UmdBook.java
│ │ │ │ │ ├── UmdChapters.java
│ │ │ │ │ ├── UmdCover.java
│ │ │ │ │ ├── UmdEnd.java
│ │ │ │ │ └── UmdHeader.java
│ │ │ │ ├── tool/
│ │ │ │ │ ├── StreamReader.java
│ │ │ │ │ ├── UmdUtils.java
│ │ │ │ │ └── WrapOutputStream.java
│ │ │ │ └── umd/
│ │ │ │ └── UmdReader.java
│ │ │ └── org/
│ │ │ └── kxml2/
│ │ │ ├── io/
│ │ │ │ ├── KXmlParser.java
│ │ │ │ └── KXmlSerializer.java
│ │ │ ├── kdom/
│ │ │ │ ├── Document.java
│ │ │ │ ├── Element.java
│ │ │ │ └── Node.java
│ │ │ └── wap/
│ │ │ ├── Wbxml.java
│ │ │ ├── WbxmlParser.java
│ │ │ ├── WbxmlSerializer.java
│ │ │ ├── syncml/
│ │ │ │ └── SyncML.java
│ │ │ ├── wml/
│ │ │ │ └── Wml.java
│ │ │ └── wv/
│ │ │ └── WV.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── services/
│ │ │ └── org.xmlpull.v1.XmlPullParserFactory
│ │ ├── application-prod.yml
│ │ ├── application.yml
│ │ ├── banner.txt
│ │ ├── defaultData/
│ │ │ └── txtTocRule.json
│ │ ├── dtd/
│ │ │ ├── openebook.org/
│ │ │ │ └── dtds/
│ │ │ │ └── oeb-1.2/
│ │ │ │ ├── oeb12.ent
│ │ │ │ └── oebpkg12.dtd
│ │ │ ├── www.daisy.org/
│ │ │ │ └── z3986/
│ │ │ │ └── 2005/
│ │ │ │ └── ncx-2005-1.dtd
│ │ │ └── www.w3.org/
│ │ │ └── TR/
│ │ │ ├── ruby/
│ │ │ │ └── xhtml-ruby-1.mod
│ │ │ ├── xhtml-modularization/
│ │ │ │ └── DTD/
│ │ │ │ ├── xhtml-arch-1.mod
│ │ │ │ ├── xhtml-attribs-1.mod
│ │ │ │ ├── xhtml-base-1.mod
│ │ │ │ ├── xhtml-bdo-1.mod
│ │ │ │ ├── xhtml-blkphras-1.mod
│ │ │ │ ├── xhtml-blkpres-1.mod
│ │ │ │ ├── xhtml-blkstruct-1.mod
│ │ │ │ ├── xhtml-charent-1.mod
│ │ │ │ ├── xhtml-csismap-1.mod
│ │ │ │ ├── xhtml-datatypes-1.mod
│ │ │ │ ├── xhtml-datatypes-1.mod.1
│ │ │ │ ├── xhtml-edit-1.mod
│ │ │ │ ├── xhtml-events-1.mod
│ │ │ │ ├── xhtml-form-1.mod
│ │ │ │ ├── xhtml-framework-1.mod
│ │ │ │ ├── xhtml-hypertext-1.mod
│ │ │ │ ├── xhtml-image-1.mod
│ │ │ │ ├── xhtml-inlphras-1.mod
│ │ │ │ ├── xhtml-inlpres-1.mod
│ │ │ │ ├── xhtml-inlstruct-1.mod
│ │ │ │ ├── xhtml-inlstyle-1.mod
│ │ │ │ ├── xhtml-lat1.ent
│ │ │ │ ├── xhtml-link-1.mod
│ │ │ │ ├── xhtml-list-1.mod
│ │ │ │ ├── xhtml-meta-1.mod
│ │ │ │ ├── xhtml-notations-1.mod
│ │ │ │ ├── xhtml-object-1.mod
│ │ │ │ ├── xhtml-param-1.mod
│ │ │ │ ├── xhtml-pres-1.mod
│ │ │ │ ├── xhtml-qname-1.mod
│ │ │ │ ├── xhtml-script-1.mod
│ │ │ │ ├── xhtml-special.ent
│ │ │ │ ├── xhtml-ssismap-1.mod
│ │ │ │ ├── xhtml-struct-1.mod
│ │ │ │ ├── xhtml-style-1.mod
│ │ │ │ ├── xhtml-symbol.ent
│ │ │ │ ├── xhtml-symbol.ent.1
│ │ │ │ ├── xhtml-table-1.mod
│ │ │ │ ├── xhtml-text-1.mod
│ │ │ │ └── xhtml11-model-1.mod
│ │ │ ├── xhtml1/
│ │ │ │ └── DTD/
│ │ │ │ ├── xhtml-lat1.ent
│ │ │ │ ├── xhtml-special.ent
│ │ │ │ ├── xhtml-symbol.ent
│ │ │ │ ├── xhtml1-strict.dtd
│ │ │ │ └── xhtml1-transitional.dtd
│ │ │ └── xhtml11/
│ │ │ └── DTD/
│ │ │ └── xhtml11.dtd
│ │ ├── epub/
│ │ │ ├── chapter.html
│ │ │ ├── cover.html
│ │ │ ├── fonts.css
│ │ │ ├── intro.html
│ │ │ └── main.css
│ │ └── logback-spring.xml
│ └── test/
│ └── java/
│ └── com/
│ └── htmake/
│ └── reader/
│ └── ReaderApplicationTests.java
├── vetur.config.js
└── web/
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── jsconfig.json
├── package.json
├── postcss.config.js
├── public/
│ ├── bookSourceDebug/
│ │ ├── index.css
│ │ ├── index.html
│ │ └── index.js
│ ├── browsertest.html
│ ├── index.html
│ ├── robots.txt
│ └── sw.js
├── src/
│ ├── App.vue
│ ├── assets/
│ │ └── fonts/
│ │ └── iconfont.css
│ ├── components/
│ │ ├── AddUser.vue
│ │ ├── BookGroup.vue
│ │ ├── BookInfo.vue
│ │ ├── BookManage.vue
│ │ ├── BookShelf.vue
│ │ ├── BookSource.vue
│ │ ├── Bookmark.vue
│ │ ├── BookmarkForm.vue
│ │ ├── Content.vue
│ │ ├── Explore.vue
│ │ ├── LocalStore.vue
│ │ ├── MPCode.vue
│ │ ├── PopCatalog.vue
│ │ ├── ReadSettings.vue
│ │ ├── ReplaceRule.vue
│ │ ├── ReplaceRuleForm.vue
│ │ ├── RssArticle.vue
│ │ ├── RssArticleList.vue
│ │ ├── RssSourceList.vue
│ │ ├── SearchBookContent.vue
│ │ ├── UserManage.vue
│ │ └── WebDAV.vue
│ ├── main.js
│ ├── plugins/
│ │ ├── animate.js
│ │ ├── axios.js
│ │ ├── cache.js
│ │ ├── chinese.js
│ │ ├── config.js
│ │ ├── element.js
│ │ ├── eventBus.js
│ │ ├── helper.js
│ │ ├── jump.js
│ │ ├── md5.js
│ │ ├── safe-json-stringify.js
│ │ └── vuex.js
│ ├── registerServiceWorker.js
│ ├── router/
│ │ └── index.js
│ └── views/
│ ├── Index.vue
│ └── Reader.vue
└── vue.config.js
SYMBOL INDEX (1435 symbols across 104 files)
FILE: src/main/java/com/htmake/reader/SpringEvent.java
class SpringEvent (line 5) | public class SpringEvent extends ApplicationEvent {
method SpringEvent (line 8) | public SpringEvent(Object source, String event, String message) {
method getEvent (line 13) | public String getEvent() {
method setEvent (line 16) | public void setEvent(String event) {
method getMessage (line 19) | public String getMessage() {
method setMessage (line 22) | public void setMessage(String message) {
FILE: src/main/java/com/htmake/reader/utils/SpringContextUtils.java
class SpringContextUtils (line 8) | @Component
method setApplicationContext (line 16) | @Override
method getApplicationContext (line 26) | public static ApplicationContext getApplicationContext() {
method getBean (line 36) | public static Object getBean(String name) {
method getBean (line 50) | public static <T> T getBean(Class<T> clazz) {
method getBean (line 65) | public static <T> T getBean(String name, Class<T> clazz) {
FILE: src/main/java/io/legado/app/help/EncodingDetectHelp.java
class EncodingDetectHelp (line 37) | public class EncodingDetectHelp {
method getHtmlEncode (line 39) | public static String getHtmlEncode( byte[] bytes) {
method getJavaEncode (line 67) | public static String getJavaEncode(byte[] bytes) {
method getJavaEncode (line 85) | public static String getJavaEncode( String filePath) {
method getJavaEncode (line 104) | public static String getJavaEncode( File file) {
class BytesEncodingDetect (line 119) | class BytesEncodingDetect extends Encoding {
method BytesEncodingDetect (line 141) | public BytesEncodingDetect() {
method detectEncoding (line 162) | public int detectEncoding(URL testurl) {
method detectEncoding (line 190) | public int detectEncoding(File testfile) {
method getFileBytes (line 195) | public static byte[] getFileBytes(File testfile) {
method detectEncoding (line 217) | public int detectEncoding(byte[] rawtext) {
method gb2312_probability (line 267) | int gb2312_probability(byte[] rawtext) {
method gbk_probability (line 308) | int gbk_probability(byte[] rawtext) {
method gb18030_probability (line 369) | int gb18030_probability(byte[] rawtext) {
method hz_probability (line 450) | int hz_probability(byte[] rawtext) {
method big5_probability (line 522) | int big5_probability(byte[] rawtext) {
method big5plus_probability (line 565) | int big5plus_probability(byte[] rawtext) {
method euc_tw_probability (line 631) | int euc_tw_probability(byte[] rawtext) {
method iso_2022_cn_probability (line 684) | int iso_2022_cn_probability(byte[] rawtext) {
method utf8_probability (line 764) | int utf8_probability(byte[] rawtext) {
method utf16_probability (line 814) | int utf16_probability(byte[] rawtext) {
method ascii_probability (line 850) | int ascii_probability(byte[] rawtext) {
method euc_kr_probability (line 872) | int euc_kr_probability(byte[] rawtext) {
method cp949_probability (line 912) | int cp949_probability(byte[] rawtext) {
method iso_2022_kr_probability (line 953) | int iso_2022_kr_probability(byte[] rawtext) {
method euc_jp_probability (line 971) | int euc_jp_probability(byte[] rawtext) {
method iso_2022_jp_probability (line 1006) | int iso_2022_jp_probability(byte[] rawtext) {
method sjis_probability (line 1023) | int sjis_probability(byte[] rawtext) {
method initialize_frequencies (line 1082) | void initialize_frequencies() {
class Encoding (line 4767) | class Encoding {
method Encoding (line 4833) | public Encoding() {
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetDetector.java
class CharsetDetector (line 41) | @SuppressWarnings({"JavaDoc", "unused", "RedundantSuppression"})
method CharsetDetector (line 59) | public CharsetDetector() {
method setDeclaredEncoding (line 77) | public CharsetDetector setDeclaredEncoding(String encoding) {
method setText (line 89) | public CharsetDetector setText(byte[] in) {
method setText (line 112) | public CharsetDetector setText(InputStream in) throws IOException {
method detect (line 153) | public CharsetMatch detect() {
method detectAll (line 181) | public CharsetMatch[] detectAll() {
method getReader (line 226) | public Reader getReader(InputStream in, String declaredEncoding) {
method getString (line 258) | public String getString(byte[] in, String declaredEncoding) {
method getAllDetectableCharsets (line 291) | public static String[] getAllDetectableCharsets() {
method inputFilterEnabled (line 306) | public boolean inputFilterEnabled() {
method enableInputFilter (line 319) | public boolean enableInputFilter(boolean filter) {
method MungeInput (line 331) | private void MungeInput() {
class CSRecognizerInfo (line 445) | private static class CSRecognizerInfo {
method CSRecognizerInfo (line 449) | CSRecognizerInfo(CharsetRecognizer recognizer, boolean isDefaultEnab...
method getDetectableCharsets (line 508) | @Deprecated
method setDetectableCharset (line 535) | @Deprecated
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetMatch.java
class CharsetMatch (line 31) | @SuppressWarnings({"JavaDoc", "unused"})
method getReader (line 47) | public Reader getReader() {
method getString (line 69) | public String getString() throws java.io.IOException {
method getString (line 87) | public String getString(int maxLength) throws java.io.IOException {
method getConfidence (line 130) | public int getConfidence() {
method getName (line 147) | public String getName() {
method getLanguage (line 157) | public String getLanguage() {
method compareTo (line 174) | @Override
method CharsetMatch (line 188) | CharsetMatch(CharsetDetector det, CharsetRecognizer rec, int conf) {
method CharsetMatch (line 208) | CharsetMatch(CharsetDetector det, CharsetRecognizer rec, int conf, Str...
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetRecog_2022.java
class CharsetRecog_2022 (line 19) | abstract class CharsetRecog_2022 extends CharsetRecognizer {
method match (line 33) | int match(byte[] text, int textLen, byte[][] escapeSequences) {
class CharsetRecog_2022JP (line 97) | static class CharsetRecog_2022JP extends CharsetRecog_2022 {
method getName (line 113) | @Override
method match (line 118) | @Override
class CharsetRecog_2022KR (line 125) | static class CharsetRecog_2022KR extends CharsetRecog_2022 {
method getName (line 130) | @Override
method match (line 135) | @Override
class CharsetRecog_2022CN (line 142) | static class CharsetRecog_2022CN extends CharsetRecog_2022 {
method getName (line 157) | @Override
method match (line 162) | @Override
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetRecog_UTF8.java
class CharsetRecog_UTF8 (line 14) | class CharsetRecog_UTF8 extends CharsetRecognizer {
method getName (line 16) | @Override
method match (line 24) | @Override
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetRecog_Unicode.java
class CharsetRecog_Unicode (line 17) | abstract class CharsetRecog_Unicode extends CharsetRecognizer {
method getName (line 22) | @Override
method match (line 28) | @Override
method codeUnit16FromBytes (line 31) | static int codeUnit16FromBytes(byte hi, byte lo) {
method adjustConfidence (line 40) | static int adjustConfidence(int codeUnit, int confidence) {
class CharsetRecog_UTF_16_BE (line 54) | static class CharsetRecog_UTF_16_BE extends CharsetRecog_Unicode {
method getName (line 55) | @Override
method match (line 60) | @Override
class CharsetRecog_UTF_16_LE (line 87) | static class CharsetRecog_UTF_16_LE extends CharsetRecog_Unicode {
method getName (line 88) | @Override
method match (line 93) | @Override
class CharsetRecog_UTF_32 (line 120) | static abstract class CharsetRecog_UTF_32 extends CharsetRecog_Unicode {
method getChar (line 121) | abstract int getChar(byte[] input, int index);
method getName (line 123) | @Override
method match (line 126) | @Override
class CharsetRecog_UTF_32_BE (line 172) | static class CharsetRecog_UTF_32_BE extends CharsetRecog_UTF_32 {
method getChar (line 173) | @Override
method getName (line 179) | @Override
class CharsetRecog_UTF_32_LE (line 186) | static class CharsetRecog_UTF_32_LE extends CharsetRecog_UTF_32 {
method getChar (line 187) | @Override
method getName (line 193) | @Override
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetRecog_mbcs.java
class CharsetRecog_mbcs (line 26) | abstract class CharsetRecog_mbcs extends CharsetRecognizer {
method getName (line 33) | @Override
method match (line 49) | int match(CharsetDetector det, int[] commonChars) {
class iteratedChar (line 144) | static class iteratedChar {
method reset (line 150) | void reset() {
method nextByte (line 157) | int nextByte(CharsetDetector det) {
method nextChar (line 179) | abstract boolean nextChar(iteratedChar it, CharsetDetector det);
class CharsetRecog_sjis (line 185) | static class CharsetRecog_sjis extends CharsetRecog_mbcs {
method nextChar (line 197) | @Override
method match (line 222) | @Override
method getName (line 228) | @Override
method getLanguage (line 233) | @Override
class CharsetRecog_big5 (line 245) | static class CharsetRecog_big5 extends CharsetRecog_mbcs {
method nextChar (line 261) | @Override
method match (line 289) | @Override
method getName (line 295) | @Override
method getLanguage (line 301) | @Override
class CharsetRecog_euc (line 313) | abstract static class CharsetRecog_euc extends CharsetRecog_mbcs {
method nextChar (line 321) | @Override
class CharsetRecog_euc_jp (line 383) | static class CharsetRecog_euc_jp extends CharsetRecog_euc {
method getName (line 399) | @Override
method match (line 404) | @Override
method getLanguage (line 410) | @Override
class CharsetRecog_euc_kr (line 420) | static class CharsetRecog_euc_kr extends CharsetRecog_euc {
method getName (line 436) | @Override
method match (line 441) | @Override
method getLanguage (line 447) | @Override
class CharsetRecog_gb_18030 (line 457) | static class CharsetRecog_gb_18030 extends CharsetRecog_mbcs {
method nextChar (line 465) | @Override
method getName (line 534) | @Override
method match (line 539) | @Override
method getLanguage (line 545) | @Override
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetRecog_sbcs.java
class CharsetRecog_sbcs (line 17) | abstract class CharsetRecog_sbcs extends CharsetRecognizer {
method getName (line 22) | @Override
class NGramParser (line 25) | static class NGramParser {
method NGramParser (line 40) | public NGramParser(int[] theNgramList, byte[] theByteMap) {
method search (line 52) | private static int search(int[] table, int value) {
method lookup (line 90) | private void lookup(int thisNgram) {
method addByte (line 99) | protected void addByte(int b) {
method nextByte (line 104) | private int nextByte(CharsetDetector det) {
method parseCharacters (line 112) | protected void parseCharacters(CharsetDetector det) {
method parse (line 131) | public int parse(CharsetDetector det) {
method parse (line 135) | public int parse(CharsetDetector det, byte spaceCh) {
class NGramParser_IBM420 (line 160) | static class NGramParser_IBM420 extends NGramParser {
method NGramParser_IBM420 (line 184) | public NGramParser_IBM420(int[] theNgramList, byte[] theByteMap) {
method isLamAlef (line 188) | private byte isLamAlef(byte b) {
method nextByte (line 206) | private int nextByte(CharsetDetector det) {
method parseCharacters (line 223) | @Override
method match (line 257) | int match(CharsetDetector det, int[] ngrams, byte[] byteMap) {
method match (line 261) | int match(CharsetDetector det, int[] ngrams, byte[] byteMap, byte spac...
method matchIBM420 (line 266) | @SuppressWarnings("SameParameterValue")
class NGramsPlusLang (line 272) | static class NGramsPlusLang {
method NGramsPlusLang (line 276) | NGramsPlusLang(String la, int[] ng) {
class CharsetRecog_8859_1 (line 282) | static class CharsetRecog_8859_1 extends CharsetRecog_sbcs {
method match (line 412) | @Override
method getName (line 428) | @Override
class CharsetRecog_8859_2 (line 435) | static class CharsetRecog_8859_2 extends CharsetRecog_sbcs {
method match (line 506) | @Override
method getName (line 521) | @Override
class CharsetRecog_8859_5 (line 529) | abstract static class CharsetRecog_8859_5 extends CharsetRecog_sbcs {
method getName (line 565) | @Override
class CharsetRecog_8859_5_ru (line 571) | static class CharsetRecog_8859_5_ru extends CharsetRecog_8859_5 {
method getLanguage (line 579) | @Override
method match (line 584) | @Override
class CharsetRecog_8859_6 (line 591) | abstract static class CharsetRecog_8859_6 extends CharsetRecog_sbcs {
method getName (line 627) | @Override
class CharsetRecog_8859_6_ar (line 633) | static class CharsetRecog_8859_6_ar extends CharsetRecog_8859_6 {
method getLanguage (line 641) | @Override
method match (line 646) | @Override
class CharsetRecog_8859_7 (line 653) | abstract static class CharsetRecog_8859_7 extends CharsetRecog_sbcs {
method getName (line 689) | @Override
class CharsetRecog_8859_7_el (line 695) | static class CharsetRecog_8859_7_el extends CharsetRecog_8859_7 {
method getLanguage (line 703) | @Override
method match (line 708) | @Override
class CharsetRecog_8859_8 (line 716) | abstract static class CharsetRecog_8859_8 extends CharsetRecog_sbcs {
method getName (line 752) | @Override
class CharsetRecog_8859_8_I_he (line 758) | static class CharsetRecog_8859_8_I_he extends CharsetRecog_8859_8 {
method getName (line 766) | @Override
method getLanguage (line 771) | @Override
method match (line 776) | @Override
class CharsetRecog_8859_8_he (line 784) | static class CharsetRecog_8859_8_he extends CharsetRecog_8859_8 {
method getLanguage (line 792) | @Override
method match (line 797) | @Override
class CharsetRecog_8859_9 (line 806) | abstract static class CharsetRecog_8859_9 extends CharsetRecog_sbcs {
method getName (line 842) | @Override
class CharsetRecog_8859_9_tr (line 848) | static class CharsetRecog_8859_9_tr extends CharsetRecog_8859_9 {
method getLanguage (line 856) | @Override
method match (line 861) | @Override
class CharsetRecog_windows_1251 (line 869) | static class CharsetRecog_windows_1251 extends CharsetRecog_sbcs {
method getName (line 912) | @Override
method getLanguage (line 917) | @Override
method match (line 922) | @Override
class CharsetRecog_windows_1256 (line 929) | static class CharsetRecog_windows_1256 extends CharsetRecog_sbcs {
method getName (line 972) | @Override
method getLanguage (line 977) | @Override
method match (line 982) | @Override
class CharsetRecog_KOI8_R (line 989) | static class CharsetRecog_KOI8_R extends CharsetRecog_sbcs {
method getName (line 1032) | @Override
method getLanguage (line 1037) | @Override
method match (line 1042) | @Override
class CharsetRecog_IBM424_he (line 1049) | abstract static class CharsetRecog_IBM424_he extends CharsetRecog_sbcs {
method getLanguage (line 1070) | @Override
class CharsetRecog_IBM424_he_rtl (line 1076) | static class CharsetRecog_IBM424_he_rtl extends CharsetRecog_IBM424_he {
method getName (line 1077) | @Override
method match (line 1089) | @Override
class CharsetRecog_IBM424_he_ltr (line 1096) | static class CharsetRecog_IBM424_he_ltr extends CharsetRecog_IBM424_he {
method getName (line 1097) | @Override
method match (line 1110) | @Override
class CharsetRecog_IBM420_ar (line 1117) | abstract static class CharsetRecog_IBM420_ar extends CharsetRecog_sbcs {
method getLanguage (line 1140) | @Override
class CharsetRecog_IBM420_ar_rtl (line 1147) | static class CharsetRecog_IBM420_ar_rtl extends CharsetRecog_IBM420_ar {
method getName (line 1155) | @Override
method match (line 1160) | @Override
class CharsetRecog_IBM420_ar_ltr (line 1168) | static class CharsetRecog_IBM420_ar_ltr extends CharsetRecog_IBM420_ar {
method getName (line 1176) | @Override
method match (line 1181) | @Override
FILE: src/main/java/io/legado/app/lib/icu4j/CharsetRecognizer.java
class CharsetRecognizer (line 25) | abstract class CharsetRecognizer {
method getName (line 31) | abstract String getName();
method getLanguage (line 38) | public String getLanguage() {
method match (line 51) | abstract CharsetMatch match(CharsetDetector det);
FILE: src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java
class QueryTTF (line 14) | @SuppressWarnings({"FieldCanBeLocal", "StatementWithEmptyBody", "unused"})
class Header (line 16) | private static class Header {
class Directory (line 25) | private static class Directory {
class NameLayout (line 32) | private static class NameLayout {
class NameRecord (line 39) | private static class NameRecord {
class HeadLayout (line 48) | private static class HeadLayout {
class MaxpLayout (line 69) | private static class MaxpLayout {
class CmapLayout (line 88) | private static class CmapLayout {
class CmapRecord (line 95) | private static class CmapRecord {
class CmapFormat (line 101) | private static class CmapFormat {
class CmapFormat4 (line 108) | private static class CmapFormat4 extends CmapFormat {
class CmapFormat6 (line 121) | private static class CmapFormat6 extends CmapFormat {
class CmapFormat12 (line 127) | private static class CmapFormat12 extends CmapFormat {
class GlyfLayout (line 135) | private static class GlyfLayout {
class ByteArrayReader (line 149) | private static class ByteArrayReader {
method ByteArrayReader (line 153) | public ByteArrayReader(byte[] buffer, int index) {
method ReadUIntX (line 158) | public long ReadUIntX(long len) {
method ReadUInt64 (line 167) | public long ReadUInt64() {
method ReadUInt32 (line 171) | public int ReadUInt32() {
method ReadUInt16 (line 175) | public int ReadUInt16() {
method ReadInt16 (line 179) | public short ReadInt16() {
method ReadUInt8 (line 183) | public short ReadUInt8() {
method ReadStrings (line 188) | public String ReadStrings(int len, Charset charset) {
method GetByte (line 194) | public byte GetByte() {
method GetBytes (line 198) | public byte[] GetBytes(int len) {
method GetUInt16Array (line 204) | public int[] GetUInt16Array(int len) {
method GetInt16Array (line 210) | public short[] GetInt16Array(int len) {
method QueryTTF (line 246) | public QueryTTF(byte[] buffer) {
method getNameById (line 496) | public String getNameById(int nameId) {
method getGlyfIndex (line 516) | private int getGlyfIndex(int code) {
method inLimit (line 580) | public boolean inLimit(char code) {
method getGlyfByCode (line 590) | public String getGlyfByCode(int key) {
method getCodeByGlyf (line 600) | public int getCodeByGlyf(String val) {
FILE: src/main/java/io/legado/app/utils/Base64.java
class Base64 (line 5) | public class Base64 {
class Coder (line 43) | static abstract class Coder {
method process (line 59) | public abstract boolean process(byte[] input, int offset, int len, b...
method maxOutputSize (line 66) | public abstract int maxOutputSize(int len);
method decode (line 86) | public static byte[] decode(String str, int flags) {
method decode (line 103) | public static byte[] decode(byte[] input, int flags) {
method decode (line 122) | public static byte[] decode(byte[] input, int offset, int len, int fla...
class Decoder (line 140) | static class Decoder extends Coder {
method Decoder (line 203) | public Decoder(int flags, byte[] output) {
method maxOutputSize (line 214) | public int maxOutputSize(int len) {
method process (line 224) | public boolean process(byte[] input, int offset, int len, boolean fi...
method encodeToString (line 399) | public static String encodeToString(byte[] input, int flags) {
method encodeToString (line 420) | public static String encodeToString(byte[] input, int offset, int len,...
method encode (line 438) | public static byte[] encode(byte[] input, int flags) {
method encode (line 454) | public static byte[] encode(byte[] input, int offset, int len, int fla...
class Encoder (line 486) | static class Encoder extends Coder {
method Encoder (line 521) | public Encoder(int flags, byte[] output) {
method maxOutputSize (line 536) | public int maxOutputSize(int len) {
method process (line 540) | public boolean process(byte[] input, int offset, int len, boolean fi...
method Base64 (line 664) | private Base64() {
FILE: src/main/java/io/legado/app/utils/TextUtils.java
class TextUtils (line 5) | public class TextUtils {
method isEmpty (line 7) | public static boolean isEmpty(CharSequence str) {
method join (line 21) | public static String join(CharSequence delimiter, Object[] tokens) {
method join (line 45) | public static String join(CharSequence delimiter, Iterable tokens) {
FILE: src/main/java/me/ag2s/epublib/Constants.java
type Constants (line 4) | public interface Constants {
FILE: src/main/java/me/ag2s/epublib/browsersupport/NavigationEvent.java
class NavigationEvent (line 16) | @SuppressWarnings("unused")
method NavigationEvent (line 28) | public NavigationEvent(Object source) {
method NavigationEvent (line 32) | public NavigationEvent(Object source, Navigator navigator) {
method getOldSectionPos (line 47) | public int getOldSectionPos() {
method getNavigator (line 51) | public Navigator getNavigator() {
method getOldFragmentId (line 55) | public String getOldFragmentId() {
method setOldFragmentId (line 60) | void setOldFragmentId(String oldFragmentId) {
method getOldBook (line 64) | public EpubBook getOldBook() {
method setOldPagePos (line 69) | void setOldPagePos(int oldPagePos) {
method getCurrentSectionPos (line 73) | public int getCurrentSectionPos() {
method getOldSpinePos (line 77) | public int getOldSpinePos() {
method getCurrentSpinePos (line 81) | public int getCurrentSpinePos() {
method getCurrentFragmentId (line 85) | public String getCurrentFragmentId() {
method isBookChanged (line 89) | public boolean isBookChanged() {
method isSpinePosChanged (line 96) | public boolean isSpinePosChanged() {
method isFragmentChanged (line 100) | public boolean isFragmentChanged() {
method getOldResource (line 104) | public Resource getOldResource() {
method getCurrentResource (line 108) | public Resource getCurrentResource() {
method setOldResource (line 112) | public void setOldResource(Resource oldResource) {
method setOldSpinePos (line 117) | public void setOldSpinePos(int oldSpinePos) {
method setNavigator (line 122) | public void setNavigator(Navigator navigator) {
method setOldBook (line 127) | public void setOldBook(EpubBook oldBook) {
method getCurrentBook (line 131) | public EpubBook getCurrentBook() {
method isResourceChanged (line 135) | public boolean isResourceChanged() {
method toString (line 139) | @SuppressWarnings("NullableProblems")
method isSectionPosChanged (line 155) | public boolean isSectionPosChanged() {
FILE: src/main/java/me/ag2s/epublib/browsersupport/NavigationEventListener.java
type NavigationEventListener (line 10) | public interface NavigationEventListener {
method navigationPerformed (line 17) | void navigationPerformed(NavigationEvent navigationEvent);
FILE: src/main/java/me/ag2s/epublib/browsersupport/NavigationHistory.java
class NavigationHistory (line 14) | public class NavigationHistory implements NavigationEventListener {
class Location (line 19) | private static class Location {
method Location (line 23) | public Location(String href) {
method setHref (line 28) | @SuppressWarnings("unused")
method getHref (line 33) | public String getHref() {
method NavigationHistory (line 46) | public NavigationHistory(Navigator navigator) {
method getCurrentPos (line 52) | public int getCurrentPos() {
method getCurrentSize (line 57) | public int getCurrentSize() {
method initBook (line 61) | public void initBook(EpubBook book) {
method getHistoryWaitTime (line 82) | public long getHistoryWaitTime() {
method setHistoryWaitTime (line 86) | public void setHistoryWaitTime(long historyWaitTime) {
method addLocation (line 90) | public void addLocation(Resource resource) {
method addLocation (line 109) | public void addLocation(Location location) {
method checkHistorySize (line 129) | private void checkHistorySize() {
method addLocation (line 137) | public void addLocation(String href) {
method getLocationHref (line 141) | private String getLocationHref(int pos) {
method move (line 159) | public boolean move(int delta) {
method navigationPerformed (line 174) | @Override
method getCurrentHref (line 193) | public String getCurrentHref() {
method setMaxHistorySize (line 200) | public void setMaxHistorySize(int maxHistorySize) {
method getMaxHistorySize (line 204) | public int getMaxHistorySize() {
FILE: src/main/java/me/ag2s/epublib/browsersupport/Navigator.java
class Navigator (line 19) | public class Navigator implements Serializable {
method Navigator (line 30) | public Navigator() {
method Navigator (line 34) | public Navigator(EpubBook book) {
method handleEventListeners (line 43) | private synchronized void handleEventListeners(
method addNavigationEventListener (line 51) | public boolean addNavigationEventListener(
method removeNavigationEventListener (line 56) | public boolean removeNavigationEventListener(
method gotoFirstSpineSection (line 61) | public int gotoFirstSpineSection(Object source) {
method gotoPreviousSpineSection (line 65) | public int gotoPreviousSpineSection(Object source) {
method gotoPreviousSpineSection (line 69) | public int gotoPreviousSpineSection(int pagePos, Object source) {
method hasNextSpineSection (line 77) | public boolean hasNextSpineSection() {
method hasPreviousSpineSection (line 81) | public boolean hasPreviousSpineSection() {
method gotoNextSpineSection (line 85) | public int gotoNextSpineSection(Object source) {
method gotoResource (line 93) | public int gotoResource(String resourceHref, Object source) {
method gotoResource (line 99) | public int gotoResource(Resource resource, Object source) {
method gotoResource (line 103) | public int gotoResource(Resource resource, String fragmentId, Object s...
method gotoResource (line 107) | public int gotoResource(Resource resource, int pagePos, Object source) {
method gotoResource (line 111) | public int gotoResource(Resource resource, int pagePos, String fragmen...
method gotoResourceId (line 126) | public int gotoResourceId(String resourceId, Object source) {
method gotoSpineSection (line 131) | public int gotoSpineSection(int newSpinePos, Object source) {
method gotoSpineSection (line 143) | public int gotoSpineSection(int newSpinePos, int newPagePos, Object so...
method gotoLastSpineSection (line 158) | public int gotoLastSpineSection(Object source) {
method gotoBook (line 162) | public void gotoBook(EpubBook book, Object source) {
method getCurrentSpinePos (line 177) | public int getCurrentSpinePos() {
method getCurrentResource (line 181) | public Resource getCurrentResource() {
method setCurrentSpinePos (line 192) | public void setCurrentSpinePos(int currentIndex) {
method getBook (line 197) | public EpubBook getBook() {
method setCurrentResource (line 207) | public int setCurrentResource(Resource currentResource) {
method getCurrentFragmentId (line 213) | public String getCurrentFragmentId() {
method getCurrentSectionPos (line 217) | public int getCurrentSectionPos() {
FILE: src/main/java/me/ag2s/epublib/domain/Author.java
class Author (line 14) | public class Author implements Serializable {
method Author (line 22) | public Author(String singleName) {
method Author (line 26) | public Author(String firstname, String lastname) {
method getFirstname (line 31) | public String getFirstname() {
method setFirstname (line 35) | public void setFirstname(String firstname) {
method getLastname (line 39) | public String getLastname() {
method setLastname (line 43) | public void setLastname(String lastname) {
method toString (line 48) | @Override
method hashCode (line 54) | public int hashCode() {
method equals (line 58) | public boolean equals(Object authorObject) {
method setRole (line 73) | public void setRole(String code) {
method getRelator (line 81) | public Relator getRelator() {
method setRelator (line 86) | public void setRelator(Relator relator) {
FILE: src/main/java/me/ag2s/epublib/domain/Date.java
class Date (line 16) | public class Date implements Serializable {
type Event (line 20) | public enum Event {
method Event (line 27) | Event(String v) {
method fromValue (line 31) | public static Event fromValue(String v) {
method toString (line 40) | @Override
method Date (line 51) | public Date() {
method Date (line 55) | public Date(java.util.Date date) {
method Date (line 59) | public Date(String dateString) {
method Date (line 63) | public Date(java.util.Date date, Event event) {
method Date (line 68) | public Date(String dateString, Event event) {
method Date (line 73) | public Date(java.util.Date date, String event) {
method Date (line 78) | public Date(String dateString, String event) {
method checkDate (line 83) | private static String checkDate(String dateString) {
method getValue (line 91) | public String getValue() {
method getEvent (line 95) | public Event getEvent() {
method setEvent (line 99) | public void setEvent(Event event) {
method toString (line 103) | @Override
FILE: src/main/java/me/ag2s/epublib/domain/EpubBook.java
class EpubBook (line 43) | public class EpubBook implements Serializable {
method getVersion (line 59) | public String getVersion() {
method setVersion (line 63) | public void setVersion(String version) {
method isEpub3 (line 67) | public boolean isEpub3() {
method addSection (line 71) | @SuppressWarnings("UnusedReturnValue")
method addSection (line 87) | public TOCReference addSection(
method addSection (line 98) | public TOCReference addSection(String title, Resource resource) {
method addSection (line 111) | public TOCReference addSection(
method generateSpineFromTableOfContents (line 122) | @SuppressWarnings("unused")
method getMetadata (line 137) | public Metadata getMetadata() {
method setMetadata (line 141) | public void setMetadata(Metadata metadata) {
method setResources (line 146) | public void setResources(Resources resources) {
method addResource (line 150) | @SuppressWarnings("unused")
method getResources (line 162) | public Resources getResources() {
method getSpine (line 173) | public Spine getSpine() {
method setSpine (line 178) | public void setSpine(Spine spine) {
method getTableOfContents (line 188) | public TableOfContents getTableOfContents() {
method setTableOfContents (line 193) | public void setTableOfContents(TableOfContents tableOfContents) {
method getCoverPage (line 203) | public Resource getCoverPage() {
method setCoverPage (line 212) | public void setCoverPage(Resource coverPage) {
method getTitle (line 227) | public String getTitle() {
method getCoverImage (line 237) | public Resource getCoverImage() {
method setCoverImage (line 241) | public void setCoverImage(Resource coverImage) {
method getGuide (line 258) | public Guide getGuide() {
method getContents (line 280) | public List<Resource> getContents() {
method addToContentsResult (line 299) | private static void addToContentsResult(Resource resource,
method getOpfResource (line 307) | public Resource getOpfResource() {
method setOpfResource (line 311) | public void setOpfResource(Resource opfResource) {
method setNcxResource (line 315) | public void setNcxResource(Resource ncxResource) {
method getNcxResource (line 319) | public Resource getNcxResource() {
FILE: src/main/java/me/ag2s/epublib/domain/EpubResourceProvider.java
class EpubResourceProvider (line 11) | public class EpubResourceProvider implements LazyResourceProvider {
method EpubResourceProvider (line 18) | public EpubResourceProvider(String epubFilename) {
method getResourceStream (line 22) | @Override
FILE: src/main/java/me/ag2s/epublib/domain/FileResourceProvider.java
class FileResourceProvider (line 13) | public class FileResourceProvider implements LazyResourceProvider {
method FileResourceProvider (line 21) | public FileResourceProvider(String parentDir) {
method FileResourceProvider (line 29) | @SuppressWarnings("unused")
method getResourceStream (line 40) | @Override
FILE: src/main/java/me/ag2s/epublib/domain/Guide.java
class Guide (line 19) | public class Guide implements Serializable {
method getReferences (line 34) | public List<GuideReference> getReferences() {
method setReferences (line 38) | public void setReferences(List<GuideReference> references) {
method uncheckCoverPage (line 43) | private void uncheckCoverPage() {
method getCoverReference (line 47) | public GuideReference getCoverReference() {
method setCoverReference (line 54) | @SuppressWarnings("UnusedReturnValue")
method checkCoverPage (line 65) | private void checkCoverPage() {
method initCoverPage (line 72) | private void initCoverPage() {
method getCoverPage (line 89) | public Resource getCoverPage() {
method setCoverPage (line 97) | public void setCoverPage(Resource coverPage) {
method addReference (line 103) | @SuppressWarnings("UnusedReturnValue")
method getGuideReferencesByType (line 118) | public List<GuideReference> getGuideReferencesByType(
FILE: src/main/java/me/ag2s/epublib/domain/GuideReference.java
class GuideReference (line 15) | public class GuideReference extends TitledResourceReference
method GuideReference (line 77) | public GuideReference(Resource resource) {
method GuideReference (line 81) | public GuideReference(Resource resource, String title) {
method GuideReference (line 85) | public GuideReference(Resource resource, String type, String title) {
method GuideReference (line 89) | public GuideReference(Resource resource, String type, String title,
method getType (line 95) | public String getType() {
method setType (line 99) | public void setType(String type) {
FILE: src/main/java/me/ag2s/epublib/domain/Identifier.java
class Identifier (line 15) | public class Identifier implements Serializable {
type Scheme (line 18) | @SuppressWarnings("unused")
method Identifier (line 34) | public Identifier() {
method Identifier (line 39) | public Identifier(String scheme, String value) {
method getBookIdIdentifier (line 55) | public static Identifier getBookIdIdentifier(List<Identifier> identifi...
method getScheme (line 75) | public String getScheme() {
method setScheme (line 79) | public void setScheme(String scheme) {
method getValue (line 83) | public String getValue() {
method setValue (line 87) | public void setValue(String value) {
method setBookId (line 92) | public void setBookId(boolean bookId) {
method isBookId (line 106) | public boolean isBookId() {
method hashCode (line 110) | public int hashCode() {
method equals (line 115) | public boolean equals(Object otherIdentifier) {
method toString (line 122) | @SuppressWarnings("NullableProblems")
FILE: src/main/java/me/ag2s/epublib/domain/LazyResource.java
class LazyResource (line 12) | public class LazyResource extends Resource {
method LazyResource (line 26) | public LazyResource(LazyResourceProvider resourceProvider, String href) {
method LazyResource (line 29) | public LazyResource(LazyResourceProvider resourceProvider, String href...
method LazyResource (line 42) | public LazyResource(
method LazyResource (line 48) | public LazyResource(
method getInputStream (line 62) | public InputStream getInputStream() throws IOException {
method initialize (line 75) | public void initialize() throws IOException {
method getData (line 88) | public byte[] getData() throws IOException {
method close (line 114) | public void close() {
method isInitialized (line 125) | public boolean isInitialized() {
method getSize (line 134) | public long getSize() {
FILE: src/main/java/me/ag2s/epublib/domain/LazyResourceProvider.java
type LazyResourceProvider (line 9) | public interface LazyResourceProvider {
method getResourceStream (line 11) | InputStream getResourceStream(String href) throws IOException;
FILE: src/main/java/me/ag2s/epublib/domain/ManifestItemProperties.java
type ManifestItemProperties (line 2) | @SuppressWarnings("unused")
method ManifestItemProperties (line 14) | ManifestItemProperties(String name) {
method getName (line 18) | public String getName() {
FILE: src/main/java/me/ag2s/epublib/domain/ManifestItemRefProperties.java
type ManifestItemRefProperties (line 2) | @SuppressWarnings("unused")
method ManifestItemRefProperties (line 9) | ManifestItemRefProperties(String name) {
method getName (line 13) | public String getName() {
FILE: src/main/java/me/ag2s/epublib/domain/ManifestProperties.java
type ManifestProperties (line 3) | public interface ManifestProperties {
method getName (line 5) | String getName();
FILE: src/main/java/me/ag2s/epublib/domain/MediaType.java
class MediaType (line 18) | public class MediaType implements Serializable {
method MediaType (line 25) | public MediaType(String name, String defaultExtension) {
method MediaType (line 29) | public MediaType(String name, String defaultExtension,
method hashCode (line 34) | public int hashCode() {
method MediaType (line 41) | public MediaType(String name, String defaultExtension,
method getName (line 49) | public String getName() {
method getDefaultExtension (line 54) | public String getDefaultExtension() {
method getExtensions (line 59) | public Collection<String> getExtensions() {
method equals (line 63) | public boolean equals(Object otherMediaType) {
method toString (line 69) | @SuppressWarnings("NullableProblems")
FILE: src/main/java/me/ag2s/epublib/domain/MediaTypes.java
class MediaTypes (line 13) | public class MediaTypes {
method isBitmapImage (line 69) | public static boolean isBitmapImage(MediaType mediaType) {
method determineMediaType (line 80) | public static MediaType determineMediaType(String filename) {
method getMediaTypeByName (line 91) | public static MediaType getMediaTypeByName(String mediaTypeName) {
FILE: src/main/java/me/ag2s/epublib/domain/Metadata.java
class Metadata (line 20) | public class Metadata implements Serializable {
method Metadata (line 42) | public Metadata() {
method isAutoGeneratedId (line 47) | @SuppressWarnings("unused")
method getOtherProperties (line 57) | public Map<QName, String> getOtherProperties() {
method setOtherProperties (line 61) | public void setOtherProperties(Map<QName, String> otherProperties) {
method addDate (line 65) | @SuppressWarnings("unused")
method getDates (line 71) | public List<Date> getDates() {
method setDates (line 75) | public void setDates(List<Date> dates) {
method addAuthor (line 79) | @SuppressWarnings("UnusedReturnValue")
method getAuthors (line 85) | public List<Author> getAuthors() {
method setAuthors (line 89) | public void setAuthors(List<Author> authors) {
method addContributor (line 93) | @SuppressWarnings("UnusedReturnValue")
method getContributors (line 99) | public List<Author> getContributors() {
method setContributors (line 103) | public void setContributors(List<Author> contributors) {
method getLanguage (line 107) | public String getLanguage() {
method setLanguage (line 111) | public void setLanguage(String language) {
method getSubjects (line 115) | public List<String> getSubjects() {
method setSubjects (line 119) | public void setSubjects(List<String> subjects) {
method setRights (line 123) | public void setRights(List<String> rights) {
method getRights (line 127) | public List<String> getRights() {
method getFirstTitle (line 138) | public String getFirstTitle() {
method addTitle (line 150) | public String addTitle(String title) {
method setTitles (line 155) | public void setTitles(List<String> titles) {
method getTitles (line 159) | public List<String> getTitles() {
method addPublisher (line 163) | @SuppressWarnings("UnusedReturnValue")
method setPublishers (line 169) | public void setPublishers(List<String> publishers) {
method getPublishers (line 173) | public List<String> getPublishers() {
method addDescription (line 177) | @SuppressWarnings("UnusedReturnValue")
method setDescriptions (line 183) | public void setDescriptions(List<String> descriptions) {
method getDescriptions (line 187) | public List<String> getDescriptions() {
method addIdentifier (line 191) | @SuppressWarnings("unused")
method setIdentifiers (line 202) | public void setIdentifiers(List<Identifier> identifiers) {
method getIdentifiers (line 207) | public List<Identifier> getIdentifiers() {
method setFormat (line 211) | public void setFormat(String format) {
method getFormat (line 215) | public String getFormat() {
method addType (line 219) | @SuppressWarnings("UnusedReturnValue")
method getTypes (line 225) | public List<String> getTypes() {
method setTypes (line 229) | public void setTypes(List<String> types) {
method getMetaAttribute (line 233) | @SuppressWarnings("unused")
method setMetaAttributes (line 238) | public void setMetaAttributes(Map<String, String> metaAttributes) {
FILE: src/main/java/me/ag2s/epublib/domain/Relator.java
type Relator (line 15) | public enum Relator {
method Relator (line 1122) | Relator(String code, String name) {
method getCode (line 1127) | public String getCode() {
method getName (line 1131) | public String getName() {
method byCode (line 1135) | public static Relator byCode(String code) {
FILE: src/main/java/me/ag2s/epublib/domain/Resource.java
class Resource (line 20) | public class Resource implements Serializable {
method Resource (line 42) | public Resource(String href) {
method Resource (line 55) | public Resource(byte[] data, MediaType mediaType) {
method Resource (line 70) | public Resource(byte[] data, String href) {
method Resource (line 84) | public Resource(Reader in, String href) throws IOException {
method Resource (line 106) | public Resource(InputStream in, String href) throws IOException {
method Resource (line 120) | public Resource(String id, byte[] data, String href, MediaType mediaTy...
method Resource (line 123) | public Resource(String id, byte[] data, String href, String originalHr...
method Resource (line 138) | public Resource(String id, byte[] data, String href, MediaType mediaType,
method Resource (line 147) | public Resource(String id, byte[] data, String href, String originalHr...
method getInputStream (line 164) | public InputStream getInputStream() throws IOException {
method getData (line 173) | public byte[] getData() throws IOException {
method close (line 182) | public void close() {
method setData (line 191) | public void setData(byte[] data) {
method getSize (line 200) | public long getSize() {
method getTitle (line 209) | public String getTitle() {
method setId (line 218) | public void setId(String id) {
method getId (line 228) | public String getId() {
method getHref (line 241) | public String getHref() {
method setHref (line 250) | public void setHref(String href) {
method getInputEncoding (line 260) | public String getInputEncoding() {
method setInputEncoding (line 269) | public void setInputEncoding(String encoding) {
method getReader (line 281) | public Reader getReader() throws IOException {
method hashCode (line 290) | public int hashCode() {
method equals (line 299) | public boolean equals(Object resourceObject) {
method getMediaType (line 311) | public MediaType getMediaType() {
method setMediaType (line 315) | public void setMediaType(MediaType mediaType) {
method setTitle (line 319) | public void setTitle(String title) {
method getProperties (line 323) | public String getProperties() {
method setProperties (line 327) | public void setProperties(String properties) {
method toString (line 330) | @SuppressWarnings("NullableProblems")
FILE: src/main/java/me/ag2s/epublib/domain/ResourceInputStream.java
class ResourceInputStream (line 14) | public class ResourceInputStream extends FilterInputStream {
method ResourceInputStream (line 26) | public ResourceInputStream(InputStream in, ZipFile zipFile) {
method close (line 31) | @Override
FILE: src/main/java/me/ag2s/epublib/domain/ResourceReference.java
class ResourceReference (line 5) | public class ResourceReference implements Serializable {
method ResourceReference (line 11) | public ResourceReference(Resource resource) {
method getResource (line 16) | public Resource getResource() {
method setResource (line 25) | public void setResource(Resource resource) {
method getResourceId (line 37) | public String getResourceId() {
FILE: src/main/java/me/ag2s/epublib/domain/Resources.java
class Resources (line 20) | public class Resources implements Serializable {
method add (line 37) | public Resource add(Resource resource) {
method fixResourceId (line 49) | public void fixResourceId(Resource resource) {
method makeValidId (line 73) | private String makeValidId(String resourceId, Resource resource) {
method getResourceItemPrefix (line 81) | private String getResourceItemPrefix(Resource resource) {
method createUniqueResourceId (line 97) | private String createUniqueResourceId(Resource resource) {
method containsId (line 123) | public boolean containsId(String id) {
method getById (line 141) | public Resource getById(String id) {
method getByProperties (line 153) | public Resource getByProperties(String properties) {
method remove (line 171) | public Resource remove(String href) {
method fixResourceHref (line 175) | private void fixResourceHref(Resource resource) {
method createHref (line 194) | private String createHref(MediaType mediaType, int counter) {
method isEmpty (line 203) | public boolean isEmpty() {
method size (line 212) | public int size() {
method getResourceMap (line 222) | @SuppressWarnings("unused")
method getAll (line 227) | public Collection<Resource> getAll() {
method notContainsByHref (line 238) | public boolean notContainsByHref(String href) {
method containsByHref (line 252) | @SuppressWarnings("unused")
method set (line 262) | public void set(Collection<Resource> resources) {
method addAll (line 272) | public void addAll(Collection<Resource> resources) {
method set (line 284) | public void set(Map<String, Resource> resources) {
method getByIdOrHref (line 296) | public Resource getByIdOrHref(String idOrHref) {
method getByHref (line 312) | public Resource getByHref(String href) {
method findFirstResourceByMediaType (line 328) | public Resource findFirstResourceByMediaType(MediaType mediaType) {
method findFirstResourceByMediaType (line 340) | public static Resource findFirstResourceByMediaType(
method getResourcesByMediaType (line 356) | public List<Resource> getResourcesByMediaType(MediaType mediaType) {
method getResourcesByMediaTypes (line 375) | @SuppressWarnings("unused")
method getAllHrefs (line 399) | public Collection<String> getAllHrefs() {
FILE: src/main/java/me/ag2s/epublib/domain/Spine.java
class Spine (line 18) | public class Spine implements Serializable {
method Spine (line 24) | public Spine() {
method Spine (line 33) | public Spine(TableOfContents tableOfContents) {
method Spine (line 38) | public Spine(List<SpineReference> spineReferences) {
method createSpineReferences (line 42) | public static List<SpineReference> createSpineReferences(
method getSpineReferences (line 52) | public List<SpineReference> getSpineReferences() {
method setSpineReferences (line 56) | public void setSpineReferences(List<SpineReference> spineReferences) {
method getResource (line 67) | public Resource getResource(int index) {
method findFirstResourceById (line 82) | public int findFirstResourceById(String resourceId) {
method addSpineReference (line 102) | public SpineReference addSpineReference(SpineReference spineReference) {
method addResource (line 115) | @SuppressWarnings("unused")
method size (line 125) | public int size() {
method setTocResource (line 136) | public void setTocResource(Resource tocResource) {
method getTocResource (line 146) | public Resource getTocResource() {
method getResourceIndex (line 157) | public int getResourceIndex(Resource currentResource) {
method getResourceIndex (line 170) | public int getResourceIndex(String resourceHref) {
method isEmpty (line 188) | public boolean isEmpty() {
FILE: src/main/java/me/ag2s/epublib/domain/SpineReference.java
class SpineReference (line 12) | public class SpineReference extends ResourceReference implements Seriali...
method SpineReference (line 17) | public SpineReference(Resource resource) {
method SpineReference (line 22) | public SpineReference(Resource resource, boolean linear) {
method isLinear (line 44) | public boolean isLinear() {
method setLinear (line 48) | public void setLinear(boolean linear) {
FILE: src/main/java/me/ag2s/epublib/domain/TOCReference.java
class TOCReference (line 15) | public class TOCReference extends TitledResourceReference
method TOCReference (line 21) | @Deprecated
method TOCReference (line 26) | public TOCReference(String name, Resource resource) {
method TOCReference (line 30) | public TOCReference(String name, Resource resource, String fragmentId) {
method TOCReference (line 34) | public TOCReference(String title, Resource resource, String fragmentId,
method getComparatorByTitleIgnoreCase (line 39) | @SuppressWarnings("unused")
method getChildren (line 44) | public List<TOCReference> getChildren() {
method addChildSection (line 48) | public TOCReference addChildSection(TOCReference childSection) {
method setChildren (line 53) | public void setChildren(List<TOCReference> children) {
FILE: src/main/java/me/ag2s/epublib/domain/TableOfContents.java
class TableOfContents (line 23) | public class TableOfContents implements Serializable {
method TableOfContents (line 31) | public TableOfContents() {
method TableOfContents (line 35) | public TableOfContents(List<TOCReference> tocReferences) {
method getTocReferences (line 39) | public List<TOCReference> getTocReferences() {
method setTocReferences (line 43) | public void setTocReferences(List<TOCReference> tocReferences) {
method addSection (line 51) | @SuppressWarnings("unused")
method addSection (line 64) | public TOCReference addSection(Resource resource, String path,
method findTocReferenceByTitle (line 77) | private static TOCReference findTocReferenceByTitle(String title,
method addSection (line 103) | public TOCReference addSection(Resource resource, String[] pathElement...
method addSection (line 137) | @SuppressWarnings("unused")
method paddTOCReferences (line 164) | private void paddTOCReferences(List<TOCReference> currentTocReferences,
method createSectionTitle (line 175) | private String createSectionTitle(int[] pathElements, int pathPos,
method addTOCReference (line 192) | public TOCReference addTOCReference(TOCReference tocReference) {
method getAllUniqueResources (line 205) | public List<Resource> getAllUniqueResources() {
method getAllUniqueResources (line 212) | private static void getAllUniqueResources(Set<String> uniqueHrefs,
method size (line 229) | public int size() {
method getTotalSize (line 233) | private static int getTotalSize(Collection<TOCReference> tocReferences) {
method calculateDepth (line 245) | public int calculateDepth() {
method calculateDepth (line 249) | private int calculateDepth(List<TOCReference> tocReferences,
FILE: src/main/java/me/ag2s/epublib/domain/TitledResourceReference.java
class TitledResourceReference (line 8) | public class TitledResourceReference extends ResourceReference
method TitledResourceReference (line 20) | @Deprecated
method TitledResourceReference (line 26) | public TitledResourceReference(Resource resource, String title) {
method TitledResourceReference (line 30) | public TitledResourceReference(Resource resource, String title,
method getFragmentId (line 37) | public String getFragmentId() {
method setFragmentId (line 41) | public void setFragmentId(String fragmentId) {
method getTitle (line 45) | public String getTitle() {
method setTitle (line 49) | public void setTitle(String title) {
method getCompleteHref (line 61) | public String getCompleteHref() {
method getResource (line 70) | @Override
method setResource (line 80) | public void setResource(Resource resource, String fragmentId) {
method setResource (line 88) | public void setResource(Resource resource) {
FILE: src/main/java/me/ag2s/epublib/epub/BookProcessor.java
type BookProcessor (line 12) | public interface BookProcessor {
method processBook (line 19) | EpubBook processBook(EpubBook book);
FILE: src/main/java/me/ag2s/epublib/epub/BookProcessorPipeline.java
class BookProcessorPipeline (line 17) | @SuppressWarnings("unused declaration")
method BookProcessorPipeline (line 23) | public BookProcessorPipeline() {
method BookProcessorPipeline (line 27) | public BookProcessorPipeline(List<BookProcessor> bookProcessingPipelin...
method processBook (line 31) | @Override
method addBookProcessor (line 47) | public void addBookProcessor(BookProcessor bookProcessor) {
method addBookProcessors (line 54) | public void addBookProcessors(Collection<BookProcessor> bookProcessors) {
method getBookProcessors (line 62) | public List<BookProcessor> getBookProcessors() {
method setBookProcessingPipeline (line 67) | public void setBookProcessingPipeline(
FILE: src/main/java/me/ag2s/epublib/epub/DOMUtil.java
class DOMUtil (line 20) | class DOMUtil {
method getAttribute (line 30) | public static String getAttribute(Element element, String namespace,
method getElementsTextChild (line 47) | public static List<String> getElementsTextChild(Element parentElement,
method getFindAttributeValue (line 71) | public static String getFindAttributeValue(Document document,
method getElementsByTagNameNS (line 95) | public static NodeList getElementsByTagNameNS(Element parentElement,
method getElementsByTagNameNS (line 115) | public static NodeList getElementsByTagNameNS(Document parentElement,
method getFirstElementByTagNameNS (line 136) | public static Element getFirstElementByTagNameNS(Element parentElement,
method getTextChildrenContent (line 160) | public static String getTextChildrenContent(Element parentElement) {
FILE: src/main/java/me/ag2s/epublib/epub/EpubProcessorSupport.java
class EpubProcessorSupport (line 28) | public class EpubProcessorSupport {
class EntityResolverImpl (line 38) | static class EntityResolverImpl implements EntityResolver {
method resolveEntity (line 42) | @Override
method init (line 69) | private static void init() {
method createXmlSerializer (line 76) | public static XmlSerializer createXmlSerializer(OutputStream out)
method createXmlSerializer (line 82) | public static XmlSerializer createXmlSerializer(Writer out) {
method getEntityResolver (line 114) | public static EntityResolver getEntityResolver() {
method getDocumentBuilderFactory (line 118) | @SuppressWarnings("unused")
method createDocumentBuilder (line 128) | public static DocumentBuilder createDocumentBuilder() {
FILE: src/main/java/me/ag2s/epublib/epub/EpubReader.java
class EpubReader (line 27) | @SuppressWarnings("ALL")
method readEpub (line 33) | public EpubBook readEpub(InputStream in) throws IOException {
method readEpub (line 37) | public EpubBook readEpub(ZipInputStream in) throws IOException {
method readEpub (line 41) | public EpubBook readEpub(ZipFile zipfile) throws IOException {
method readEpub (line 53) | public EpubBook readEpub(InputStream in, String encoding) throws IOExc...
method readEpubLazy (line 66) | public EpubBook readEpubLazy(ZipFile zipFile, String encoding)
method readEpub (line 72) | public EpubBook readEpub(ZipInputStream in, String encoding) throws IO...
method readEpub (line 76) | public EpubBook readEpub(ZipFile in, String encoding) throws IOExcepti...
method readEpubLazy (line 89) | public EpubBook readEpubLazy(ZipFile zipFile, String encoding,
method readEpub (line 96) | public EpubBook readEpub(Resources resources) {
method readEpub (line 100) | public EpubBook readEpub(Resources resources, EpubBook result) {
method postProcessBook (line 116) | private EpubBook postProcessBook(EpubBook book) {
method processNcxResource (line 123) | private Resource processNcxResource(Resource packageResource, EpubBook...
method processPackageResource (line 133) | private Resource processPackageResource(String packageResourceHref, Ep...
method getPackageResourceHref (line 145) | private String getPackageResourceHref(Resources resources) {
method handleMimeType (line 169) | private void handleMimeType(EpubBook result, Resources resources) {
FILE: src/main/java/me/ag2s/epublib/epub/EpubWriter.java
class EpubWriter (line 24) | public class EpubWriter {
method EpubWriter (line 33) | public EpubWriter() {
method EpubWriter (line 38) | public EpubWriter(BookProcessor bookProcessor) {
method write (line 43) | public void write(EpubBook book, OutputStream out) throws IOException {
method processBook (line 54) | private EpubBook processBook(EpubBook book) {
method initTOCResource (line 61) | private void initTOCResource(EpubBook book) {
method writeResources (line 85) | private void writeResources(EpubBook book, ZipOutputStream resultStrea...
method writeResource (line 97) | private void writeResource(Resource resource, ZipOutputStream resultSt...
method writePackageDocument (line 114) | private void writePackageDocument(EpubBook book, ZipOutputStream resul...
method writeContainer (line 131) | private void writeContainer(ZipOutputStream resultStream) throws IOExc...
method writeMimeType (line 151) | private void writeMimeType(ZipOutputStream resultStream) throws IOExce...
method calculateCrc (line 161) | private long calculateCrc(byte[] data) {
method getNcxId (line 167) | String getNcxId() {
method getNcxHref (line 171) | String getNcxHref() {
method getNcxMediaType (line 175) | String getNcxMediaType() {
method getBookProcessor (line 180) | @SuppressWarnings("unused")
method setBookProcessor (line 185) | @SuppressWarnings("unused")
FILE: src/main/java/me/ag2s/epublib/epub/HtmlProcessor.java
type HtmlProcessor (line 5) | @SuppressWarnings("unused")
method processHtmlResource (line 8) | void processHtmlResource(Resource resource, OutputStream out);
FILE: src/main/java/me/ag2s/epublib/epub/NCXDocumentV2.java
class NCXDocumentV2 (line 34) | public class NCXDocumentV2 {
type NCXTags (line 45) | private interface NCXTags {
type NCXAttributes (line 59) | private interface NCXAttributes {
type NCXAttributeValues (line 70) | private interface NCXAttributeValues {
method read (line 77) | @SuppressWarnings("unused")
method readTOCReferences (line 110) | static List<TOCReference> readTOCReferences(NodeList navpoints,
method readTOCReference (line 131) | static TOCReference readTOCReference(Element navpointElement, EpubBook...
method readNavReference (line 163) | private static String readNavReference(Element navpointElement) {
method readNavLabel (line 181) | private static String readNavLabel(Element navpointElement) {
method write (line 191) | @SuppressWarnings("unused")
method write (line 211) | public static void write(XmlSerializer xmlSerializer, EpubBook book)
method createNCXResource (line 217) | public static Resource createNCXResource(EpubBook book)
method createNCXResource (line 224) | public static Resource createNCXResource(List<Identifier> identifiers,
method write (line 234) | public static void write(XmlSerializer serializer,
method writeMetaElement (line 285) | private static void writeMetaElement(String dtbName, String content,
method writeNavPoints (line 297) | private static int writeNavPoints(List<TOCReference> tocReferences,
method writeNavPointStart (line 319) | private static void writeNavPointStart(TOCReference tocReference,
method writeNavPointEnd (line 340) | @SuppressWarnings("unused")
FILE: src/main/java/me/ag2s/epublib/epub/NCXDocumentV3.java
class NCXDocumentV3 (line 34) | public class NCXDocumentV3 {
type XHTMLTgs (line 47) | private interface XHTMLTgs {
type XHTMLAttributes (line 63) | private interface XHTMLAttributes {
type XHTMLAttributeValues (line 79) | private interface XHTMLAttributeValues {
method read (line 96) | @SuppressWarnings("unused")
method doToc (line 144) | private static List<TOCReference> doToc(Node n, EpubBook book) {
method readTOCReferences (line 160) | static List<TOCReference> readTOCReferences(NodeList navpoints,
method readTOCReference (line 187) | static TOCReference readTOCReference(Element navpointElement, EpubBook...
method readNavReference (line 232) | private static String readNavReference(Element navpointElement) {
method readNavLabel (line 261) | private static String readNavLabel(Element navpointElement) {
method createNCXResource (line 281) | public static Resource createNCXResource(EpubBook book)
method createNCXResource (line 288) | public static Resource createNCXResource(List<Identifier> identifiers,
method write (line 310) | public static void write(XmlSerializer xmlSerializer, EpubBook book)
method write (line 325) | @SuppressWarnings("unused")
method writeNavPoints (line 369) | private static int writeNavPoints(List<TOCReference> tocReferences,
method writeNavPointStart (line 395) | private static void writeNavPointStart(TOCReference tocReference, XmlS...
method writeNavPointEnd (line 406) | @SuppressWarnings("unused")
method writeLabel (line 412) | protected static void writeLabel(String title, String href, XmlSeriali...
method writeLabel (line 421) | protected static void writeLabel(String title, XmlSerializer serialize...
method writeLiStart (line 427) | private static void writeLiStart(XmlSerializer serializer) throws IOEx...
method writeLiEnd (line 433) | private static void writeLiEnd(XmlSerializer serializer) throws IOExce...
method writeOlStart (line 439) | private static void writeOlStart(XmlSerializer serializer) throws IOEx...
method writeOlSEnd (line 445) | private static void writeOlSEnd(XmlSerializer serializer) throws IOExc...
method writeHead (line 451) | private static void writeHead(String title, XmlSerializer serializer) ...
FILE: src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java
class PackageDocumentBase (line 10) | public class PackageDocumentBase {
type DCTags (line 25) | protected interface DCTags {
type DCAttributes (line 44) | protected interface DCAttributes {
type OPFTags (line 50) | protected interface OPFTags {
type OPFAttributes (line 63) | protected interface OPFAttributes {
type OPFValues (line 89) | protected interface OPFValues {
FILE: src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataReader.java
class PackageDocumentMetadataReader (line 29) | class PackageDocumentMetadataReader extends PackageDocumentBase {
method readMetadata (line 33) | public static Metadata readMetadata(Document packageDocument) {
method readOtherProperties (line 84) | private static Map<QName, String> readOtherProperties(
method readMetaProperties (line 110) | private static Map<String, String> readMetaProperties(
method getBookIdId (line 125) | private static String getBookIdId(Document document) {
method readCreators (line 136) | private static List<Author> readCreators(Element metadataElement) {
method readContributors (line 140) | private static List<Author> readContributors(Element metadataElement) {
method readAuthors (line 144) | private static List<Author> readAuthors(String authorTag,
method readDates (line 160) | private static List<Date> readDates(Element metadataElement) {
method createAuthor (line 180) | private static Author createAuthor(Element authorElement) {
method readIdentifiers (line 199) | private static List<Identifier> readIdentifiers(Element metadataElemen...
FILE: src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java
class PackageDocumentMetadataWriter (line 18) | public class PackageDocumentMetadataWriter extends PackageDocumentBase {
method writeMetaData (line 29) | public static void writeMetaData(EpubBook book, XmlSerializer serializer)
method writeSimpleMetdataElements (line 135) | private static void writeSimpleMetdataElements(String tagName,
method writeIdentifiers (line 160) | private static void writeIdentifiers(List<Identifier> identifiers,
FILE: src/main/java/me/ag2s/epublib/epub/PackageDocumentReader.java
class PackageDocumentReader (line 38) | public class PackageDocumentReader extends PackageDocumentBase {
method read (line 45) | public static void read(
method readManifest (line 93) | @SuppressWarnings("unused")
method readGuide (line 156) | @SuppressWarnings("unused")
method fixHrefs (line 216) | static Resources fixHrefs(String packageHref,
method readSpine (line 241) | private static Spine readSpine(Document packageDocument, Resources res...
method generateSpineFromResources (line 304) | private static Spine generateSpineFromResources(Resources resources) {
method findTableOfContentsResource (line 330) | static Resource findTableOfContentsResource(String tocResourceId,
method findCoverHrefs (line 384) | static Set<String> findCoverHrefs(Document packageDocument) {
method readCover (line 424) | private static void readCover(Document packageDocument, EpubBook book) {
FILE: src/main/java/me/ag2s/epublib/epub/PackageDocumentWriter.java
class PackageDocumentWriter (line 25) | public class PackageDocumentWriter extends PackageDocumentBase {
method write (line 29) | public static void write(EpubWriter epubWriter, XmlSerializer serializer,
method writeSpine (line 66) | @SuppressWarnings("unused")
method writeManifest (line 94) | private static void writeManifest(EpubBook book, EpubWriter epubWriter,
method getAllResourcesSortById (line 125) | private static List<Resource> getAllResourcesSortById(EpubBook book) {
method writeItem (line 141) | private static void writeItem(EpubBook book, Resource resource,
method writeSpineItems (line 188) | @SuppressWarnings("unused")
method writeGuide (line 205) | private static void writeGuide(EpubBook book, EpubWriter epubWriter,
method ensureCoverPageGuideReferenceWritten (line 217) | @SuppressWarnings("unused")
method writeGuideReference (line 233) | private static void writeGuideReference(GuideReference reference,
FILE: src/main/java/me/ag2s/epublib/epub/ResourcesLoader.java
class ResourcesLoader (line 29) | public class ResourcesLoader {
method loadResources (line 47) | public static Resources loadResources(ZipFile zipFile,
method shouldLoadLazy (line 99) | private static boolean shouldLoadLazy(String href,
method loadResources (line 120) | public static Resources loadResources(ZipInputStream zipInputStream,
method getNextZipEntry (line 149) | private static ZipEntry getNextZipEntry(ZipInputStream zipInputStream)
method loadResources (line 180) | public static Resources loadResources(ZipFile zipFile, String defaultH...
FILE: src/main/java/me/ag2s/epublib/util/CollectionUtil.java
class CollectionUtil (line 8) | public class CollectionUtil {
class IteratorEnumerationAdapter (line 16) | private static class IteratorEnumerationAdapter<T> implements Enumerat...
method IteratorEnumerationAdapter (line 20) | public IteratorEnumerationAdapter(Iterator<T> iter) {
method hasMoreElements (line 24) | @Override
method nextElement (line 29) | @Override
method createEnumerationFromIterator (line 41) | @SuppressWarnings("unused")
method first (line 55) | public static <T> T first(List<T> list) {
method isEmpty (line 68) | public static boolean isEmpty(Collection<?> collection) {
FILE: src/main/java/me/ag2s/epublib/util/IOUtil.java
class IOUtil (line 31) | public class IOUtil {
method toByteArray (line 66) | public static byte[] toByteArray(Reader in, String encoding)
method toByteArray (line 81) | public static byte[] toByteArray(InputStream in) throws IOException {
method toByteArray (line 100) | public static byte[] toByteArray(InputStream in, int size)
method calcNewNrReadSize (line 134) | protected static int calcNewNrReadSize(int nrRead, int totalNrNread) {
method copy (line 146) | public static void copy(InputStream in, OutputStream result) throws IO...
method copy (line 165) | public static long copy(final InputStream input, final OutputStream ou...
method copy (line 186) | @Deprecated
method copy (line 208) | public static void copy(final InputStream input, final Writer output, ...
method copy (line 236) | public static void copy(final InputStream input, final Writer output, ...
method copy (line 259) | public static long copy(final Reader input, final Appendable output) t...
method copy (line 278) | public static long copy(final Reader input, final Appendable output, f...
method copy (line 309) | @Deprecated
method copy (line 338) | public static void copy(final Reader input, final OutputStream output,...
method copy (line 373) | public static void copy(final Reader input, final OutputStream output,...
method copy (line 396) | public static int copy(final Reader input, final Writer output) throws...
method copyLarge (line 422) | public static long copyLarge(final InputStream input, final OutputStre...
method copyLarge (line 442) | public static long copyLarge(final InputStream input, final OutputStre...
method copyLarge (line 480) | public static long copyLarge(final InputStream input, final OutputStre...
method copyLarge (line 509) | public static long copyLarge(final InputStream input, final OutputStre...
method copyLarge (line 550) | public static long copyLarge(final Reader input, final Writer output) ...
method copyLarge (line 569) | public static long copyLarge(final Reader input, final Writer output, ...
method copyLarge (line 598) | public static long copyLarge(final Reader input, final Writer output, ...
method copyLarge (line 622) | public static long copyLarge(final Reader input, final Writer output, ...
method skip (line 669) | public static long skip(final InputStream input, final long toSkip) th...
method skip (line 703) | public static long skip(final ReadableByteChannel input, final long to...
method skip (line 742) | public static long skip(final Reader input, final long toSkip) throws ...
method skipFully (line 785) | public static void skipFully(final InputStream input, final long toSki...
method skipFully (line 805) | public static void skipFully(final ReadableByteChannel input, final lo...
method skipFully (line 834) | public static void skipFully(final Reader input, final long toSkip) th...
method length (line 848) | public static int length(final byte[] array) {
method length (line 859) | public static int length(final char[] array) {
method length (line 870) | public static int length(final CharSequence csq) {
method length (line 881) | public static int length(final Object[] array) {
method close (line 892) | public static void close(final Closeable closeable) throws IOException {
method close (line 905) | public static void close(final Closeable... closeables) throws IOExcep...
method close (line 921) | public static void close(final Closeable closeable, final IOConsumer<I...
method close (line 939) | public static void close(final URLConnection conn) {
method Stream2String (line 945) | @SuppressWarnings("unused")
FILE: src/main/java/me/ag2s/epublib/util/NoCloseOutputStream.java
class NoCloseOutputStream (line 14) | @SuppressWarnings("unused")
method NoCloseOutputStream (line 19) | public NoCloseOutputStream(OutputStream outputStream) {
method write (line 23) | @Override
method close (line 31) | public void close() {
FILE: src/main/java/me/ag2s/epublib/util/NoCloseWriter.java
class NoCloseWriter (line 14) | @SuppressWarnings("unused")
method NoCloseWriter (line 19) | public NoCloseWriter(Writer writer) {
method close (line 23) | @Override
method flush (line 27) | @Override
method write (line 32) | @Override
FILE: src/main/java/me/ag2s/epublib/util/ResourceUtil.java
class ResourceUtil (line 29) | public class ResourceUtil {
method createChapterResource (line 38) | public static Resource createChapterResource(String title, String txt,...
method createPublicResource (line 51) | public static Resource createPublicResource(String name, String author...
method createResource (line 68) | @SuppressWarnings("unused")
method createResource (line 86) | @SuppressWarnings("unused")
method createResource (line 103) | public static Resource createResource(ZipEntry zipEntry,
method createResource (line 109) | public static Resource createResource(ZipEntry zipEntry,
method recode (line 124) | @SuppressWarnings("unused")
method getInputSource (line 133) | @SuppressWarnings("unused")
method getAsDocument (line 150) | public static Document getAsDocument(Resource resource)
method getAsDocument (line 166) | public static Document getAsDocument(Resource resource,
FILE: src/main/java/me/ag2s/epublib/util/StringUtil.java
class StringUtil (line 17) | public class StringUtil {
method collapsePathDots (line 27) | public static String collapsePathDots(String path) {
method isNotBlank (line 61) | public static boolean isNotBlank(String text) {
method isBlank (line 70) | public static boolean isBlank(String text) {
method isEmpty (line 88) | public static boolean isEmpty(String text) {
method endsWithIgnoreCase (line 100) | public static boolean endsWithIgnoreCase(String source, String suffix) {
method defaultIfNull (line 120) | public static String defaultIfNull(String text) {
method defaultIfNull (line 131) | public static String defaultIfNull(String text, String defaultValue) {
method equals (line 145) | public static boolean equals(String text1, String text2) {
method toString (line 158) | public static String toString(Object... keyValues) {
method hashCode (line 183) | public static int hashCode(String... values) {
method substringBefore (line 201) | public static String substringBefore(String text, char separator) {
method substringBeforeLast (line 223) | public static String substringBeforeLast(String text, char separator) {
method substringAfterLast (line 244) | public static String substringAfterLast(String text, char separator) {
method substringAfter (line 264) | public static String substringAfter(String text, char c) {
method formatHtml (line 275) | public static String formatHtml(String text) {
FILE: src/main/java/me/ag2s/epublib/util/commons/io/BOMInputStream.java
class BOMInputStream (line 89) | public class BOMInputStream extends ProxyInputStream {
method BOMInputStream (line 108) | @SuppressWarnings("unused")
method BOMInputStream (line 121) | @SuppressWarnings("unused")
method BOMInputStream (line 134) | @SuppressWarnings("unused")
method BOMInputStream (line 158) | public BOMInputStream(final InputStream delegate, final boolean includ...
method hasBOM (line 180) | @SuppressWarnings("unused")
method hasBOM (line 196) | @SuppressWarnings("unused")
method getBOM (line 212) | public ByteOrderMark getBOM() throws IOException {
method getBOMCharsetName (line 249) | public String getBOMCharsetName() throws IOException {
method readFirstBytes (line 263) | private int readFirstBytes() throws IOException {
method find (line 273) | private ByteOrderMark find() {
method matches (line 289) | private boolean matches(final ByteOrderMark bom) {
method read (line 313) | @Override
method read (line 332) | @Override
method read (line 357) | @Override
method mark (line 368) | @Override
method reset (line 381) | @Override
method skip (line 400) | @Override
FILE: src/main/java/me/ag2s/epublib/util/commons/io/ByteOrderMark.java
class ByteOrderMark (line 32) | public class ByteOrderMark implements Serializable {
method ByteOrderMark (line 78) | public ByteOrderMark(final String charsetName, final int... bytes) {
method getCharsetName (line 95) | public String getCharsetName() {
method length (line 104) | public int length() {
method get (line 114) | public int get(final int pos) {
method getBytes (line 123) | public byte[] getBytes() {
method equals (line 138) | @Override
method hashCode (line 161) | @Override
method toString (line 175) | @Override
FILE: src/main/java/me/ag2s/epublib/util/commons/io/IOConsumer.java
type IOConsumer (line 30) | @FunctionalInterface
method accept (line 39) | void accept(T t) throws IOException;
method andThen (line 51) | @SuppressWarnings("unused")
FILE: src/main/java/me/ag2s/epublib/util/commons/io/ProxyInputStream.java
class ProxyInputStream (line 44) | public abstract class ProxyInputStream extends FilterInputStream {
method ProxyInputStream (line 51) | public ProxyInputStream(final InputStream proxy) {
method read (line 62) | @Override
method read (line 82) | @Override
method read (line 104) | @Override
method skip (line 124) | @Override
method available (line 140) | @Override
method close (line 155) | @Override
method mark (line 165) | @Override
method reset (line 175) | @Override
method markSupported (line 189) | @Override
method beforeRead (line 211) | @SuppressWarnings("unused")
method afterRead (line 233) | @SuppressWarnings("unused")
method handleIOException (line 248) | protected void handleIOException(final IOException e) throws IOExcepti...
FILE: src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReader.java
class XmlStreamReader (line 73) | public class XmlStreamReader extends Reader {
method getDefaultEncoding (line 128) | public String getDefaultEncoding() {
method XmlStreamReader (line 144) | @SuppressWarnings("unused")
method XmlStreamReader (line 160) | public XmlStreamReader(final InputStream inputStream) throws IOExcepti...
method XmlStreamReader (line 191) | public XmlStreamReader(final InputStream inputStream, final boolean le...
method XmlStreamReader (line 223) | public XmlStreamReader(final InputStream inputStream, final boolean le...
method XmlStreamReader (line 250) | @SuppressWarnings("unused")
method XmlStreamReader (line 274) | public XmlStreamReader(final URLConnection conn, final String defaultE...
method XmlStreamReader (line 307) | public XmlStreamReader(final InputStream inputStream, final String htt...
method XmlStreamReader (line 346) | public XmlStreamReader(final InputStream inputStream, final String htt...
method XmlStreamReader (line 389) | public XmlStreamReader(final InputStream inputStream, final String htt...
method getEncoding (line 399) | public String getEncoding() {
method read (line 411) | @Override
method close (line 421) | @Override
method doRawStream (line 436) | private String doRawStream(final BOMInputStream bom, final BOMInputStr...
method processHttpStream (line 462) | private String processHttpStream(final BOMInputStream bom, final BOMIn...
method doLenientDetection (line 486) | private String doLenientDetection(String httpContentType,
method calculateRawEncoding (line 517) | String calculateRawEncoding(final String bomEnc, final String xmlGuess...
method calculateHttpEncoding (line 589) | String calculateHttpEncoding(final String httpContentType,
method getContentTypeMime (line 663) | static String getContentTypeMime(final String httpContentType) {
method getContentTypeEncoding (line 687) | static String getContentTypeEncoding(final String httpContentType) {
method getXmlProlog (line 716) | private static String getXmlProlog(final InputStream inputStream, fina...
method isAppXml (line 769) | static boolean isAppXml(final String mime) {
method isTextXml (line 784) | static boolean isTextXml(final String mime) {
FILE: src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReaderException.java
class XmlStreamReaderException (line 35) | public class XmlStreamReaderException extends IOException {
method XmlStreamReaderException (line 61) | public XmlStreamReaderException(final String msg, final String bomEnc,
method XmlStreamReaderException (line 80) | public XmlStreamReaderException(final String msg, final String ctMime,...
method getBomEncoding (line 95) | public String getBomEncoding() {
method getXmlGuessEncoding (line 104) | public String getXmlGuessEncoding() {
method getXmlEncoding (line 113) | public String getXmlEncoding() {
method getContentTypeMime (line 124) | public String getContentTypeMime() {
method getContentTypeEncoding (line 136) | public String getContentTypeEncoding() {
FILE: src/main/java/me/ag2s/umdlib/domain/UmdBook.java
class UmdBook (line 8) | public class UmdBook {
method getNum (line 10) | public int getNum() {
method setNum (line 14) | public void setNum(int num) {
method buildUmd (line 40) | public void buildUmd(OutputStream os) throws IOException {
method getHeader (line 49) | public UmdHeader getHeader() {
method setHeader (line 53) | public void setHeader(UmdHeader header) {
method getChapters (line 57) | public UmdChapters getChapters() {
method setChapters (line 61) | public void setChapters(UmdChapters chapters) {
method getCover (line 65) | public UmdCover getCover() {
method setCover (line 69) | public void setCover(UmdCover cover) {
method getEnd (line 73) | public UmdEnd getEnd() {
method setEnd (line 77) | public void setEnd(UmdEnd end) {
FILE: src/main/java/me/ag2s/umdlib/domain/UmdChapters.java
class UmdChapters (line 21) | public class UmdChapters {
method getTitles (line 26) | public List<byte[]> getTitles() {
method addTitle (line 34) | public void addTitle(String s){
method addTitle (line 37) | public void addTitle(byte[] s){
method addContentLength (line 40) | public void addContentLength(Integer integer){
method getContentLength (line 43) | public int getContentLength(int index){
method getContent (line 47) | public byte[] getContent(int index) {
method getContentString (line 59) | public String getContentString(int index) {
method getTitle (line 63) | public String getTitle(int index){
method buildChapters (line 68) | public void buildChapters(WrapOutputStream wos) throws IOException {
method writeChaptersHead (line 75) | private void writeChaptersHead(WrapOutputStream wos) throws IOException {
method writeChaptersContentOffset (line 80) | private void writeChaptersContentOffset(WrapOutputStream wos) throws I...
method writeChaptersTitles (line 95) | private void writeChaptersTitles(WrapOutputStream wos) throws IOExcept...
method writeChaptersChunks (line 116) | private void writeChaptersChunks(WrapOutputStream wos) throws IOExcept...
method addChapter (line 168) | public void addChapter(String title, String content) {
method addFile (line 179) | public void addFile(File f, String title) throws IOException {
method addFile (line 185) | public void addFile(File f) throws IOException {
method clearChapters (line 194) | public void clearChapters() {
method getTotalContentLen (line 200) | public int getTotalContentLen() {
method setTotalContentLen (line 204) | public void setTotalContentLen(int totalContentLen) {
FILE: src/main/java/me/ag2s/umdlib/domain/UmdCover.java
class UmdCover (line 25) | public class UmdCover {
method UmdCover (line 32) | public UmdCover() {
method UmdCover (line 35) | public UmdCover(byte[] coverData) {
method load (line 39) | public void load(File f) throws IOException {
method load (line 43) | public void load(String fileName) throws IOException {
method initDefaultCover (line 47) | public void initDefaultCover(String title) throws IOException {
method buildCover (line 75) | public void buildCover(WrapOutputStream wos) throws IOException {
method getCoverData (line 88) | public byte[] getCoverData() {
method setCoverData (line 92) | public void setCoverData(byte[] coverData) {
FILE: src/main/java/me/ag2s/umdlib/domain/UmdEnd.java
class UmdEnd (line 13) | public class UmdEnd {
method buildEnd (line 15) | public void buildEnd(WrapOutputStream wos) throws IOException {
FILE: src/main/java/me/ag2s/umdlib/domain/UmdHeader.java
class UmdHeader (line 17) | public class UmdHeader {
method getUmdType (line 18) | public byte getUmdType() {
method setUmdType (line 22) | public void setUmdType(byte umdType) {
method buildHeader (line 52) | public void buildHeader(WrapOutputStream wos) throws IOException {
method buildType (line 70) | public void buildType(WrapOutputStream wos, byte type, String content)...
method getTitle (line 84) | public String getTitle() {
method setTitle (line 88) | public void setTitle(String title) {
method getAuthor (line 92) | public String getAuthor() {
method setAuthor (line 96) | public void setAuthor(String author) {
method getBookMan (line 100) | public String getBookMan() {
method setBookMan (line 104) | public void setBookMan(String bookMan) {
method getShopKeeper (line 108) | public String getShopKeeper() {
method setShopKeeper (line 112) | public void setShopKeeper(String shopKeeper) {
method getYear (line 116) | public String getYear() {
method setYear (line 120) | public void setYear(String year) {
method getMonth (line 124) | public String getMonth() {
method setMonth (line 128) | public void setMonth(String month) {
method getDay (line 132) | public String getDay() {
method setDay (line 136) | public void setDay(String day) {
method getBookType (line 140) | public String getBookType() {
method setBookType (line 144) | public void setBookType(String bookType) {
method toString (line 148) | @Override
FILE: src/main/java/me/ag2s/umdlib/tool/StreamReader.java
class StreamReader (line 7) | public class StreamReader {
method getOffset (line 10) | public long getOffset() {
method setOffset (line 14) | public void setOffset(long offset) {
method getSize (line 18) | public long getSize() {
method setSize (line 22) | public void setSize(long size) {
method incCount (line 29) | private void incCount(int value) {
method StreamReader (line 36) | public StreamReader(InputStream inputStream) throws IOException {
method readUint8 (line 41) | public short readUint8() throws IOException {
method readByte (line 49) | public byte readByte() throws IOException {
method readBytes (line 55) | public byte[] readBytes(int len) throws IOException {
method readHex (line 65) | public String readHex(int len) throws IOException {
method readShort (line 76) | public short readShort() throws IOException {
method readShortLe (line 83) | public short readShortLe() throws IOException {
method readInt (line 90) | public int readInt() throws IOException {
method readIntLe (line 98) | public int readIntLe() throws IOException {
method skip (line 106) | public void skip(int len) throws IOException {
method read (line 111) | public byte[] read(byte[] b) throws IOException {
method read (line 117) | public byte[] read(byte[] b, int off, int len) throws IOException {
FILE: src/main/java/me/ag2s/umdlib/tool/UmdUtils.java
class UmdUtils (line 16) | public class UmdUtils {
method stringToUnicodeBytes (line 27) | public static byte[] stringToUnicodeBytes(String s) {
method unicodeBytesToString (line 56) | public static String unicodeBytesToString(byte[] bytes){
method toHex (line 79) | public static String toHex(byte[] bArr){
method decompress (line 99) | public static byte[] decompress(byte[] compress) throws Exception {
method saveFile (line 119) | public static void saveFile(File f, byte[] content) throws IOException {
method readFile (line 130) | public static byte[] readFile(File f) throws IOException {
method genRandomBytes (line 148) | public static byte[] genRandomBytes(int len) {
FILE: src/main/java/me/ag2s/umdlib/tool/WrapOutputStream.java
class WrapOutputStream (line 6) | public class WrapOutputStream extends OutputStream {
method WrapOutputStream (line 11) | public WrapOutputStream(OutputStream os) {
method incCount (line 15) | private void incCount(int value) {
method writeInt (line 24) | public void writeInt(int v) throws IOException {
method writeByte (line 32) | public void writeByte(byte b) throws IOException {
method writeByte (line 36) | public void writeByte(int n) throws IOException {
method writeBytes (line 40) | public void writeBytes(byte ... bytes) throws IOException {
method writeBytes (line 44) | public void writeBytes(int ... vals) throws IOException {
method write (line 50) | public void write(byte[] b, int off, int len) throws IOException {
method write (line 55) | public void write(byte[] b) throws IOException {
method write (line 60) | public void write(int b) throws IOException {
method close (line 67) | public void close() throws IOException {
method flush (line 71) | public void flush() throws IOException {
method equals (line 75) | public boolean equals(Object obj) {
method hashCode (line 79) | public int hashCode() {
method toString (line 83) | public String toString() {
method getWritten (line 87) | public int getWritten() {
FILE: src/main/java/me/ag2s/umdlib/umd/UmdReader.java
class UmdReader (line 21) | public class UmdReader {
method read (line 29) | public synchronized UmdBook read(InputStream inputStream) throws Excep...
method ReadAdditionalSection (line 69) | private void ReadAdditionalSection(short segType, int additionalCheckN...
method ReadSection (line 121) | public void ReadSection(short segType, byte segFlag, short length, Str...
method toString (line 216) | @Override
FILE: src/main/java/org/kxml2/io/KXmlParser.java
class KXmlParser (line 33) | public class KXmlParser implements XmlPullParser {
method KXmlParser (line 98) | public KXmlParser() {
method isProp (line 103) | private final boolean isProp(String n1, boolean prop, String n2) {
method adjustNsp (line 112) | private final boolean adjustNsp() throws XmlPullParserException {
method ensureCapacity (line 224) | private final String[] ensureCapacity(String[] arr, int required) {
method error (line 232) | private final void error(String desc) throws XmlPullParserException {
method exception (line 241) | private final void exception(String desc) throws XmlPullParserException {
method nextImpl (line 252) | private final void nextImpl() throws IOException, XmlPullParserExcepti...
method parseLegacy (line 340) | private final int parseLegacy(boolean push)
method parseDoctype (line 473) | private final void parseDoctype(boolean push)
method parseEndTag (line 512) | private final void parseEndTag()
method peekType (line 555) | private final int peekType() throws IOException {
method get (line 576) | private final String get(int pos) {
method push (line 588) | private final void push(int c) {
method parseStartTag (line 610) | private final void parseStartTag(boolean xmldecl)
method pushEntity (line 737) | private final void pushEntity()
method pushText (line 808) | private final void pushText(int delimiter, boolean resolveEntities)
method read (line 845) | private final void read(char c)
method read (line 852) | private final int read() throws IOException {
method peek (line 880) | private final int peek(int pos) throws IOException {
method readName (line 919) | private final String readName()
method skip (line 950) | private final void skip() throws IOException {
method setInput (line 962) | public void setInput(Reader reader) throws XmlPullParserException {
method setInput (line 992) | public void setInput(InputStream is, String _enc)
method getFeature (line 1114) | public boolean getFeature(String feature) {
method getInputEncoding (line 1123) | public String getInputEncoding() {
method defineEntityReplacementText (line 1127) | public void defineEntityReplacementText(String entity, String value)
method getProperty (line 1134) | public Object getProperty(String property) {
method getNamespaceCount (line 1144) | public int getNamespaceCount(int depth) {
method getNamespacePrefix (line 1150) | public String getNamespacePrefix(int pos) {
method getNamespaceUri (line 1154) | public String getNamespaceUri(int pos) {
method getNamespace (line 1158) | public String getNamespace(String prefix) {
method getDepth (line 1176) | public int getDepth() {
method getPositionDescription (line 1180) | public String getPositionDescription() {
method getLineNumber (line 1232) | public int getLineNumber() {
method getColumnNumber (line 1236) | public int getColumnNumber() {
method isWhitespace (line 1240) | public boolean isWhitespace() throws XmlPullParserException {
method getText (line 1246) | public String getText() {
method getTextCharacters (line 1251) | public char[] getTextCharacters(int[] poslen) {
method getNamespace (line 1268) | public String getNamespace() {
method getName (line 1272) | public String getName() {
method getPrefix (line 1276) | public String getPrefix() {
method isEmptyElementTag (line 1280) | public boolean isEmptyElementTag() throws XmlPullParserException {
method getAttributeCount (line 1286) | public int getAttributeCount() {
method getAttributeType (line 1290) | public String getAttributeType(int index) {
method isAttributeDefault (line 1294) | public boolean isAttributeDefault(int index) {
method getAttributeNamespace (line 1298) | public String getAttributeNamespace(int index) {
method getAttributeName (line 1304) | public String getAttributeName(int index) {
method getAttributePrefix (line 1310) | public String getAttributePrefix(int index) {
method getAttributeValue (line 1316) | public String getAttributeValue(int index) {
method getAttributeValue (line 1322) | public String getAttributeValue(String namespace, String name) {
method getEventType (line 1333) | public int getEventType() throws XmlPullParserException {
method next (line 1337) | public int next() throws XmlPullParserException, IOException {
method nextToken (line 1360) | public int nextToken() throws XmlPullParserException, IOException {
method nextTag (line 1373) | public int nextTag() throws XmlPullParserException, IOException {
method require (line 1385) | public void require(int type, String namespace, String name)
method nextText (line 1395) | public String nextText() throws XmlPullParserException, IOException {
method setFeature (line 1416) | public void setFeature(String feature, boolean value)
method setProperty (line 1426) | public void setProperty(String property, Object value)
method skipSubTree (line 1442) | public void skipSubTree() throws XmlPullParserException, IOException {
FILE: src/main/java/org/kxml2/io/KXmlSerializer.java
class KXmlSerializer (line 27) | public class KXmlSerializer implements XmlSerializer {
method check (line 46) | private final void check(boolean close) throws IOException {
method writeEscaped (line 88) | private final void writeEscaped(String s, int quot)
method docdecl (line 150) | public void docdecl(String dd) throws IOException {
method endDocument (line 156) | public void endDocument() throws IOException {
method entityRef (line 165) | public void entityRef(String name) throws IOException {
method getFeature (line 172) | public boolean getFeature(String name) {
method getPrefix (line 182) | public String getPrefix(String namespace, boolean create) {
method getPrefix (line 191) | private final String getPrefix(
method getProperty (line 245) | public Object getProperty(String name) {
method ignorableWhitespace (line 249) | public void ignorableWhitespace(String s)
method setFeature (line 254) | public void setFeature(String name, boolean value) {
method setProperty (line 263) | public void setProperty(String name, Object value) {
method setPrefix (line 268) | public void setPrefix(String prefix, String namespace)
method setOutput (line 296) | public void setOutput(Writer writer) {
method setOutput (line 317) | public void setOutput(OutputStream os, String encoding)
method startDocument (line 331) | public void startDocument(
method startTag (line 358) | public XmlSerializer startTag(String namespace, String name)
method attribute (line 411) | public XmlSerializer attribute(
method flush (line 463) | public void flush() throws IOException {
method endTag (line 473) | public XmlSerializer endTag(String namespace, String name)
method getNamespace (line 513) | public String getNamespace() {
method getName (line 517) | public String getName() {
method getDepth (line 521) | public int getDepth() {
method text (line 525) | public XmlSerializer text(String text) throws IOException {
method text (line 532) | public XmlSerializer text(char[] text, int start, int len)
method cdsect (line 538) | public void cdsect(String data) throws IOException {
method comment (line 545) | public void comment(String comment) throws IOException {
method processingInstruction (line 552) | public void processingInstruction(String pi)
FILE: src/main/java/org/kxml2/kdom/Document.java
class Document (line 31) | public class Document extends Node {
method getEncoding (line 39) | public String getEncoding () {
method setEncoding (line 43) | public void setEncoding(String enc) {
method setStandalone (line 47) | public void setStandalone (Boolean standalone) {
method getStandalone (line 51) | public Boolean getStandalone() {
method getName (line 56) | public String getName() {
method addChild (line 63) | public void addChild(int index, int type, Object child) {
method parse (line 81) | public void parse(XmlPullParser parser)
method removeChild (line 97) | public void removeChild(int index) {
method getRootElement (line 108) | public Element getRootElement() {
method write (line 120) | public void write(XmlSerializer writer)
FILE: src/main/java/org/kxml2/kdom/Element.java
class Element (line 33) | public class Element extends Node {
method Element (line 41) | public Element() {
method init (line 49) | public void init() {
method clear (line 58) | public void clear() {
method createElement (line 67) | public Element createElement(
method getAttributeCount (line 79) | public int getAttributeCount() {
method getAttributeNamespace (line 83) | public String getAttributeNamespace (int index) {
method getAttributeName (line 91) | public String getAttributeName (int index) {
method getAttributeValue (line 96) | public String getAttributeValue (int index) {
method getAttributeValue (line 101) | public String getAttributeValue (String namespace, String name) {
method getRoot (line 115) | public Node getRoot() {
method getName (line 130) | public String getName() {
method getNamespace (line 137) | public String getNamespace() {
method getNamespaceUri (line 145) | public String getNamespaceUri (String prefix) {
method getNamespaceCount (line 160) | public int getNamespaceCount () {
method getNamespacePrefix (line 165) | public String getNamespacePrefix (int i) {
method getNamespaceUri (line 169) | public String getNamespaceUri (int i) {
method getParent (line 177) | public Node getParent() {
method parse (line 196) | public void parse(XmlPullParser parser)
method setAttribute (line 239) | public void setAttribute (String namespace, String name, String value) {
method setPrefix (line 270) | public void setPrefix (String prefix, String namespace) {
method setName (line 279) | public void setName(String name) {
method setNamespace (line 289) | public void setNamespace(String namespace) {
method setParent (line 301) | protected void setParent(Node parent) {
method write (line 309) | public void write(XmlSerializer writer)
FILE: src/main/java/org/kxml2/kdom/Node.java
class Node (line 29) | public class Node { //implements XmlIO{
method addChild (line 47) | public void addChild(int index, int type, Object child) {
method addChild (line 72) | public void addChild(int type, Object child) {
method createElement (line 84) | public Element createElement(String namespace, String name) {
method getChild (line 96) | public Object getChild(int index) {
method getChildCount (line 102) | public int getChildCount() {
method getElement (line 109) | public Element getElement(int index) {
method getElement (line 118) | public Element getElement(String namespace, String name) {
method getText (line 174) | public String getText(int index) {
method getType (line 181) | public int getType(int index) {
method indexOf (line 198) | public int indexOf(String namespace, String name, int startIndex) {
method isText (line 214) | public boolean isText(int i) {
method parse (line 223) | public void parse(XmlPullParser parser)
method removeChild (line 274) | public void removeChild(int idx) {
method write (line 312) | public void write(XmlSerializer writer) throws IOException {
method writeChildren (line 319) | public void writeChildren(XmlSerializer writer) throws IOException {
FILE: src/main/java/org/kxml2/wap/Wbxml.java
type Wbxml (line 27) | public interface Wbxml {
FILE: src/main/java/org/kxml2/wap/WbxmlParser.java
class WbxmlParser (line 34) | public class WbxmlParser implements XmlPullParser {
method getFeature (line 92) | public boolean getFeature(String feature) {
method getInputEncoding (line 101) | public String getInputEncoding() {
method defineEntityReplacementText (line 105) | public void defineEntityReplacementText(
method getProperty (line 113) | public Object getProperty(String property) {
method getNamespaceCount (line 117) | public int getNamespaceCount(int depth) {
method getNamespacePrefix (line 123) | public String getNamespacePrefix(int pos) {
method getNamespaceUri (line 127) | public String getNamespaceUri(int pos) {
method getNamespace (line 131) | public String getNamespace(String prefix) {
method getDepth (line 151) | public int getDepth() {
method getPositionDescription (line 155) | public String getPositionDescription() {
method getLineNumber (line 210) | public int getLineNumber() {
method getColumnNumber (line 214) | public int getColumnNumber() {
method isWhitespace (line 218) | public boolean isWhitespace()
method getText (line 227) | public String getText() {
method getTextCharacters (line 231) | public char[] getTextCharacters(int[] poslen) {
method getNamespace (line 245) | public String getNamespace() {
method getName (line 249) | public String getName() {
method getPrefix (line 253) | public String getPrefix() {
method isEmptyElementTag (line 257) | public boolean isEmptyElementTag()
method getAttributeCount (line 264) | public int getAttributeCount() {
method getAttributeType (line 268) | public String getAttributeType(int index) {
method isAttributeDefault (line 272) | public boolean isAttributeDefault(int index) {
method getAttributeNamespace (line 276) | public String getAttributeNamespace(int index) {
method getAttributeName (line 282) | public String getAttributeName(int index) {
method getAttributePrefix (line 288) | public String getAttributePrefix(int index) {
method getAttributeValue (line 294) | public String getAttributeValue(int index) {
method getAttributeValue (line 300) | public String getAttributeValue(
method getEventType (line 316) | public int getEventType() throws XmlPullParserException {
method next (line 324) | public int next() throws XmlPullParserException, IOException {
method nextToken (line 367) | public int nextToken() throws XmlPullParserException, IOException {
method nextTag (line 376) | public int nextTag() throws XmlPullParserException, IOException {
method nextText (line 389) | public String nextText() throws XmlPullParserException, IOException {
method require (line 411) | public void require(int type, String namespace, String name)
method setInput (line 422) | public void setInput(Reader reader) throws XmlPullParserException {
method setInput (line 426) | public void setInput(InputStream in, String enc)
method setFeature (line 471) | public void setFeature(String feature, boolean value)
method setProperty (line 479) | public void setProperty(String property, Object value)
method adjustNsp (line 486) | private final boolean adjustNsp()
method setTable (line 606) | private final void setTable(int page, int type, String[] table) {
method exception (line 620) | private final void exception(String desc)
method selectPage (line 626) | private void selectPage(int nr, boolean tags) throws XmlPullParserExce...
method nextImpl (line 640) | private final void nextImpl()
method parseWapExtension (line 737) | public Object parseWapExtension(int id) throws IOException, XmlPullPa...
method readAttr (line 774) | public void readAttr() throws IOException, XmlPullParserException {
method peekId (line 857) | private int peekId () throws IOException {
method resolveWapExtension (line 867) | protected String resolveWapExtension(int id, Object data){
method resolveId (line 883) | String resolveId(String[] tab, int id) throws IOException {
method parseElement (line 900) | void parseElement(int id)
method ensureCapacity (line 949) | private final String[] ensureCapacity(
method readByte (line 959) | int readByte() throws IOException {
method readInt (line 966) | int readInt() throws IOException {
method readStrI (line 979) | String readStrI() throws IOException {
method readStrT (line 1001) | String readStrT() throws IOException {
method setTagTable (line 1028) | public void setTagTable(int page, String[] table) {
method setAttrStartTable (line 1043) | public void setAttrStartTable(
method setAttrValueTable (line 1055) | public void setAttrValueTable(
method getWapCode (line 1066) | public int getWapCode(){
method getWapExtensionData (line 1070) | public Object getWapExtensionData(){
FILE: src/main/java/org/kxml2/wap/WbxmlSerializer.java
class WbxmlSerializer (line 35) | public class WbxmlSerializer implements XmlSerializer {
method attribute (line 68) | public XmlSerializer attribute(String namespace, String name, String v...
method cdsect (line 75) | public void cdsect (String cdsect) throws IOException{
method comment (line 82) | public void comment (String comment) {
method docdecl (line 89) | public void docdecl (String docdecl) {
method entityRef (line 96) | public void entityRef (String er) {
method getDepth (line 103) | public int getDepth() {
method getFeature (line 110) | public boolean getFeature (String name) {
method getNamespace (line 118) | public String getNamespace() {
method getName (line 128) | public String getName() {
method getPrefix (line 135) | public String getPrefix(String nsp, boolean create) {
method getProperty (line 144) | public Object getProperty (String name) {
method ignorableWhitespace (line 148) | public void ignorableWhitespace (String sp) {
method endDocument (line 157) | public void endDocument() throws IOException {
method flush (line 166) | public void flush() throws IOException {
method checkPending (line 179) | public void checkPending(boolean degenerated) throws IOException {
method processingInstruction (line 245) | public void processingInstruction(String pi) {
method setFeature (line 252) | public void setFeature(String name, boolean value) {
method setOutput (line 259) | public void setOutput (Writer writer) {
method setOutput (line 266) | public void setOutput (OutputStream out, String encoding) throws IOExc...
method setPrefix (line 281) | public void setPrefix(String prefix, String nsp) {
method setProperty (line 288) | public void setProperty(String property, Object value) {
method startDocument (line 298) | public void startDocument(String encoding, Boolean standalone) throws ...
method startTag (line 319) | public XmlSerializer startTag(String namespace, String name) throws IO...
method text (line 333) | public XmlSerializer text(char[] chars, int start, int len) throws IOE...
method text (line 339) | public XmlSerializer text(String text) throws IOException {
method writeStr (line 348) | private void writeStr(String text) throws IOException{
method endTag (line 398) | public XmlSerializer endTag(String namespace, String name) throws IOEx...
method writeWapExtension (line 412) | public void writeWapExtension(int type, Object data) throws IOException {
method writeInt (line 446) | static void writeInt(OutputStream out, int i) throws IOException {
method writeStrI (line 462) | void writeStrI(OutputStream out, String s) throws IOException {
method writeStrT (line 468) | private final void writeStrT(String s, boolean mayPrependSpace) throws...
method addToStringTable (line 484) | public int addToStringTable(String s, boolean mayPrependSpace) throws ...
method setTagTable (line 517) | public void setTagTable(int page, String[] tagTable) {
method setAttrStartTable (line 536) | public void setAttrStartTable(int page, String[] attrStartTable) {
method setAttrValueTable (line 552) | public void setAttrValueTable(int page, String[] attrValueTable) {
FILE: src/main/java/org/kxml2/wap/syncml/SyncML.java
class SyncML (line 5) | public abstract class SyncML {
method createParser (line 10) | public static WbxmlParser createParser() {
method createSerializer (line 17) | public static WbxmlSerializer createSerializer() {
method createDMParser (line 27) | public static WbxmlParser createDMParser() {
method createDMSerializer (line 33) | public static WbxmlSerializer createDMSerializer() {
FILE: src/main/java/org/kxml2/wap/wml/Wml.java
class Wml (line 11) | public abstract class Wml {
method createParser (line 15) | public static WbxmlParser createParser() {
method createSerializer (line 23) | public static WbxmlSerializer createSerializer() {
FILE: src/main/java/org/kxml2/wap/wv/WV.java
class WV (line 31) | public abstract class WV {
method createParser (line 37) | public static WbxmlParser createParser () throws IOException {
FILE: src/test/java/com/htmake/reader/ReaderApplicationTests.java
class ReaderApplicationTests (line 8) | @RunWith(SpringRunner.class)
method contextLoads (line 12) | @Test
FILE: web/public/bookSourceDebug/index.js
function $ (line 4) | function $(selector) {
function $$ (line 7) | function $$(selector) {
function hashParam (line 11) | function hashParam(key, val) {
function Container (line 29) | function Container() {
function showTab (line 68) | function showTab(tabName) {
function newRule (line 81) | function newRule(rule) {
function HttpGet (line 100) | function HttpGet(url) {
function HttpPost (line 109) | function HttpPost(url, data) {
function rule2json (line 123) | function rule2json() {
function json2rule (line 191) | function json2rule(RuleEditor) {
function todo (line 264) | function todo() {
function undo (line 271) | function undo() {
function redo (line 280) | function redo() {
function setRule (line 289) | function setRule(editRule) {
function DebugPrint (line 430) | function DebugPrint(msg) {
FILE: web/src/main.js
method api (line 58) | api() {
method isWebApp (line 61) | isWebApp() {
method isPWA (line 64) | isPWA() {
method isNightTheme (line 70) | isNightTheme() {
method currentUserName (line 73) | currentUserName() {
method getImagePath (line 78) | getImagePath(url, useSW) {
method getCover (line 94) | getCover(coverUrl, normal, useSW) {
method getImage (line 106) | getImage(imageUrl, normal, useSW) {
FILE: web/src/plugins/animate.js
function Animate (line 8) | function Animate(options) {
FILE: web/src/plugins/element.js
method info (line 78) | info(message, duration) {
method error (line 83) | error(message, duration) {
method success (line 88) | success(message, duration) {
FILE: web/src/plugins/helper.js
function run (line 30) | async function run() {
FILE: web/src/plugins/jump.js
function location (line 34) | function location() {
function top (line 42) | function top(element) {
function scrollTo (line 53) | function scrollTo(top) {
function loop (line 61) | function loop(timeCurrent) {
function done (line 84) | function done() {
function jump (line 108) | function jump(target, options = {}) {
FILE: web/src/plugins/md5.js
function RotateLeft (line 6) | function RotateLeft(lValue, iShiftBits) {
function AddUnsigned (line 9) | function AddUnsigned(lX, lY) {
function F (line 22) | function F(x, y, z) {
function G (line 25) | function G(x, y, z) {
function H (line 28) | function H(x, y, z) {
function I (line 31) | function I(x, y, z) {
function FF (line 34) | function FF(a, b, c, d, x, s, ac) {
function GG (line 38) | function GG(a, b, c, d, x, s, ac) {
function HH (line 42) | function HH(a, b, c, d, x, s, ac) {
function II (line 46) | function II(a, b, c, d, x, s, ac) {
function ConvertToWordArray (line 50) | function ConvertToWordArray(sMessage) {
function WordToHex (line 75) | function WordToHex(lValue) {
FILE: web/src/plugins/safe-json-stringify.js
function throwsMessage (line 3) | function throwsMessage(err) {
function safeGetValueFromPropertyOnObject (line 7) | function safeGetValueFromPropertyOnObject(obj, property) {
function ensureProperties (line 19) | function ensureProperties(obj) {
FILE: web/src/plugins/vuex.js
method setShelfBooks (line 72) | setShelfBooks(state, books) {
method updateShelfBook (line 101) | updateShelfBook(state, book) {
method setReadingBook (line 145) | setReadingBook(state, readingBook) {
method setConfig (line 175) | setConfig(state, config) {
method setMiniInterface (line 210) | setMiniInterface(state, mini) {
method setWindowSize (line 217) | setWindowSize(state, size) {
method setTouchable (line 220) | setTouchable(state, touchable) {
method setApi (line 223) | setApi(state, api) {
method setConnected (line 226) | setConnected(state, connected) {
method setShowLogin (line 229) | setShowLogin(state, showLogin) {
method setLoginAuth (line 235) | setLoginAuth(state, loginAuth) {
method setToken (line 238) | setToken(state, token) {
method setBookSourceList (line 242) | setBookSourceList(state, list) {
method setUserNS (line 254) | setUserNS(state, userNS) {
method setIsSecureMode (line 257) | setIsSecureMode(state, isSecureMode) {
method setSecureKey (line 260) | setSecureKey(state, secureKey) {
method setIsManagerMode (line 263) | setIsManagerMode(state, isManagerMode) {
method setShowManagerMode (line 266) | setShowManagerMode(state, showManagerMode) {
method setUserInfo (line 269) | setUserInfo(state, userInfo) {
method setUserList (line 272) | setUserList(state, userList) {
method setFilterRules (line 281) | setFilterRules(state, filterRules) {
method addFilterRule (line 285) | addFilterRule(state, rule) {
method setNightTheme (line 296) | setNightTheme(state, isNight) {
method setSpeechVoiceConfig (line 345) | setSpeechVoiceConfig(state, voiceConfig) {
method setSafeArea (line 349) | setSafeArea(state, safeArea) {
method setAutoPlay (line 352) | setAutoPlay(state, autoPlay) {
method addFailureBookSource (line 355) | addFailureBookSource(state, { bookSourceUrl, errorMsg }) {
method removeFailureBookSource (line 371) | removeFailureBookSource(state, bookSourceList) {
method setFailureIncludeTimeout (line 381) | setFailureIncludeTimeout(state, failureIncludeTimeout) {
method setBookGroupList (line 384) | setBookGroupList(state, bookGroupList) {
method setRssSourceList (line 396) | setRssSourceList(state, rssSources) {
method setShelfConfig (line 399) | setShelfConfig(state, shelfConfig) {
method setPreviewImageIndex (line 403) | setPreviewImageIndex(state, previewImageIndex) {
method setPreviewImgList (line 406) | setPreviewImgList(state, previewImgList) {
method setSearchConfig (line 416) | setSearchConfig(state, searchConfig) {
method setTxtTocRules (line 420) | setTxtTocRules(state, tocRules) {
method setCustomConfigList (line 423) | setCustomConfigList(state, customConfigList) {
method setShowBookInfo (line 427) | setShowBookInfo(state, book) {
method setCachingBookList (line 430) | setCachingBookList(state, cachingBookList) {
method setBookmarks (line 433) | setBookmarks(state, bookmarks) {
method syncFromLocalStorage (line 637) | syncFromLocalStorage({ commit, getters }) {
FILE: web/src/registerServiceWorker.js
function registerServiceWorker (line 5) | function registerServiceWorker() {
FILE: web/vue.config.js
function buildVersion (line 4) | function buildVersion() {
function customWorkboxPlugin (line 18) | function customWorkboxPlugin(generateCacheKey, checkResponse) {
Condensed preview — 375 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,988K chars).
[
{
"path": ".dockerignore",
"chars": 528,
"preview": "HELP.md\n.gradle\nbuild/\n!gradle/wrapper/gradle-wrapper.jar\n!**/src/main/**\n!**/src/test/**\n\n### STS ###\n.apt_generated\n.c"
},
{
"path": ".gitattributes",
"chars": 31,
"preview": "*.java linguist-language=Kotlin"
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report--问题反馈-.md",
"chars": 902,
"preview": "---\nname: Bug report (问题反馈)\nabout: 描述你在使用中遇到的问题(issue语言:1. 中文;2. 英文)\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**为避免无效问题和"
},
{
"path": ".github/workflows/Dockerfile",
"chars": 417,
"preview": "FROM openjdk:8-jdk-alpine\n# Install base packages\nRUN \\\n # apk update; \\\n # apk upgrade; \\\n # Add CA certs tini"
},
{
"path": ".github/workflows/Openj9-Dockerfile",
"chars": 391,
"preview": "FROM ibm-semeru-runtimes:open-8u332-b09-jre\n# Install base packages\nRUN \\\n apt-get update; \\\n apt-get install -y c"
},
{
"path": ".github/workflows/build.yml",
"chars": 481,
"preview": "name: Build Docker Image\non:\n push:\n branches:\n - master\njobs:\n build:\n if: github.repository == 'hectorqin"
},
{
"path": ".github/workflows/pull-request.yml",
"chars": 1088,
"preview": "name: Pull Request Check\n\non:\n pull_request:\n types: [synchronize, reopened, labeled]\n branches:\n - master\n\n"
},
{
"path": ".github/workflows/release-github.yml",
"chars": 2225,
"preview": "name: Publish Github Releases\n\non:\n # push:\n # tags:\n # - 'v**'\n # branches:\n # - master\n workflow_dis"
},
{
"path": ".github/workflows/release-openj9.yml",
"chars": 2204,
"preview": "name: Publish Docker Multi-Platform Images Using Openj9\n\non:\n # push:\n # tags:\n # - 'v**'\n workflow_dispatch:\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 2142,
"preview": "name: Publish Docker Multi-Platform Images\n\non:\n # push:\n # tags:\n # - 'v**'\n workflow_dispatch:\n\njobs:\n dock"
},
{
"path": ".gitignore",
"chars": 579,
"preview": "HELP.md\n.gradle\nbuild/\n!gradle/wrapper/gradle-wrapper.jar\n!**/src/main/**\n!**/src/test/**\n\n### STS ###\n.apt_generated\n.c"
},
{
"path": "Dockerfile",
"chars": 139,
"preview": "FROM hectorqin/reader\n\n# 时区\nENV TZ=Asia/Shanghai\n\nEXPOSE 8080\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"java\", \"-jar\", \"/app"
},
{
"path": "Dockerfile.source",
"chars": 1109,
"preview": "FROM node:lts-alpine3.14 AS build-web\nADD . /app\nWORKDIR /app/web\n# Build web\nRUN yarn && yarn build\n\n# Build jar\nFROM g"
},
{
"path": "LICENSE",
"chars": 35135,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README.md",
"chars": 2213,
"preview": "# reader\n\n阅读3服务器版,不需要手机。\n\n加入TG群(限时开放) 👉 [https://t.me/+pQ8HDlANPZ84ZWNl](https://t.me/+pQ8HDlANPZ84ZWNl)\n\n关注公众号,查看教程和书源👇"
},
{
"path": "UPDATELOG.md",
"chars": 4962,
"preview": "# Update Log\n\n## v3.2.6\n\n### Features\n\n- 新增清除最近阅读功能\n- 新增编辑文章内容功能\n- 优化多个弹窗显示\n- 优化书源导入逻辑\n- 优化订阅同步逻辑\n\n## v3.2.5\n\n### Featur"
},
{
"path": "build.gradle.kts",
"chars": 7567,
"preview": "import org.openjfx.gradle.*\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\nimport java.lang.reflect.*\nimport io."
},
{
"path": "build.sh",
"chars": 3142,
"preview": "#!/bin/bash\n\noldJAVAHome=$JAVA_HOME\n\ntask=$1\n\nversion=\"\"\n\ncheckJava()\n{\n if [ -d /Library/Java/JavaVirtualMachines/op"
},
{
"path": "cli.gradle",
"chars": 2735,
"preview": "buildscript {\n ext.kotlin_version = '1.5.21'\n repositories {\n mavenCentral()\n }\n dependencies {\n "
},
{
"path": "doc.md",
"chars": 16526,
"preview": "# 文档\n\n- [文档](#文档)\n - [免责声明(Disclaimer)](#免责声明disclaimer)\n - [数据存储](#数据存储)\n - [本地书仓](#本地书仓)\n - [阅读页面地址](#阅读页面地址)\n "
},
{
"path": "docker-compose.yaml",
"chars": 2233,
"preview": "version: '3.1'\nservices:\n# reader 在线阅读\n# 公开服务器(服务器位于日本):[https://reader.nxnow.top](https://reader.nxnow.top) 测试账号/密码分别为g"
},
{
"path": "docker-compose.yml",
"chars": 2122,
"preview": "version: '3.1'\nservices:\n# reader 在线阅读\n# 公开服务器(服务器位于日本):[https://reader.nxnow.top](https://reader.nxnow.top) 测试账号/密码分别为g"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 202,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 28,
"preview": "org.gradle.jvmargs=-Xmx2048m"
},
{
"path": "gradlew",
"chars": 5305,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2185,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "nixpacks.toml",
"chars": 132,
"preview": "[phases.build]\ncmds = ['railway run']\n\n[start]\nrunImage = 'hectorqin/reader'\nonlyIncludeFiles = ['Dockerfile']\ncmd = '/s"
},
{
"path": "preview.md",
"chars": 166,
"preview": "# 预览\n\n\n\n\n\n\n\n\n!["
},
{
"path": "reader.sh",
"chars": 6672,
"preview": "#!/bin/bash\n\nred='\\033[0;31m'\ngreen=\"\\033[32m\"\nyellow='\\033[0;33m'\nplain='\\033[0m'\n\nfile_dir=\"\"\nremotePort=\"\"\nisMultiUse"
},
{
"path": "server/bin/shutdown.cmd",
"chars": 1237,
"preview": "@echo off\nrem Copyright 1999-2018 Alibaba Group Holding Ltd.\nrem Licensed under the Apache License, Version 2.0 (the \"Li"
},
{
"path": "server/bin/shutdown.sh",
"chars": 938,
"preview": "#!/bin/bash\n\n# Copyright 1999-2018 Alibaba Group Holding Ltd.\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "server/bin/startup.cmd",
"chars": 4908,
"preview": "@echo off\nREM Copyright 1999-2018 Alibaba Group Holding Ltd.\nREM Licensed under the Apache License, Version 2.0 (the \"Li"
},
{
"path": "server/bin/startup.sh",
"chars": 5437,
"preview": "#!/bin/bash\n\n# Copyright 1999-2018 Alibaba Group Holding Ltd.\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "server/conf/application.properties",
"chars": 1193,
"preview": "# 是否多用户模式,如果启动 startup 脚本时使用了 -m 1 选择多用户模式,-m single 运行单用户模式,否则根据此处的参数选择模式\nreader.app.secure=true\n\n# 邀请码,如果启动 startup 脚本"
},
{
"path": "settings.gradle",
"chars": 103,
"preview": "pluginManagement {\n repositories {\n gradlePluginPortal()\n }\n}\nrootProject.name = 'reader'\n"
},
{
"path": "src/main/java/com/htmake/reader/ReaderApplication.kt",
"chars": 2087,
"preview": "package com.htmake.reader\n\nimport com.fasterxml.jackson.databind.DeserializationFeature\nimport com.fasterxml.jackson.mod"
},
{
"path": "src/main/java/com/htmake/reader/ReaderUIApplication.kt",
"chars": 16189,
"preview": "package com.htmake.reader\n\nimport com.fasterxml.jackson.databind.DeserializationFeature\nimport com.fasterxml.jackson.mod"
},
{
"path": "src/main/java/com/htmake/reader/SpringEvent.java",
"chars": 628,
"preview": "package com.htmake.reader;\n\nimport org.springframework.context.ApplicationEvent;\n\npublic class SpringEvent extends Appli"
},
{
"path": "src/main/java/com/htmake/reader/api/ReturnData.kt",
"chars": 512,
"preview": "package com.htmake.reader.api\n\n\nclass ReturnData {\n\n var isSuccess: Boolean = false\n private set\n\n var erro"
},
{
"path": "src/main/java/com/htmake/reader/api/YueduApi.kt",
"chars": 23598,
"preview": "package com.htmake.reader.api\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookChapter\nim"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/BaseController.kt",
"chars": 15420,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/BookController.kt",
"chars": 138570,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.constant.AppPattern\nimport io.legado.app.data.entities.Bo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/BookSourceController.kt",
"chars": 16281,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/BookmarkController.kt",
"chars": 9130,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/ReplaceRuleController.kt",
"chars": 9296,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/RssSourceController.kt",
"chars": 11688,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/UserController.kt",
"chars": 21272,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/api/controller/WebdavController.kt",
"chars": 29879,
"preview": "package com.htmake.reader.api.controller\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.Boo"
},
{
"path": "src/main/java/com/htmake/reader/config/AppConfig.kt",
"chars": 872,
"preview": "package com.htmake.reader.config\n\nimport org.springframework.boot.context.properties.ConfigurationProperties\nimport org."
},
{
"path": "src/main/java/com/htmake/reader/config/BookConfig.kt",
"chars": 5441,
"preview": "package com.htmake.reader.config\n\nimport java.io.File\n\nobject BookConfig {\n val javascriptVersion = \"reader-inject-ja"
},
{
"path": "src/main/java/com/htmake/reader/entity/BasicError.kt",
"chars": 224,
"preview": "package com.htmake.reader.entity\n\ndata class BasicError(\n val error: String,\n val exception: String,\n "
},
{
"path": "src/main/java/com/htmake/reader/entity/Size.kt",
"chars": 106,
"preview": "package com.htmake.reader.entity\n\ndata class Size(\n val width: Double,\n val height: Double\n)"
},
{
"path": "src/main/java/com/htmake/reader/entity/User.kt",
"chars": 472,
"preview": "package com.htmake.reader.entity\n\ndata class User(\n var username: String=\"\",\n var password: String=\"\",\n "
},
{
"path": "src/main/java/com/htmake/reader/init/appCtx.kt",
"chars": 186,
"preview": "package com.htmake.reader.init\n\nimport com.htmake.reader.utils.getWorkDir\n\n// 处理 appCtx\nobject appCtx {\n val cacheDir"
},
{
"path": "src/main/java/com/htmake/reader/utils/Ext.kt",
"chars": 6482,
"preview": "package com.htmake.reader.utils\n\nimport io.vertx.core.buffer.Buffer\nimport io.vertx.ext.web.client.HttpRequest\nimport io"
},
{
"path": "src/main/java/com/htmake/reader/utils/SpringContextUtils.java",
"chars": 1626,
"preview": "package com.htmake.reader.utils;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.Ap"
},
{
"path": "src/main/java/com/htmake/reader/utils/VertExt.kt",
"chars": 7765,
"preview": "package com.htmake.reader.utils\n\nimport com.google.common.base.Throwables\nimport com.google.gson.Gson\nimport com.google."
},
{
"path": "src/main/java/com/htmake/reader/verticle/RestVerticle.kt",
"chars": 5330,
"preview": "package com.htmake.reader.verticle\n\nimport io.vertx.core.http.HttpMethod\nimport io.vertx.ext.web.Route\nimport io.vertx.e"
},
{
"path": "src/main/java/io/legado/app/README.md",
"chars": 63,
"preview": "# 文件结构介绍\n\n* constant 常量\n* data 数据\n* help 帮助\n* lib 库\n* model 解析\n"
},
{
"path": "src/main/java/io/legado/app/constant/Action.kt",
"chars": 546,
"preview": "package io.legado.app.constant\n\nobject Action {\n\n const val play = \"play\"\n const val stop = \"stop\"\n const val r"
},
{
"path": "src/main/java/io/legado/app/constant/AppConst.kt",
"chars": 1079,
"preview": "package io.legado.app.constant\n\nimport java.text.SimpleDateFormat\nimport com.script.javascript.RhinoScriptEngine\n\nobject"
},
{
"path": "src/main/java/io/legado/app/constant/AppPattern.kt",
"chars": 1074,
"preview": "package io.legado.app.constant\n\nimport java.util.regex.Pattern\n\nobject AppPattern {\n val JS_PATTERN: Pattern =\n "
},
{
"path": "src/main/java/io/legado/app/constant/BookType.kt",
"chars": 268,
"preview": "package io.legado.app.constant\n\nobject BookType {\n const val default = 0 // 0 文本\n const val audio = 1 "
},
{
"path": "src/main/java/io/legado/app/constant/DeepinkBookSource.kt",
"chars": 2301,
"preview": "package io.legado.app.constant\n\nimport java.io.File\n\nobject DeepinkBookSource {\n\n fun generate(name: String, url: Str"
},
{
"path": "src/main/java/io/legado/app/constant/PreferKey.kt",
"chars": 323,
"preview": "package io.legado.app.constant\n\nobject PreferKey {\n\n const val downloadPath = \"downloadPath\"\n const val hideStatus"
},
{
"path": "src/main/java/io/legado/app/constant/RSSKeywords.kt",
"chars": 563,
"preview": "package io.legado.app.constant\n\nobject RSSKeywords {\n\n const val RSS_ITEM = \"item\"\n const val RSS_ITEM_TITLE = \"ti"
},
{
"path": "src/main/java/io/legado/app/constant/Status.kt",
"chars": 119,
"preview": "package io.legado.app.constant\n\nobject Status {\n const val STOP = 0\n const val PLAY = 1\n const val PAUSE = 3\n}"
},
{
"path": "src/main/java/io/legado/app/data/entities/BaseBook.kt",
"chars": 663,
"preview": "package io.legado.app.data.entities\n\nimport io.legado.app.model.analyzeRule.RuleDataInterface\nimport io.legado.app.utils"
},
{
"path": "src/main/java/io/legado/app/data/entities/BaseSource.kt",
"chars": 4620,
"preview": "package io.legado.app.data.entities\n\nimport com.script.SimpleBindings\nimport io.legado.app.utils.Base64\nimport io.legado"
},
{
"path": "src/main/java/io/legado/app/data/entities/Book.kt",
"chars": 10319,
"preview": "package io.legado.app.data.entities\n\n\nimport io.legado.app.constant.BookType\nimport io.legado.app.constant.AppPattern\nim"
},
{
"path": "src/main/java/io/legado/app/data/entities/BookChapter.kt",
"chars": 2291,
"preview": "package io.legado.app.data.entities\n\n\nimport io.legado.app.utils.GSON\nimport io.legado.app.utils.fromJsonObject\nimport i"
},
{
"path": "src/main/java/io/legado/app/data/entities/BookGroup.kt",
"chars": 262,
"preview": "package io.legado.app.data.entities\n\n\n\n\n//@Parcelize\n//@Entity(tableName = \"book_groups\")\ndata class BookGroup(\n// "
},
{
"path": "src/main/java/io/legado/app/data/entities/BookSource.kt",
"chars": 7667,
"preview": "package io.legado.app.data.entities\n\n\n//import io.legado.app.App\nimport io.legado.app.constant.AppConst\nimport io.legado"
},
{
"path": "src/main/java/io/legado/app/data/entities/Bookmark.kt",
"chars": 453,
"preview": "package io.legado.app.data.entities\n\n\n\n\n//@Parcelize\n//@Entity(tableName = \"bookmarks\", indices = [(Index(value = [\"book"
},
{
"path": "src/main/java/io/legado/app/data/entities/Cache.kt",
"chars": 246,
"preview": "package io.legado.app.data.entities\n\n// @Entity(tableName = \"caches\", indices = [(Index(value = [\"key\"], unique = true))"
},
{
"path": "src/main/java/io/legado/app/data/entities/Cookie.kt",
"chars": 217,
"preview": "package io.legado.app.data.entities\n\n// @Entity(tableName = \"cookies\", indices = [(Index(value = [\"url\"], unique = true)"
},
{
"path": "src/main/java/io/legado/app/data/entities/ReplaceRule.kt",
"chars": 653,
"preview": "package io.legado.app.data.entities\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\n\n\n//@Parcelize\n//@Entity(\n//"
},
{
"path": "src/main/java/io/legado/app/data/entities/RssArticle.kt",
"chars": 1519,
"preview": "package io.legado.app.data.entities\n\nimport io.legado.app.utils.GSON\nimport io.legado.app.utils.fromJsonObject\nimport io"
},
{
"path": "src/main/java/io/legado/app/data/entities/RssSource.kt",
"chars": 6401,
"preview": "package io.legado.app.data.entities\n\nimport com.jayway.jsonpath.DocumentContext\nimport com.fasterxml.jackson.annotation."
},
{
"path": "src/main/java/io/legado/app/data/entities/SearchBook.kt",
"chars": 3226,
"preview": "package io.legado.app.data.entities\n\n//import android.os.Parcelable\n//import androidx.room.*\nimport io.legado.app.utils."
},
{
"path": "src/main/java/io/legado/app/data/entities/SearchKeyword.kt",
"chars": 380,
"preview": "package io.legado.app.data.entities\n\n\n\n\n\n//@Parcelize\n//@Entity(tableName = \"search_keywords\", indices = [(Index(value ="
},
{
"path": "src/main/java/io/legado/app/data/entities/SearchResult.kt",
"chars": 396,
"preview": "package io.legado.app.data.entities\n\ndata class SearchResult(\n val resultCount: Int = 0,\n val resultCountWithinCha"
},
{
"path": "src/main/java/io/legado/app/data/entities/TxtTocRule.kt",
"chars": 350,
"preview": "package io.legado.app.data.entities\n\n// import androidx.room.Entity\n// import androidx.room.PrimaryKey\n\n\n// @Entity(tabl"
},
{
"path": "src/main/java/io/legado/app/data/entities/rule/BookInfoRule.kt",
"chars": 429,
"preview": "package io.legado.app.data.entities.rule\n\ndata class BookInfoRule(\n var init: String? = null,\n var name: String? ="
},
{
"path": "src/main/java/io/legado/app/data/entities/rule/BookListRule.kt",
"chars": 320,
"preview": "package io.legado.app.data.entities.rule\n\ninterface BookListRule {\n var bookList: String?\n var name: String?\n v"
},
{
"path": "src/main/java/io/legado/app/data/entities/rule/ContentRule.kt",
"chars": 308,
"preview": "package io.legado.app.data.entities.rule\n\ndata class ContentRule(\n var content: String? = null,\n var nextContentUr"
},
{
"path": "src/main/java/io/legado/app/data/entities/rule/ExploreRule.kt",
"chars": 503,
"preview": "package io.legado.app.data.entities.rule\n\ndata class ExploreRule(\n override var bookList: String? = null,\n overrid"
},
{
"path": "src/main/java/io/legado/app/data/entities/rule/SearchRule.kt",
"chars": 542,
"preview": "package io.legado.app.data.entities.rule\n\ndata class SearchRule(\n override var bookList: String? = null,\n "
},
{
"path": "src/main/java/io/legado/app/data/entities/rule/TocRule.kt",
"chars": 346,
"preview": "package io.legado.app.data.entities.rule\n\ndata class TocRule(\n var preUpdateJs: String? = null,\n var chapterList: "
},
{
"path": "src/main/java/io/legado/app/exception/ConcurrentException.kt",
"chars": 161,
"preview": "@file:Suppress(\"unused\")\n\npackage io.legado.app.exception\n\n/**\n * 并发限制\n */\nclass ConcurrentException(msg: String, val wa"
},
{
"path": "src/main/java/io/legado/app/exception/ContentEmptyException.kt",
"chars": 118,
"preview": "package io.legado.app.exception\n\n/**\n * 内容为空\n */\nclass ContentEmptyException(msg: String) : NoStackTraceException(msg)"
},
{
"path": "src/main/java/io/legado/app/exception/NoStackTraceException.kt",
"chars": 198,
"preview": "package io.legado.app.exception\n\n/**\n * 不记录错误堆栈的报错\n */\nopen class NoStackTraceException(msg: String) : Exception(msg) {\n"
},
{
"path": "src/main/java/io/legado/app/exception/RegexTimeoutException.kt",
"chars": 102,
"preview": "package io.legado.app.exception\n\nclass RegexTimeoutException(msg: String) : NoStackTraceException(msg)"
},
{
"path": "src/main/java/io/legado/app/exception/TocEmptyException.kt",
"chars": 114,
"preview": "package io.legado.app.exception\n\n/**\n * 目录为空\n */\nclass TocEmptyException(msg: String) : NoStackTraceException(msg)"
},
{
"path": "src/main/java/io/legado/app/help/BookHelp.kt",
"chars": 5662,
"preview": "package io.legado.app.help\n\nimport io.legado.app.constant.AppPattern\nimport io.legado.app.data.entities.Book\nimport io.l"
},
{
"path": "src/main/java/io/legado/app/help/CacheManager.kt",
"chars": 1930,
"preview": "package io.legado.app.help\n\nimport io.legado.app.data.entities.Cache\nimport io.legado.app.model.analyzeRule.QueryTTF\nimp"
},
{
"path": "src/main/java/io/legado/app/help/DefaultData.kt",
"chars": 764,
"preview": "package io.legado.app.help\n\n// import io.legado.app.data.entities.RssSource\nimport io.legado.app.data.entities.TxtTocRul"
},
{
"path": "src/main/java/io/legado/app/help/EncodingDetectHelp.java",
"chars": 177909,
"preview": "package io.legado.app.help;\n\n//import androidx.annotation.NonNull;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Docume"
},
{
"path": "src/main/java/io/legado/app/help/JsExtensions.kt",
"chars": 24607,
"preview": "package io.legado.app.help\n\nimport cn.hutool.crypto.digest.DigestUtil\nimport cn.hutool.crypto.symmetric.AES\nimport cn.hu"
},
{
"path": "src/main/java/io/legado/app/help/coroutine/CompositeCoroutine.kt",
"chars": 2062,
"preview": "package io.legado.app.help.coroutine\n\nclass CompositeCoroutine : CoroutineContainer {\n\n private var resources: HashSe"
},
{
"path": "src/main/java/io/legado/app/help/coroutine/Coroutine.kt",
"chars": 6035,
"preview": "package io.legado.app.help.coroutine\n\nimport kotlinx.coroutines.*\nimport kotlin.coroutines.CoroutineContext\n\n\nclass Coro"
},
{
"path": "src/main/java/io/legado/app/help/coroutine/CoroutineContainer.kt",
"chars": 303,
"preview": "package io.legado.app.help.coroutine\n\ninternal interface CoroutineContainer {\n\n fun add(coroutine: Coroutine<*>): Boo"
},
{
"path": "src/main/java/io/legado/app/help/http/AjaxWebView.kt",
"chars": 9707,
"preview": "//package io.legado.app.help.http\n//\n//import android.annotation.SuppressLint\n//import android.net.http.SslError\n//impor"
},
{
"path": "src/main/java/io/legado/app/help/http/ByteConverter.kt",
"chars": 446,
"preview": "package io.legado.app.help.http\n\nimport okhttp3.ResponseBody\nimport retrofit2.Converter\nimport retrofit2.Retrofit\nimport"
},
{
"path": "src/main/java/io/legado/app/help/http/CookieStore.kt",
"chars": 2702,
"preview": "@file:Suppress(\"unused\")\n\npackage io.legado.app.help.http\n\nimport io.legado.app.utils.TextUtils\nimport io.legado.app.dat"
},
{
"path": "src/main/java/io/legado/app/help/http/CoroutinesCallAdapterFactory.kt",
"chars": 3253,
"preview": "package io.legado.app.help.http\n\nimport kotlinx.coroutines.CompletableDeferred\nimport kotlinx.coroutines.Deferred\nimport"
},
{
"path": "src/main/java/io/legado/app/help/http/EncodeConverter.kt",
"chars": 1203,
"preview": "package io.legado.app.help.http\n\nimport io.legado.app.utils.UTF8BOMFighter\nimport okhttp3.ResponseBody\nimport io.legado."
},
{
"path": "src/main/java/io/legado/app/help/http/HttpHelper.kt",
"chars": 5408,
"preview": "package io.legado.app.help.http\n\n// import io.legado.app.help.http.cronet.CronetInterceptor\nimport kotlinx.coroutines.su"
},
{
"path": "src/main/java/io/legado/app/help/http/OkHttpUtils.kt",
"chars": 5600,
"preview": "package io.legado.app.help.http\n\nimport io.legado.app.constant.AppConst\nimport io.legado.app.utils.EncodingDetect\nimport"
},
{
"path": "src/main/java/io/legado/app/help/http/RequestMethod.kt",
"chars": 75,
"preview": "package io.legado.app.help.http\n\nenum class RequestMethod {\n GET, POST\n}"
},
{
"path": "src/main/java/io/legado/app/help/http/Res.kt",
"chars": 83,
"preview": "package io.legado.app.help.http\n\ndata class Res(val url: String, val body: String?)"
},
{
"path": "src/main/java/io/legado/app/help/http/SSLHelper.kt",
"chars": 6333,
"preview": "package io.legado.app.help.http\n\n//import android.annotation.SuppressLint\nimport java.io.IOException\nimport java.io.Inpu"
},
{
"path": "src/main/java/io/legado/app/help/http/StrResponse.kt",
"chars": 1483,
"preview": "package io.legado.app.help.http\n\nimport okhttp3.*\nimport okhttp3.Response.Builder\n\n/**\n * An HTTP response.\n */\n@Suppres"
},
{
"path": "src/main/java/io/legado/app/help/http/api/CookieManager.kt",
"chars": 496,
"preview": "package io.legado.app.help.http.api\n\ninterface CookieManager {\n\n /**\n * 保存cookie\n */\n fun setCookie(url: S"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetDetector.java",
"chars": 21993,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/*\n ***"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetMatch.java",
"chars": 8919,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/*\n * **"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetRecog_2022.java",
"chars": 6121,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/*\n ****"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetRecog_UTF8.java",
"chars": 3382,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/**\n * *"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetRecog_Unicode.java",
"chars": 6761,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/*\n ****"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetRecog_mbcs.java",
"chars": 22595,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/*\n ****"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetRecog_sbcs.java",
"chars": 89468,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/*\n ****"
},
{
"path": "src/main/java/io/legado/app/lib/icu4j/CharsetRecognizer.java",
"chars": 1944,
"preview": "// © 2016 and later: Unicode, Inc. and others.\n// License & terms of use: http://www.unicode.org/copyright.html\n/**\n * *"
},
{
"path": "src/main/java/io/legado/app/model/Debug.kt",
"chars": 256,
"preview": "package io.legado.app.model\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookChapter\nimpo"
},
{
"path": "src/main/java/io/legado/app/model/DebugLog.kt",
"chars": 544,
"preview": "package io.legado.app.model\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookChapter\nimpo"
},
{
"path": "src/main/java/io/legado/app/model/Debugger.kt",
"chars": 6267,
"preview": "package io.legado.app.model\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookChapter\nimpo"
},
{
"path": "src/main/java/io/legado/app/model/README.md",
"chars": 81,
"preview": "# 放置一些模块类\n* analyzeRule 书源规则解析\n* localBook 本地书籍解析\n* rss 订阅规则解析\n* webBook 获取网络书籍\n\n"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/AnalyzeByJSonPath.kt",
"chars": 5634,
"preview": "package io.legado.app.model.analyzeRule\n\nimport com.jayway.jsonpath.JsonPath\nimport com.jayway.jsonpath.ReadContext\nimpo"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/AnalyzeByJSoup.kt",
"chars": 15577,
"preview": "package io.legado.app.model.analyzeRule\n\nimport org.jsoup.Jsoup\nimport org.jsoup.nodes.Element\nimport org.jsoup.select.C"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/AnalyzeByRegex.kt",
"chars": 1820,
"preview": "package io.legado.app.model.analyzeRule\n\nimport java.util.*\nimport java.util.regex.Pattern\n\nobject AnalyzeByRegex {\n\n "
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/AnalyzeByXPath.kt",
"chars": 4713,
"preview": "package io.legado.app.model.analyzeRule\n\nimport io.legado.app.utils.splitNotBlank\nimport io.legado.app.utils.TextUtils\ni"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt",
"chars": 25596,
"preview": "package io.legado.app.model.analyzeRule\n\nimport com.script.SimpleBindings\nimport io.legado.app.constant.AppConst.SCRIPT_"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt",
"chars": 20905,
"preview": "package io.legado.app.model.analyzeRule\n\nimport com.script.SimpleBindings\nimport io.legado.app.constant.AppConst\nimport "
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java",
"chars": 24131,
"preview": "package io.legado.app.model.analyzeRule;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tu"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/RuleAnalyzer.kt",
"chars": 11186,
"preview": "package io.legado.app.model.analyzeRule\n\n//通用的规则切分处理\nclass RuleAnalyzer(data: String, code: Boolean = false) {\n\n priv"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/RuleData.kt",
"chars": 541,
"preview": "package io.legado.app.model.analyzeRule\n\nimport io.legado.app.utils.GSON\n\nclass RuleData : RuleDataInterface {\n\n over"
},
{
"path": "src/main/java/io/legado/app/model/analyzeRule/RuleDataInterface.kt",
"chars": 252,
"preview": "package io.legado.app.model.analyzeRule\n\ninterface RuleDataInterface {\n\n val variableMap: HashMap<String, String>\n\n "
},
{
"path": "src/main/java/io/legado/app/model/localBook/CbzFile.kt",
"chars": 4902,
"preview": "package io.legado.app.model.localBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookCh"
},
{
"path": "src/main/java/io/legado/app/model/localBook/EpubFile.kt",
"chars": 14571,
"preview": "package io.legado.app.model.localBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookCh"
},
{
"path": "src/main/java/io/legado/app/model/localBook/LocalBook.kt",
"chars": 3523,
"preview": "package io.legado.app.model.localBook\n\nimport io.legado.app.constant.AppConst\nimport io.legado.app.constant.AppPattern\ni"
},
{
"path": "src/main/java/io/legado/app/model/localBook/TextFile.kt",
"chars": 13881,
"preview": "package io.legado.app.model.localBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookCh"
},
{
"path": "src/main/java/io/legado/app/model/localBook/UmdFile.kt",
"chars": 4217,
"preview": "package io.legado.app.model.localBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookCh"
},
{
"path": "src/main/java/io/legado/app/model/rss/Rss.kt",
"chars": 2137,
"preview": "package io.legado.app.model.rss\n\nimport io.legado.app.data.entities.RssArticle\nimport io.legado.app.data.entities.RssSou"
},
{
"path": "src/main/java/io/legado/app/model/rss/RssParserByRule.kt",
"chars": 5244,
"preview": "package io.legado.app.model.rss\n\nimport io.legado.app.data.entities.RssArticle\nimport io.legado.app.data.entities.RssSou"
},
{
"path": "src/main/java/io/legado/app/model/rss/RssParserDefault.kt",
"chars": 6672,
"preview": "package io.legado.app.model.rss\n\nimport io.legado.app.data.entities.RssArticle\nimport io.legado.app.model.DebugLog\nimpor"
},
{
"path": "src/main/java/io/legado/app/model/webBook/BookChapterList.kt",
"chars": 9149,
"preview": "package io.legado.app.model.webBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookChap"
},
{
"path": "src/main/java/io/legado/app/model/webBook/BookContent.kt",
"chars": 6353,
"preview": "package io.legado.app.model.webBook\n\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookCha"
},
{
"path": "src/main/java/io/legado/app/model/webBook/BookInfo.kt",
"chars": 4999,
"preview": "package io.legado.app.model.webBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookSour"
},
{
"path": "src/main/java/io/legado/app/model/webBook/BookList.kt",
"chars": 9614,
"preview": "package io.legado.app.model.webBook\n\nimport io.legado.app.data.entities.BookSource\nimport io.legado.app.data.entities.Bo"
},
{
"path": "src/main/java/io/legado/app/model/webBook/WebBook.kt",
"chars": 7898,
"preview": "package io.legado.app.model.webBook\n\nimport io.legado.app.data.entities.Book\nimport io.legado.app.data.entities.BookChap"
},
{
"path": "src/main/java/io/legado/app/utils/ACache.kt",
"chars": 22581,
"preview": "//Copyright (c) 2017. 章钦豪. All rights reserved.\npackage io.legado.app.utils\n\nimport com.htmake.reader.init.appCtx\nimport"
},
{
"path": "src/main/java/io/legado/app/utils/AnkoHelps.kt",
"chars": 723,
"preview": "package io.legado.app.utils\n\ninline fun <T> attempt(f: () -> T): AttemptResult<T> {\n var value: T? = null\n var err"
},
{
"path": "src/main/java/io/legado/app/utils/Base64.java",
"chars": 28503,
"preview": "package io.legado.app.utils;\n\nimport java.io.UnsupportedEncodingException;\n\npublic class Base64 {\n /**\n * Default"
},
{
"path": "src/main/java/io/legado/app/utils/EncoderUtils.kt",
"chars": 11854,
"preview": "package io.legado.app.utils\n\nimport io.legado.app.utils.Base64\nimport java.security.spec.AlgorithmParameterSpec\nimport j"
},
{
"path": "src/main/java/io/legado/app/utils/EncodingDetect.kt",
"chars": 2369,
"preview": "package io.legado.app.utils\n\nimport io.legado.app.lib.icu4j.CharsetDetector\nimport org.jsoup.Jsoup\nimport java.io.File\ni"
},
{
"path": "src/main/java/io/legado/app/utils/FileExtensions.kt",
"chars": 281,
"preview": "package io.legado.app.utils\n\nimport java.io.File\n\nfun File.getFile(vararg subDirFiles: String): File {\n val path = Fi"
},
{
"path": "src/main/java/io/legado/app/utils/FilesUtil.kt",
"chars": 21535,
"preview": "package io.legado.app.utils\n\nimport java.io.*\nimport java.nio.charset.Charset\nimport java.text.SimpleDateFormat\nimport j"
},
{
"path": "src/main/java/io/legado/app/utils/GsonExtensions.kt",
"chars": 4962,
"preview": "package io.legado.app.utils\n\nimport com.google.gson.*\nimport com.google.gson.internal.LinkedTreeMap\nimport com.google.gs"
},
{
"path": "src/main/java/io/legado/app/utils/HtmlFormatter.kt",
"chars": 2434,
"preview": "package io.legado.app.utils\n\nimport io.legado.app.model.analyzeRule.AnalyzeUrl\nimport java.net.URL\nimport java.util.rege"
},
{
"path": "src/main/java/io/legado/app/utils/JsonExtensions.kt",
"chars": 568,
"preview": "package io.legado.app.utils\n\nimport com.jayway.jsonpath.*\n\nval jsonPath: ParseContext by lazy {\n JsonPath.using(\n "
},
{
"path": "src/main/java/io/legado/app/utils/JsoupExtensions.kt",
"chars": 1942,
"preview": "package io.legado.app.utils\n\nimport org.jsoup.internal.StringUtil\nimport org.jsoup.nodes.CDataNode\nimport org.jsoup.node"
},
{
"path": "src/main/java/io/legado/app/utils/LogUtils.kt",
"chars": 110,
"preview": "@file:Suppress(\"unused\")\n\npackage io.legado.app.utils\n\nfun Throwable.printOnDebug() {\n printStackTrace()\n}\n"
},
{
"path": "src/main/java/io/legado/app/utils/MD5Utils.kt",
"chars": 1016,
"preview": "package io.legado.app.utils\n\nimport java.security.MessageDigest\nimport java.security.NoSuchAlgorithmException\n\n/**\n * 将字"
},
{
"path": "src/main/java/io/legado/app/utils/NetworkUtils.kt",
"chars": 4956,
"preview": "package io.legado.app.utils\n\nimport retrofit2.Response\nimport java.net.InetAddress\nimport java.net.NetworkInterface\nimpo"
},
{
"path": "src/main/java/io/legado/app/utils/SourceAnalyzer.kt",
"chars": 17397,
"preview": "package io.legado.app.help\n\nimport com.jayway.jsonpath.JsonPath\nimport io.legado.app.constant.AppConst\nimport io.legado."
},
{
"path": "src/main/java/io/legado/app/utils/StringExtensions.kt",
"chars": 2726,
"preview": "package io.legado.app.utils\n\n// import org.apache.commons.text.StringEscapeUtils\nimport io.legado.app.constant.AppPatter"
},
{
"path": "src/main/java/io/legado/app/utils/StringUtils.kt",
"chars": 10078,
"preview": "package io.legado.app.utils\n\nimport io.legado.app.utils.TextUtils\nimport java.text.DecimalFormat\nimport java.text.ParseE"
},
{
"path": "src/main/java/io/legado/app/utils/TextUtils.java",
"chars": 2032,
"preview": "package io.legado.app.utils;\n\nimport java.util.Iterator;\n\npublic class TextUtils {\n\n public static boolean isEmpty(Ch"
},
{
"path": "src/main/java/io/legado/app/utils/ThrowableExtensions.kt",
"chars": 282,
"preview": "package io.legado.app.utils\n\nval Throwable.msg: String\n get() {\n val stackTrace = stackTraceToString()\n "
},
{
"path": "src/main/java/io/legado/app/utils/UTF8BOMFighter.kt",
"chars": 989,
"preview": "package io.legado.app.utils\n\nobject UTF8BOMFighter {\n private val UTF8_BOM_BYTES = byteArrayOf(0xEF.toByte(), 0xBB.to"
},
{
"path": "src/main/java/io/legado/app/utils/Utf8BomUtils.kt",
"chars": 1237,
"preview": "package io.legado.app.utils\n\n@Suppress(\"unused\")\nobject Utf8BomUtils {\n private val UTF8_BOM_BYTES = byteArrayOf(0xEF"
},
{
"path": "src/main/java/io/legado/app/utils/ZipUtils.kt",
"chars": 12220,
"preview": "package io.legado.app.utils\n\nimport kotlinx.coroutines.Dispatchers.IO\nimport kotlinx.coroutines.withContext\nimport java."
},
{
"path": "src/main/java/me/ag2s/epublib/Constants.java",
"chars": 446,
"preview": "package me.ag2s.epublib;\n\n\npublic interface Constants {\n\n String CHARACTER_ENCODING = \"UTF-8\";\n String DOCTYPE_XHTML ="
},
{
"path": "src/main/java/me/ag2s/epublib/browsersupport/NavigationEvent.java",
"chars": 3692,
"preview": "package me.ag2s.epublib.browsersupport;\n\nimport java.util.EventObject;\n\nimport me.ag2s.epublib.domain.EpubBook;\nimport m"
},
{
"path": "src/main/java/me/ag2s/epublib/browsersupport/NavigationEventListener.java",
"chars": 402,
"preview": "package me.ag2s.epublib.browsersupport;\n\n/**\n * Implemented by classes that want to be notified if the user moves to\n * "
},
{
"path": "src/main/java/me/ag2s/epublib/browsersupport/NavigationHistory.java",
"chars": 5415,
"preview": "package me.ag2s.epublib.browsersupport;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport me.ag2s.epublib.doma"
},
{
"path": "src/main/java/me/ag2s/epublib/browsersupport/Navigator.java",
"chars": 6396,
"preview": "package me.ag2s.epublib.browsersupport;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;"
},
{
"path": "src/main/java/me/ag2s/epublib/browsersupport/package-info.java",
"chars": 235,
"preview": "/**\n * Provides classes that help make an epub reader application.\n *\n * These classes have no dependencies on graphic t"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Author.java",
"chars": 1895,
"preview": "package me.ag2s.epublib.domain;\n\n\n\nimport me.ag2s.epublib.util.StringUtil;\n\nimport java.io.Serializable;\n\n/**\n * Represe"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Date.java",
"chars": 2609,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.Serializable;\nimport java.text.SimpleDateFormat;\nimport java.util.Locale"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/EpubBook.java",
"chars": 9527,
"preview": "package me.ag2s.epublib.domain;\n\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMa"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/EpubResourceProvider.java",
"chars": 899,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.zip.ZipEntry;\n"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/FileResourceProvider.java",
"chars": 1057,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Guide.java",
"chars": 3398,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n *"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/GuideReference.java",
"chars": 2413,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.util.StringUtil;\nimport java.io.Serializable;\n\n\n/**\n * These are"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Identifier.java",
"chars": 3018,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.util.StringUtil;\nimport java.io.Serializable;\nimport java.util.L"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/LazyResource.java",
"chars": 3924,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.util.IOUtil;\nimport java.io.ByteArrayInputStream;\nimport java.io"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/LazyResourceProvider.java",
"chars": 222,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @author jake\n */\npublic"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/ManifestItemProperties.java",
"chars": 438,
"preview": "package me.ag2s.epublib.domain;\n@SuppressWarnings(\"unused\")\npublic enum ManifestItemProperties implements ManifestProper"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/ManifestItemRefProperties.java",
"chars": 356,
"preview": "package me.ag2s.epublib.domain;\n@SuppressWarnings(\"unused\")\npublic enum ManifestItemRefProperties implements ManifestPro"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/ManifestProperties.java",
"chars": 94,
"preview": "package me.ag2s.epublib.domain;\n\npublic interface ManifestProperties {\n\n String getName();\n}\n"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/MediaType.java",
"chars": 1707,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collection;\n\n/**"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/MediaTypes.java",
"chars": 3092,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.util.StringUtil;\nimport java.util.HashMap;\nimport java.util.Map;"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Metadata.java",
"chars": 6122,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.util.StringUtil;\n\nimport java.io.Serializable;\nimport java.util."
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Relator.java",
"chars": 45624,
"preview": "package me.ag2s.epublib.domain;\n\n\n/**\n * A relator denotes which role a certain individual had in the creation/modificat"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Resource.java",
"chars": 9809,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.Constants;\nimport me.ag2s.epublib.util.IOUtil;\nimport me.ag2s.ep"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/ResourceInputStream.java",
"chars": 739,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStrea"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/ResourceReference.java",
"chars": 864,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.Serializable;\n\npublic class ResourceReference implements Serializable {\n"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Resources.java",
"chars": 12193,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.Constants;\nimport me.ag2s.epublib.util.StringUtil;\n\nimport java."
},
{
"path": "src/main/java/me/ag2s/epublib/domain/Spine.java",
"chars": 5061,
"preview": "package me.ag2s.epublib.domain;\n\nimport me.ag2s.epublib.util.StringUtil;\nimport java.io.Serializable;\nimport java.util.A"
},
{
"path": "src/main/java/me/ag2s/epublib/domain/SpineReference.java",
"chars": 1672,
"preview": "package me.ag2s.epublib.domain;\n\nimport java.io.Serializable;\n\n\n/**\n * A Section of a book.\n * Represents both an item i"
}
]
// ... and 175 more files (download for full content)
About this extraction
This page contains the full source code of the hectorqin/reader GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 375 files (2.7 MB), approximately 731.3k tokens, and a symbol index with 1435 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.