mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-06 04:14:34 -05:00
Compare commits
16 Commits
v0.6.1
...
token-auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53c3ac5676 | ||
|
|
a26b8ecca0 | ||
|
|
5a32ccfa7a | ||
|
|
f51818b157 | ||
|
|
31a624db51 | ||
|
|
f045b18762 | ||
|
|
f5006cac7d | ||
|
|
152b193ad5 | ||
|
|
a27af0b642 | ||
|
|
44ffed3f7c | ||
|
|
fa035ad9be | ||
|
|
186ace4343 | ||
|
|
8fb1a0bb1f | ||
|
|
05513bf8b9 | ||
|
|
858784857e | ||
|
|
291a23949a |
75
CHANGELOG.md
75
CHANGELOG.md
@@ -1,3 +1,78 @@
|
|||||||
|
# Server: v0.6.3 + WebUI: r942
|
||||||
|
## TL;DR
|
||||||
|
- Changes in Server
|
||||||
|
- Support for array search filter changes list
|
||||||
|
- Support for Tachiyomi extensions lib 1.3
|
||||||
|
- Changes in WebUI
|
||||||
|
- Better search filter support
|
||||||
|
- Fluid manga grid
|
||||||
|
- Library comfortable grid
|
||||||
|
- Sources view layouts
|
||||||
|
- Various other changes...
|
||||||
|
|
||||||
|
## Tachidesk-Server Changelog
|
||||||
|
- (r1074) v0.6.2 (by @AriaMoradi)
|
||||||
|
- (r1075) support array filter changes ([#304](https://github.com/Suwayomi/Tachidesk-Server/pull/304) by @AriaMoradi)
|
||||||
|
- (r1076) fix filterlist bugs ([#306](https://github.com/Suwayomi/Tachidesk-Server/pull/306) by @AriaMoradi)
|
||||||
|
- (r1077) Update README.md ([#305](https://github.com/Suwayomi/Tachidesk-Server/pull/305) by @mahor1221)
|
||||||
|
- (r1078) fix meta update changing all keys ([#314](https://github.com/Suwayomi/Tachidesk-Server/pull/314) by @AriaMoradi)
|
||||||
|
- (r1079) add support for tachiyomi extensions Lib 1.3 ([#316](https://github.com/Suwayomi/Tachidesk-Server/pull/316) by @AriaMoradi)
|
||||||
|
- (r1080) Fix sources list of one source throws an exception ([#308](https://github.com/Suwayomi/Tachidesk-Server/pull/308) by @Syer10)
|
||||||
|
- (r1081) Improve source handling, fix errors with uninitialized mangas in broken sources ([#319](https://github.com/Suwayomi/Tachidesk-Server/pull/319) by @Syer10)
|
||||||
|
- (r1082) Add thumbnail support for stub sources ([#320](https://github.com/Suwayomi/Tachidesk-Server/pull/320) by @Syer10)
|
||||||
|
- (r1083) update description for Tachidesk-Sorayomi ([#326](https://github.com/Suwayomi/Tachidesk-Server/pull/326) by @DattatreyaReddy)
|
||||||
|
- (r1084) Add last bit of code needed for Extensions Lib 1.3 ([#330](https://github.com/Suwayomi/Tachidesk-Server/pull/330) by @Syer10)
|
||||||
|
- (r1085) Add QuickJS, replaces Duktape for Extensions Lib 1.3 ([#331](https://github.com/Suwayomi/Tachidesk-Server/pull/331) by @Syer10)
|
||||||
|
- (r1086) fix auth not actually blocking requests ([#333](https://github.com/Suwayomi/Tachidesk-Server/pull/333) by @AriaMoradi)
|
||||||
|
|
||||||
|
## Tachidesk-WebUI Changelog
|
||||||
|
- (r930) Source filter scroll fix (array of filters on submit [#149](https://github.com/Suwayomi/Tachidesk-WebUI/pull/149) by @Robonau)
|
||||||
|
- (r931) fix manga badges setting menu that turns the update/download badges on and off ([#150](https://github.com/Suwayomi/Tachidesk-WebUI/pull/150) by @Robonau)
|
||||||
|
- (r932) move sorts to copy tachiyomi ([#151](https://github.com/Suwayomi/Tachidesk-WebUI/pull/151) by @Robonau)
|
||||||
|
- (r933) add comfortable grid option ([#152](https://github.com/Suwayomi/Tachidesk-WebUI/pull/152) by @Robonau)
|
||||||
|
- (r934) source layouts ([#153](https://github.com/Suwayomi/Tachidesk-WebUI/pull/153) by @Robonau)
|
||||||
|
- (r935) List layout ([#154](https://github.com/Suwayomi/Tachidesk-WebUI/pull/154) by @Robonau)
|
||||||
|
- (r936) in library badge to manga in sources ([#156](https://github.com/Suwayomi/Tachidesk-WebUI/pull/156) by @Robonau)
|
||||||
|
- (r937) mass search ([#157](https://github.com/Suwayomi/Tachidesk-WebUI/pull/157) by @Robonau)
|
||||||
|
- (r938) 18+ tag on source/extension cards ([#160](https://github.com/Suwayomi/Tachidesk-WebUI/pull/160) by @Robonau)
|
||||||
|
- (r939) fix search source click ([#164](https://github.com/Suwayomi/Tachidesk-WebUI/pull/164) by @Robonau)
|
||||||
|
- (r940) items per row setting ([#165](https://github.com/Suwayomi/Tachidesk-WebUI/pull/165) by @Robonau)
|
||||||
|
- (r941) fix the grid width thing ([#169](https://github.com/Suwayomi/Tachidesk-WebUI/pull/169) by @Robonau)
|
||||||
|
- (r942) unified library options ([#168](https://github.com/Suwayomi/Tachidesk-WebUI/pull/168) by @infix)
|
||||||
|
|
||||||
|
# Server: v0.6.2 + WebUI: r929
|
||||||
|
## TL;DR
|
||||||
|
- Changes in WebUI
|
||||||
|
- Moved search to Browse
|
||||||
|
- Support for Source Filters
|
||||||
|
- Better visuals for Download Queue
|
||||||
|
- A live version of WebUI is now available [at this link](https://tachidesk-webui-preview.github.io/).
|
||||||
|
|
||||||
|
## Tachidesk-Server Changelog
|
||||||
|
- (r1073) Refactor debian-packager.sh, rename launcher scripts ([#303](https://github.com/Suwayomi/Tachidesk-Server/pull/303) by @mahor1221)
|
||||||
|
|
||||||
|
## Tachidesk-WebUI Changelog
|
||||||
|
- (r912) show locale date, less confusing ([#131](https://github.com/Suwayomi/Tachidesk-WebUI/pull/131) by @AriaMoradi)
|
||||||
|
- (r913) fix links to work on a bare host ([#132](https://github.com/Suwayomi/Tachidesk-WebUI/pull/132) by @AriaMoradi)
|
||||||
|
- (r914) fix direct links ([#133](https://github.com/Suwayomi/Tachidesk-WebUI/pull/133) by @AriaMoradi)
|
||||||
|
- (r915) deploy to github pages (by @AriaMoradi)
|
||||||
|
- (r916) fix typo (by @AriaMoradi)
|
||||||
|
- (r917) better naming (by @AriaMoradi)
|
||||||
|
- (r918) update notice about github pages (by @AriaMoradi)
|
||||||
|
- (r919) move text (by @AriaMoradi)
|
||||||
|
- (r920) make all links work by catching 404 (by @AriaMoradi)
|
||||||
|
- (r921) fix scrolling 8px ([#135](https://github.com/Suwayomi/Tachidesk-WebUI/pull/135) by @Robonau)
|
||||||
|
- (r922) sorting ([#136](https://github.com/Suwayomi/Tachidesk-WebUI/pull/136) by @Robonau)
|
||||||
|
- (r923) Close button fix ([#141](https://github.com/Suwayomi/Tachidesk-WebUI/pull/141)) z14942744@gmail.com
|
||||||
|
- (r924) add NavBarContextProvider ([#128](https://github.com/Suwayomi/Tachidesk-WebUI/pull/128) by @abhijeetChawla)
|
||||||
|
- (r925) Resolved Merged Conflicts ([#127](https://github.com/Suwayomi/Tachidesk-WebUI/pull/127) by @abhijeetChawla)
|
||||||
|
- (r926) more Download Queue info ([#138](https://github.com/Suwayomi/Tachidesk-WebUI/pull/138) by @Robonau)
|
||||||
|
- (r927) Source filters, move search to SourceMangas ([#142](https://github.com/Suwayomi/Tachidesk-WebUI/pull/142) by @Robonau)
|
||||||
|
- (r928) Source genre sorts design ([#147](https://github.com/Suwayomi/Tachidesk-WebUI/pull/147) by @Robonau)
|
||||||
|
- (r929) Update LibraryOptions.tsx ([#146](https://github.com/Suwayomi/Tachidesk-WebUI/pull/146) by @Robonau)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Server: v0.6.1 + WebUI: r911
|
# Server: v0.6.1 + WebUI: r911
|
||||||
## TL;DR
|
## TL;DR
|
||||||
- msi and deb packages thanks to @mahor1221
|
- msi and deb packages thanks to @mahor1221
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -49,7 +49,7 @@ Here's a list of known clients/user interfaces for Tachidesk-Server:
|
|||||||
- [Tachidesk-WebUI](https://github.com/Suwayomi/Tachidesk-WebUI): The web/ElectronJS front-end that Tachidesk-Server is traditionally shipped with. Usually gets new features faster.
|
- [Tachidesk-WebUI](https://github.com/Suwayomi/Tachidesk-WebUI): The web/ElectronJS front-end that Tachidesk-Server is traditionally shipped with. Usually gets new features faster.
|
||||||
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): The native desktop front-end for Tachidesk-Server. Currently the most advanced.
|
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): The native desktop front-end for Tachidesk-Server. Currently the most advanced.
|
||||||
- [Tachidesk-qtui](https://github.com/Suwayomi/Tachidesk-qtui): A C++/Qt front-end for mobile devices(Android/linux), in super early stage of development.
|
- [Tachidesk-qtui](https://github.com/Suwayomi/Tachidesk-qtui): A C++/Qt front-end for mobile devices(Android/linux), in super early stage of development.
|
||||||
- [Tachidesk-Flutter](https://github.com/Suwayomi/Tachidesk-Flutter): A Flutter front-end for Desktop(Linux, windows, etc.), in early stage of development.
|
- [Tachidesk-Sorayomi](https://github.com/Suwayomi/Tachidesk-Sorayomi): A Flutter front-end for Desktop(Linux, windows, etc.), Web and Android. UI and UX similar to Tachiyomi.
|
||||||
##### Inctive/Abandoned Cients
|
##### Inctive/Abandoned Cients
|
||||||
- [Equinox](https://github.com/Suwayomi/Equinox): A web user interface made with Vue.js, in super early stage of development.
|
- [Equinox](https://github.com/Suwayomi/Equinox): A web user interface made with Vue.js, in super early stage of development.
|
||||||
- [Tachidesk-GTK](https://github.com/mahor1221/Tachidesk-GTK): A native Rust/GTK desktop client, in super early stage of development.
|
- [Tachidesk-GTK](https://github.com/mahor1221/Tachidesk-GTK): A native Rust/GTK desktop client, in super early stage of development.
|
||||||
@@ -80,33 +80,40 @@ If a bundle for your operating system or cpu architecture is not provided then r
|
|||||||
**Node:** Linux launcher scripts are named a bit differently but work the same.
|
**Node:** Linux launcher scripts are named a bit differently but work the same.
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
Download the latest `win32`(Windows 32-bit) or `win64`(Windows 64-bit) release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Tachidesk-preview/releases).
|
Download the latest `win32`(Windows 32-bit) or `win64`(Windows 64-bit) release from [the releases section](https://github.com/Suwayomi/Tachidesk-Server/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Tachidesk-Server-preview/releases).
|
||||||
|
|
||||||
Unzip the downloaded file and double click on one of the launcher scripts.
|
Unzip the downloaded file and double click on one of the launcher scripts.
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
Download the latest `macOS-x64`(older macOS systems) or `macOS-arm64`(Apple M1) release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Tachidesk-preview/releases).
|
Download the latest `macOS-x64`(older macOS systems) or `macOS-arm64`(Apple M1) release from [the releases section](https://github.com/Suwayomi/Tachidesk-Server/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Tachidesk-Server-preview/releases).
|
||||||
|
|
||||||
Unzip the downloaded file and double click on one of the launcher scripts.
|
Unzip the downloaded file and double click on one of the launcher scripts.
|
||||||
|
|
||||||
### GNU/Linux
|
### GNU/Linux
|
||||||
Download the latest `linux-x64`(x86_64) release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Tachidesk-preview/releases).
|
Download the latest `linux-x64`(x86_64) release from [the releases section](https://github.com/Suwayomi/Tachidesk-Server/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Tachidesk-Server-preview/releases).
|
||||||
|
|
||||||
`tar xvf` the downloaded file and double click on one of the launcher scripts or run them using the terminal.
|
`tar xvf` the downloaded file and double click on one of the launcher scripts or run them using the terminal.
|
||||||
|
|
||||||
## Other methods of getting Tachidesk
|
## Other methods of getting Tachidesk
|
||||||
### Arch Linux
|
### Arch Linux
|
||||||
You can install Tachidesk from the AUR
|
You can install Tachidesk from the AUR:
|
||||||
```
|
```
|
||||||
yay -S tachidesk
|
yay -S tachidesk
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ubuntu-based distributions
|
### Debian/Ubuntu
|
||||||
More information can be found on the [PPA's page](https://launchpad.net/~suwayomi/+archive/ubuntu/tachidesk).
|
Download the latest deb package from the release section or Install from the MPR
|
||||||
```
|
```
|
||||||
sudo add-apt-repository ppa:suwayomi/tachidesk
|
git clone https://mpr.makedeb.org/tachidesk-server.git
|
||||||
|
cd tachidesk-server
|
||||||
|
makedeb -si
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ubuntu
|
||||||
|
```
|
||||||
|
sudo add-apt-repository ppa:suwayomi/tachidesk-server
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install tachidesk
|
sudo apt install tachidesk-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ const val kotlinVersion = "1.6.10"
|
|||||||
const val MainClass = "suwayomi.tachidesk.MainKt"
|
const val MainClass = "suwayomi.tachidesk.MainKt"
|
||||||
|
|
||||||
// should be bumped with each stable release
|
// should be bumped with each stable release
|
||||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.6.1"
|
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.6.3"
|
||||||
|
|
||||||
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r911"
|
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r942"
|
||||||
|
|
||||||
// counts commits on the master branch
|
// counts commits on the master branch
|
||||||
val tachideskRevision = runCatching {
|
val tachideskRevision = runCatching {
|
||||||
|
|||||||
@@ -6,43 +6,46 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
echo "creating debian package"
|
echo "Creating DEB Package"
|
||||||
|
pkgname="tachidesk-server"
|
||||||
|
PkgName="Tachidesk-Server"
|
||||||
jar=$(ls ../server/build/*.jar | tail -n1)
|
jar=$(ls ../server/build/*.jar | tail -n1)
|
||||||
release_ver=$(tmp="${jar%-*}" && echo "${tmp##*-}" | tr -d v)
|
pkgver="$(tmp="${jar%-*}" && echo "${tmp##*-}" | tr -d v)"
|
||||||
orig_dir="tachidesk-$release_ver" # dir uses hyphen "-"
|
pkgrel=1
|
||||||
orig_tar_gz="tachidesk_$release_ver.orig.tar.gz" # orig file uses underscore "_"
|
|
||||||
package_name="tachidesk_$release_ver-1_all.deb"
|
|
||||||
|
|
||||||
# copy artifacts
|
srcdir="$pkgname-$pkgver" # uses hyphen "-"
|
||||||
mkdir "$orig_dir"
|
srctgz="${pkgname}_$pkgver.orig.tar.gz" # uses underscore "_"
|
||||||
cp "$jar" "$orig_dir/Tachidesk.jar"
|
deb="${pkgname}_$pkgver-${pkgrel}_all.deb"
|
||||||
cp -r "resources/debian" "$orig_dir"
|
Deb="${PkgName}_$pkgver-${pkgrel}_all.deb"
|
||||||
cp "resources/tachidesk-browser-launcher-standalone.sh" "$orig_dir/debian"
|
|
||||||
cp "resources/tachidesk-debug-launcher-standalone.sh" "$orig_dir/debian"
|
|
||||||
cp "resources/tachidesk-electron-launcher-standalone.sh" "$orig_dir/debian"
|
|
||||||
cp "resources/tachidesk.desktop" "$orig_dir/debian"
|
|
||||||
cp "../server/src/main/resources/icon/faviconlogo.png" "$orig_dir/debian/tachidesk.png"
|
|
||||||
|
|
||||||
# prepare
|
# Prepare
|
||||||
tar cvzf "$orig_tar_gz" "$orig_dir/Tachidesk.jar"
|
mkdir "$srcdir/"
|
||||||
sed -i "s/\${version}/$release_ver/" "$orig_dir/debian/changelog"
|
cp "$jar" "$srcdir/$pkgname.jar"
|
||||||
|
cp "resources/$pkgname-browser-launcher.sh" "$srcdir/"
|
||||||
|
cp "resources/$pkgname-debug-launcher.sh" "$srcdir/"
|
||||||
|
cp "resources/$pkgname-electron-launcher-debian.sh" "$srcdir/$pkgname-electron-launcher.sh"
|
||||||
|
cp "resources/$pkgname.desktop" "$srcdir/"
|
||||||
|
cp "../server/src/main/resources/icon/faviconlogo.png" "$srcdir/$pkgname.png"
|
||||||
|
|
||||||
# build
|
GZIP=-9 tar -cvzf "$srctgz" "$srcdir/"
|
||||||
mkdir "build"
|
|
||||||
mv $orig_dir $orig_tar_gz "build/"
|
cp -r "resources/debian" "$srcdir/"
|
||||||
cd "build/$orig_dir/debian"
|
sed -i "s/\${pkgver}/$pkgver/" "$srcdir/debian/changelog"
|
||||||
|
sed -i "s/\${pkgrel}/$pkgrel/" "$srcdir/debian/changelog"
|
||||||
|
|
||||||
|
# Build
|
||||||
|
mkdir "debuild/"
|
||||||
|
mv "$srctgz" "$srcdir/" "debuild/"
|
||||||
sudo apt install devscripts build-essential dh-exec
|
sudo apt install devscripts build-essential dh-exec
|
||||||
# --lintian-opts --profile debian: build Debian packages on Ubuntu
|
# --lintian-opts --profile are for building Debian packages on Ubuntu
|
||||||
|
cd "debuild/$srcdir/debian"
|
||||||
debuild -uc -us --lintian-opts --profile debian
|
debuild -uc -us --lintian-opts --profile debian
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
# clean build directory
|
|
||||||
mv "build/$package_name" "./"
|
|
||||||
rm -rf "build"
|
|
||||||
|
|
||||||
# clean up from possible previous runs
|
# clean up from possible previous runs
|
||||||
if [ -f "../server/build/$package_name" ]; then
|
if [ -f "../server/build/$Deb" ]; then
|
||||||
rm "../server/build/$package_name"
|
rm "../server/build/$Deb"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mv "$package_name" "../server/build/"
|
mv "debuild/$deb" "../server/build/$Deb"
|
||||||
|
rm -rf "debuild/"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
tachidesk (${version}-1) unstable; urgency=medium
|
tachidesk-server (${pkgver}-${pkgrel}) unstable; urgency=medium
|
||||||
|
|
||||||
* See CHANGELOG.md on https://github.com/Suwayomi/Tachidesk-Server
|
* See CHANGELOG.md on https://github.com/Suwayomi/Tachidesk-Server
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Source: tachidesk
|
Source: tachidesk-server
|
||||||
Section: web
|
Section: web
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Mahor Foruzesh <mahorforuzesh@pm.me>
|
Maintainer: Mahor Foruzesh <mahorforuzesh@pm.me>
|
||||||
@@ -6,7 +6,7 @@ Build-Depends: debhelper-compat (= 12), dh-exec
|
|||||||
Standards-Version: 4.5.1
|
Standards-Version: 4.5.1
|
||||||
Homepage: https://github.com/Suwayomi/Tachidesk-Server
|
Homepage: https://github.com/Suwayomi/Tachidesk-Server
|
||||||
|
|
||||||
Package: tachidesk
|
Package: tachidesk-server
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${misc:Depends}, default-jre-headless (>= 8)
|
Depends: ${misc:Depends}, default-jre-headless (>= 8)
|
||||||
Description: Manga Reader
|
Description: Manga Reader
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
Upstream-Name: tachidesk
|
Upstream-Name: tachidesk-server
|
||||||
Upstream-Contact: https://discord.gg/DDZdqZWaHA
|
Upstream-Contact: https://discord.gg/DDZdqZWaHA
|
||||||
Source: https://github.com/Suwayomi/Tachidesk-Server
|
Source: https://github.com/Suwayomi/Tachidesk-Server
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
#!/usr/bin/dh-exec
|
#!/usr/bin/dh-exec
|
||||||
|
|
||||||
debian/tachidesk-browser-launcher-standalone.sh => usr/bin/tachidesk
|
tachidesk-server-browser-launcher.sh => usr/bin/tachidesk-server-browser
|
||||||
debian/tachidesk-browser-launcher-standalone.sh => usr/bin/tachidesk-browser
|
tachidesk-server-debug-launcher.sh => usr/bin/tachidesk-server-debug
|
||||||
debian/tachidesk-debug-launcher-standalone.sh => usr/bin/tachidesk-debug
|
tachidesk-server-electron-launcher.sh => usr/bin/tachidesk-server-electron
|
||||||
debian/tachidesk-electron-launcher-standalone.sh => usr/bin/tachidesk-electron
|
tachidesk-server.jar usr/share/java/tachidesk-server/
|
||||||
Tachidesk.jar => usr/share/java/tachidesk/tachidesk.jar
|
tachidesk-server.png usr/share/pixmaps/
|
||||||
debian/tachidesk.png usr/share/pixmaps/
|
tachidesk-server.desktop usr/share/applications/
|
||||||
debian/tachidesk.desktop usr/share/applications/
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
debian/tachidesk.png
|
tachidesk-server.png
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
exec /usr/bin/java -jar /usr/share/java/tachidesk/tachidesk.jar
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
./jre/bin/java -jar Tachidesk.jar
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
./jre/bin/java -Dsuwayomi.tachidesk.config.server.debugLogsEnabled=true -jar Tachidesk.jar
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
./jre/bin/java "-Dsuwayomi.tachidesk.config.server.webUIInterface=electron" "-Dsuwayomi.tachidesk.config.server.electronPath=electron/electron" -jar Tachidesk.jar
|
|
||||||
2
scripts/resources/tachidesk-server-browser-launcher.sh
Normal file
2
scripts/resources/tachidesk-server-browser-launcher.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
exec /usr/bin/java -jar /usr/share/java/tachidesk-server/tachidesk-server.jar
|
||||||
2
scripts/resources/tachidesk-debug-launcher-standalone.sh → scripts/resources/tachidesk-server-debug-launcher.sh
Executable file → Normal file
2
scripts/resources/tachidesk-debug-launcher-standalone.sh → scripts/resources/tachidesk-server-debug-launcher.sh
Executable file → Normal file
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
exec /usr/bin/java -Dsuwayomi.tachidesk.config.server.debugLogsEnabled=true -jar /usr/share/java/tachidesk/tachidesk.jar
|
exec /usr/bin/java -Dsuwayomi.tachidesk.config.server.debugLogsEnabled=true -jar /usr/share/java/tachidesk-server/tachidesk-server.jar
|
||||||
10
scripts/resources/tachidesk-electron-launcher-standalone.sh → scripts/resources/tachidesk-server-electron-launcher-debian.sh
Executable file → Normal file
10
scripts/resources/tachidesk-electron-launcher-standalone.sh → scripts/resources/tachidesk-server-electron-launcher-debian.sh
Executable file → Normal file
@@ -1,14 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
if [ ! -f /usr/bin/electron ]; then
|
if [ ! -f /usr/bin/electron ]; then
|
||||||
echo "Electron executable was not found!
|
echo "Electron executable was not found
|
||||||
In order to run this launcher, you need Electron installed.
|
In order to run this launcher, you need Electron installed.
|
||||||
|
|
||||||
You can install it with these commands:
|
You can install it with these commands:
|
||||||
sudo apt install npm
|
sudo apt install npm
|
||||||
sudo npm install electron -g
|
sudo npm install electron -g"
|
||||||
"
|
exit 1
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec /usr/bin/java -Dsuwayomi.tachidesk.config.server.webUIInterface=electron -Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron -jar /usr/share/java/tachidesk/tachidesk.jar
|
exec /usr/bin/java -Dsuwayomi.tachidesk.config.server.webUIInterface=electron -Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron -jar /usr/share/java/tachidesk-server/tachidesk-server.jar
|
||||||
3
scripts/resources/tachidesk-server-electron-launcher.sh
Normal file
3
scripts/resources/tachidesk-server-electron-launcher.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec /usr/bin/java -Dsuwayomi.tachidesk.config.server.webUIInterface=electron -Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron -jar /usr/share/java/tachidesk-server/tachidesk-server.jar
|
||||||
8
scripts/resources/tachidesk-server.desktop
Normal file
8
scripts/resources/tachidesk-server.desktop
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Tachidesk-Server
|
||||||
|
Comment=Manga Reader
|
||||||
|
Exec=/usr/bin/java -jar /usr/share/java/tachidesk-server/tachidesk-server.jar "\\$@"
|
||||||
|
Icon=tachidesk-server
|
||||||
|
Terminal=false
|
||||||
|
Categories=Network;
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=Tachidesk
|
|
||||||
Comment=Manga Reader
|
|
||||||
Exec=/usr/bin/java -jar /usr/share/java/tachidesk/tachidesk.jar "\\$@"
|
|
||||||
Icon=tachidesk
|
|
||||||
Terminal=false
|
|
||||||
Categories=Network;
|
|
||||||
@@ -57,11 +57,11 @@ fi
|
|||||||
unzip $electron -d $release_name/electron
|
unzip $electron -d $release_name/electron
|
||||||
|
|
||||||
# copy artifacts
|
# copy artifacts
|
||||||
cp $jar $release_name/Tachidesk.jar
|
cp $jar $release_name/Tachidesk-Server.jar
|
||||||
if [ $os = linux ]; then
|
if [ $os = linux ]; then
|
||||||
cp "resources/tachidesk-browser-launcher.sh" $release_name
|
cp "resources/tachidesk-server-browser-launcher.sh" $release_name
|
||||||
cp "resources/tachidesk-debug-launcher.sh" $release_name
|
cp "resources/tachidesk-server-debug-launcher.sh" $release_name
|
||||||
cp "resources/tachidesk-electron-launcher.sh" $release_name
|
cp "resources/tachidesk-server-electron-launcher.sh" $release_name
|
||||||
elif [ $os = macOS ]; then
|
elif [ $os = macOS ]; then
|
||||||
cp "resources/Tachidesk Browser Launcher.command" $release_name
|
cp "resources/Tachidesk Browser Launcher.command" $release_name
|
||||||
cp "resources/Tachidesk Debug Launcher.command" $release_name
|
cp "resources/Tachidesk Debug Launcher.command" $release_name
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ dependencies {
|
|||||||
implementation("com.squareup.okhttp3:okhttp:$okhttpVersion")
|
implementation("com.squareup.okhttp3:okhttp:$okhttpVersion")
|
||||||
implementation("io.reactivex:rxjava:1.3.8")
|
implementation("io.reactivex:rxjava:1.3.8")
|
||||||
implementation("org.jsoup:jsoup:1.14.3")
|
implementation("org.jsoup:jsoup:1.14.3")
|
||||||
|
implementation("app.cash.quickjs:quickjs-jvm:0.9.2")
|
||||||
|
|
||||||
// Sort
|
// Sort
|
||||||
implementation("com.github.gpanther:java-nat-sort:natural-comparator-1.1")
|
implementation("com.github.gpanther:java-nat-sort:natural-comparator-1.1")
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package eu.kanade.tachiyomi;
|
||||||
|
|
||||||
|
public class BuildConfig {
|
||||||
|
/** should be something like 74 */
|
||||||
|
public static final int VERSION_CODE = Integer.parseInt(suwayomi.tachidesk.server.BuildConfig.REVISION.substring(1));
|
||||||
|
|
||||||
|
/** should be something like "0.13.1" */
|
||||||
|
public static final String VERSION_NAME = suwayomi.tachidesk.server.BuildConfig.VERSION.substring(1);
|
||||||
|
}
|
||||||
11
server/src/main/kotlin/eu/kanade/tachiyomi/AppInfo.kt
Normal file
11
server/src/main/kotlin/eu/kanade/tachiyomi/AppInfo.kt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package eu.kanade.tachiyomi
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by extensions.
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.3
|
||||||
|
*/
|
||||||
|
object AppInfo {
|
||||||
|
fun getVersionCode() = BuildConfig.VERSION_CODE
|
||||||
|
fun getVersionName() = BuildConfig.VERSION_NAME
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi;
|
|
||||||
|
|
||||||
public class BuildConfig {
|
|
||||||
public static final int VERSION_CODE = -1;
|
|
||||||
public static final String VERSION_NAME = "stub";
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package eu.kanade.tachiyomi.network.interceptor
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OkHttp interceptor that handles rate limiting.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* permits = 5, period = 1, unit = seconds => 5 requests per second
|
||||||
|
* permits = 10, period = 2, unit = minutes => 10 requests per 2 minutes
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.3
|
||||||
|
*
|
||||||
|
* @param permits {Int} Number of requests allowed within a period of units.
|
||||||
|
* @param period {Long} The limiting duration. Defaults to 1.
|
||||||
|
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
||||||
|
*/
|
||||||
|
fun OkHttpClient.Builder.rateLimit(
|
||||||
|
permits: Int,
|
||||||
|
period: Long = 1,
|
||||||
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
|
) = addInterceptor(RateLimitInterceptor(permits, period, unit))
|
||||||
|
|
||||||
|
private class RateLimitInterceptor(
|
||||||
|
private val permits: Int,
|
||||||
|
period: Long,
|
||||||
|
unit: TimeUnit,
|
||||||
|
) : Interceptor {
|
||||||
|
|
||||||
|
private val requestQueue = ArrayList<Long>(permits)
|
||||||
|
private val rateLimitMillis = unit.toMillis(period)
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
synchronized(requestQueue) {
|
||||||
|
val now = SystemClock.elapsedRealtime()
|
||||||
|
val waitTime = if (requestQueue.size < permits) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
val oldestReq = requestQueue[0]
|
||||||
|
val newestReq = requestQueue[permits - 1]
|
||||||
|
|
||||||
|
if (newestReq - oldestReq > rateLimitMillis) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
oldestReq + rateLimitMillis - now // Remaining time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestQueue.size == permits) {
|
||||||
|
requestQueue.removeAt(0)
|
||||||
|
}
|
||||||
|
if (waitTime > 0) {
|
||||||
|
requestQueue.add(now + waitTime)
|
||||||
|
Thread.sleep(waitTime) // Sleep inside synchronized to pause queued requests
|
||||||
|
} else {
|
||||||
|
requestQueue.add(now)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.proceed(chain.request())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package eu.kanade.tachiyomi.network.interceptor
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OkHttp interceptor that handles given url host's rate limiting.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* httpUrl = "api.manga.com".toHttpUrlOrNull(), permits = 5, period = 1, unit = seconds => 5 requests per second to api.manga.com
|
||||||
|
* httpUrl = "imagecdn.manga.com".toHttpUrlOrNull(), permits = 10, period = 2, unit = minutes => 10 requests per 2 minutes to imagecdn.manga.com
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.3
|
||||||
|
*
|
||||||
|
* @param httpUrl {HttpUrl} The url host that this interceptor should handle. Will get url's host by using HttpUrl.host()
|
||||||
|
* @param permits {Int} Number of requests allowed within a period of units.
|
||||||
|
* @param period {Long} The limiting duration. Defaults to 1.
|
||||||
|
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
||||||
|
*/
|
||||||
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
|
httpUrl: HttpUrl,
|
||||||
|
permits: Int,
|
||||||
|
period: Long = 1,
|
||||||
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
|
) = addInterceptor(SpecificHostRateLimitInterceptor(httpUrl, permits, period, unit))
|
||||||
|
|
||||||
|
class SpecificHostRateLimitInterceptor(
|
||||||
|
httpUrl: HttpUrl,
|
||||||
|
private val permits: Int,
|
||||||
|
period: Long,
|
||||||
|
unit: TimeUnit,
|
||||||
|
) : Interceptor {
|
||||||
|
|
||||||
|
private val requestQueue = ArrayList<Long>(permits)
|
||||||
|
private val rateLimitMillis = unit.toMillis(period)
|
||||||
|
private val host = httpUrl.host
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
if (chain.request().url.host != host) {
|
||||||
|
return chain.proceed(chain.request())
|
||||||
|
}
|
||||||
|
synchronized(requestQueue) {
|
||||||
|
val now = SystemClock.elapsedRealtime()
|
||||||
|
val waitTime = if (requestQueue.size < permits) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
val oldestReq = requestQueue[0]
|
||||||
|
val newestReq = requestQueue[permits - 1]
|
||||||
|
|
||||||
|
if (newestReq - oldestReq > rateLimitMillis) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
oldestReq + rateLimitMillis - now // Remaining time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestQueue.size == permits) {
|
||||||
|
requestQueue.removeAt(0)
|
||||||
|
}
|
||||||
|
if (waitTime > 0) {
|
||||||
|
requestQueue.add(now + waitTime)
|
||||||
|
Thread.sleep(waitTime) // Sleep inside synchronized to pause queued requests
|
||||||
|
} else {
|
||||||
|
requestQueue.add(now)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.proceed(chain.request())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package eu.kanade.tachiyomi.source
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source that explicitly doesn't require traffic considerations.
|
||||||
|
*
|
||||||
|
* This typically applies for self-hosted sources.
|
||||||
|
*/
|
||||||
|
interface UnmeteredSource
|
||||||
@@ -55,6 +55,9 @@ interface SManga : Serializable {
|
|||||||
const val ONGOING = 1
|
const val ONGOING = 1
|
||||||
const val COMPLETED = 2
|
const val COMPLETED = 2
|
||||||
const val LICENSED = 3
|
const val LICENSED = 3
|
||||||
|
const val PUBLISHING_FINISHED = 4
|
||||||
|
const val CANCELLED = 5
|
||||||
|
const val ON_HIATUS = 6
|
||||||
|
|
||||||
fun create(): SManga {
|
fun create(): SManga {
|
||||||
return SMangaImpl()
|
return SMangaImpl()
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ object MangaAPI {
|
|||||||
post("{sourceId}/preferences", SourceController::setPreference)
|
post("{sourceId}/preferences", SourceController::setPreference)
|
||||||
|
|
||||||
get("{sourceId}/filters", SourceController::getFilters)
|
get("{sourceId}/filters", SourceController::getFilters)
|
||||||
post("{sourceId}/filters", SourceController::setFilter)
|
post("{sourceId}/filters", SourceController::setFilters)
|
||||||
|
|
||||||
get("{sourceId}/search", SourceController::searchSingle)
|
get("{sourceId}/search", SourceController::searchSingle)
|
||||||
// get("all/search", SourceController::searchGlobal) // TODO
|
// get("all/search", SourceController::searchGlobal) // TODO
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.Context
|
import io.javalin.http.Context
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.kodein.di.DI
|
||||||
|
import org.kodein.di.conf.global
|
||||||
|
import org.kodein.di.instance
|
||||||
import suwayomi.tachidesk.manga.impl.MangaList
|
import suwayomi.tachidesk.manga.impl.MangaList
|
||||||
import suwayomi.tachidesk.manga.impl.Search
|
import suwayomi.tachidesk.manga.impl.Search
|
||||||
import suwayomi.tachidesk.manga.impl.Search.FilterChange
|
import suwayomi.tachidesk.manga.impl.Search.FilterChange
|
||||||
@@ -24,7 +29,7 @@ object SourceController {
|
|||||||
/** fetch source with id `sourceId` */
|
/** fetch source with id `sourceId` */
|
||||||
fun retrieve(ctx: Context) {
|
fun retrieve(ctx: Context) {
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
ctx.json(Source.getSource(sourceId))
|
ctx.json(Source.getSource(sourceId)!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** popular mangas from source with id `sourceId` */
|
/** popular mangas from source with id `sourceId` */
|
||||||
@@ -69,10 +74,16 @@ object SourceController {
|
|||||||
ctx.json(Search.getFilterList(sourceId, reset))
|
ctx.json(Search.getFilterList(sourceId, reset))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** set one filter of source with id `sourceId` */
|
private val json by DI.global.instance<Json>()
|
||||||
fun setFilter(ctx: Context) {
|
|
||||||
|
/** change filters of source with id `sourceId` */
|
||||||
|
fun setFilters(ctx: Context) {
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
val filterChange = ctx.bodyAsClass(FilterChange::class.java)
|
val filterChange = try {
|
||||||
|
json.decodeFromString<List<FilterChange>>(ctx.body())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
listOf(json.decodeFromString<FilterChange>(ctx.body()))
|
||||||
|
}
|
||||||
|
|
||||||
ctx.json(Search.setFilter(sourceId, filterChange))
|
ctx.json(Search.setFilter(sourceId, filterChange))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,8 +201,10 @@ object Chapter {
|
|||||||
val chapterId =
|
val chapterId =
|
||||||
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
|
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
|
||||||
.first()[ChapterTable.id].value
|
.first()[ChapterTable.id].value
|
||||||
val meta =
|
val meta = transaction {
|
||||||
transaction { ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } }.firstOrNull()
|
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
||||||
|
}.firstOrNull()
|
||||||
|
|
||||||
if (meta == null) {
|
if (meta == null) {
|
||||||
ChapterMetaTable.insert {
|
ChapterMetaTable.insert {
|
||||||
it[ChapterMetaTable.key] = key
|
it[ChapterMetaTable.key] = key
|
||||||
@@ -210,7 +212,7 @@ object Chapter {
|
|||||||
it[ChapterMetaTable.ref] = chapterId
|
it[ChapterMetaTable.ref] = chapterId
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ChapterMetaTable.update {
|
ChapterMetaTable.update({ (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }) {
|
||||||
it[ChapterMetaTable.value] = value
|
it[ChapterMetaTable.value] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ package suwayomi.tachidesk.manga.impl
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.local.LocalSource
|
import eu.kanade.tachiyomi.source.local.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insert
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
@@ -23,7 +25,9 @@ import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
|
|||||||
import suwayomi.tachidesk.manga.impl.Source.getSource
|
import suwayomi.tachidesk.manga.impl.Source.getSource
|
||||||
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
||||||
import suwayomi.tachidesk.manga.impl.util.network.await
|
import suwayomi.tachidesk.manga.impl.util.network.await
|
||||||
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
|
||||||
|
import suwayomi.tachidesk.manga.impl.util.source.StubSource
|
||||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.clearCachedImage
|
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.clearCachedImage
|
||||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getImageResponse
|
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getImageResponse
|
||||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
|
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
|
||||||
@@ -34,6 +38,7 @@ import suwayomi.tachidesk.manga.model.table.MangaMetaTable
|
|||||||
import suwayomi.tachidesk.manga.model.table.MangaStatus
|
import suwayomi.tachidesk.manga.model.table.MangaStatus
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
import suwayomi.tachidesk.server.ApplicationDirs
|
import suwayomi.tachidesk.server.ApplicationDirs
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -50,30 +55,10 @@ object Manga {
|
|||||||
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
|
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
|
||||||
|
|
||||||
return if (mangaEntry[MangaTable.initialized] && !onlineFetch) {
|
return if (mangaEntry[MangaTable.initialized] && !onlineFetch) {
|
||||||
MangaDataClass(
|
getMangaDataClass(mangaId, mangaEntry)
|
||||||
mangaId,
|
|
||||||
mangaEntry[MangaTable.sourceReference].toString(),
|
|
||||||
|
|
||||||
mangaEntry[MangaTable.url],
|
|
||||||
mangaEntry[MangaTable.title],
|
|
||||||
proxyThumbnailUrl(mangaId),
|
|
||||||
|
|
||||||
true,
|
|
||||||
|
|
||||||
mangaEntry[MangaTable.artist],
|
|
||||||
mangaEntry[MangaTable.author],
|
|
||||||
mangaEntry[MangaTable.description],
|
|
||||||
mangaEntry[MangaTable.genre].toGenreList(),
|
|
||||||
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
|
||||||
mangaEntry[MangaTable.inLibrary],
|
|
||||||
mangaEntry[MangaTable.inLibraryAt],
|
|
||||||
getSource(mangaEntry[MangaTable.sourceReference]),
|
|
||||||
getMangaMetaMap(mangaId),
|
|
||||||
mangaEntry[MangaTable.realUrl],
|
|
||||||
false
|
|
||||||
)
|
|
||||||
} else { // initialize manga
|
} else { // initialize manga
|
||||||
val source = getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference])
|
val source = getCatalogueSourceOrNull(mangaEntry[MangaTable.sourceReference])
|
||||||
|
?: return getMangaDataClass(mangaId, mangaEntry)
|
||||||
val sManga = SManga.create().apply {
|
val sManga = SManga.create().apply {
|
||||||
url = mangaEntry[MangaTable.url]
|
url = mangaEntry[MangaTable.url]
|
||||||
title = mangaEntry[MangaTable.title]
|
title = mangaEntry[MangaTable.title]
|
||||||
@@ -135,6 +120,29 @@ object Manga {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getMangaDataClass(mangaId: Int, mangaEntry: ResultRow) = MangaDataClass(
|
||||||
|
mangaId,
|
||||||
|
mangaEntry[MangaTable.sourceReference].toString(),
|
||||||
|
|
||||||
|
mangaEntry[MangaTable.url],
|
||||||
|
mangaEntry[MangaTable.title],
|
||||||
|
proxyThumbnailUrl(mangaId),
|
||||||
|
|
||||||
|
true,
|
||||||
|
|
||||||
|
mangaEntry[MangaTable.artist],
|
||||||
|
mangaEntry[MangaTable.author],
|
||||||
|
mangaEntry[MangaTable.description],
|
||||||
|
mangaEntry[MangaTable.genre].toGenreList(),
|
||||||
|
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||||
|
mangaEntry[MangaTable.inLibrary],
|
||||||
|
mangaEntry[MangaTable.inLibraryAt],
|
||||||
|
getSource(mangaEntry[MangaTable.sourceReference]),
|
||||||
|
getMangaMetaMap(mangaId),
|
||||||
|
mangaEntry[MangaTable.realUrl],
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
fun getMangaMetaMap(manga: Int): Map<String, String> {
|
fun getMangaMetaMap(manga: Int): Map<String, String> {
|
||||||
return transaction {
|
return transaction {
|
||||||
MangaMetaTable.select { MangaMetaTable.ref eq manga }
|
MangaMetaTable.select { MangaMetaTable.ref eq manga }
|
||||||
@@ -146,8 +154,10 @@ object Manga {
|
|||||||
transaction {
|
transaction {
|
||||||
val manga = MangaTable.select { MangaTable.id eq mangaId }
|
val manga = MangaTable.select { MangaTable.id eq mangaId }
|
||||||
.first()[MangaTable.id]
|
.first()[MangaTable.id]
|
||||||
val meta =
|
val meta = transaction {
|
||||||
transaction { MangaMetaTable.select { (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) } }.firstOrNull()
|
MangaMetaTable.select { (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) }
|
||||||
|
}.firstOrNull()
|
||||||
|
|
||||||
if (meta == null) {
|
if (meta == null) {
|
||||||
MangaMetaTable.insert {
|
MangaMetaTable.insert {
|
||||||
it[MangaMetaTable.key] = key
|
it[MangaMetaTable.key] = key
|
||||||
@@ -155,7 +165,7 @@ object Manga {
|
|||||||
it[MangaMetaTable.ref] = manga
|
it[MangaMetaTable.ref] = manga
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MangaMetaTable.update {
|
MangaMetaTable.update({ (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) }) {
|
||||||
it[MangaMetaTable.value] = value
|
it[MangaMetaTable.value] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,6 +173,7 @@ object Manga {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||||
|
private val network: NetworkHelper by injectLazy()
|
||||||
suspend fun getMangaThumbnail(mangaId: Int, useCache: Boolean): Pair<InputStream, String> {
|
suspend fun getMangaThumbnail(mangaId: Int, useCache: Boolean): Pair<InputStream, String> {
|
||||||
val saveDir = applicationDirs.thumbnailsRoot
|
val saveDir = applicationDirs.thumbnailsRoot
|
||||||
val fileName = mangaId.toString()
|
val fileName = mangaId.toString()
|
||||||
@@ -176,10 +187,12 @@ object Manga {
|
|||||||
?: if (!mangaEntry[MangaTable.initialized]) {
|
?: if (!mangaEntry[MangaTable.initialized]) {
|
||||||
// initialize then try again
|
// initialize then try again
|
||||||
getManga(mangaId)
|
getManga(mangaId)
|
||||||
transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }[MangaTable.thumbnail_url]!!
|
transaction {
|
||||||
|
MangaTable.select { MangaTable.id eq mangaId }.first()
|
||||||
|
}[MangaTable.thumbnail_url]!!
|
||||||
} else {
|
} else {
|
||||||
// source provides no thumbnail url for this manga
|
// source provides no thumbnail url for this manga
|
||||||
throw NullPointerException()
|
throw NullPointerException("No thumbnail found")
|
||||||
}
|
}
|
||||||
|
|
||||||
source.client.newCall(
|
source.client.newCall(
|
||||||
@@ -199,6 +212,13 @@ object Manga {
|
|||||||
?: "image/jpeg"
|
?: "image/jpeg"
|
||||||
imageFile.inputStream() to contentType
|
imageFile.inputStream() to contentType
|
||||||
}
|
}
|
||||||
|
is StubSource -> getImageResponse(saveDir, fileName, useCache) {
|
||||||
|
val thumbnailUrl = mangaEntry[MangaTable.thumbnail_url]
|
||||||
|
?: throw NullPointerException("No thumbnail found")
|
||||||
|
network.client.newCall(
|
||||||
|
GET(thumbnailUrl)
|
||||||
|
).await()
|
||||||
|
}
|
||||||
else -> throw IllegalArgumentException("Unknown source")
|
else -> throw IllegalArgumentException("Unknown source")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
|||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.plugin.json.JsonMapper
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.conf.global
|
import org.kodein.di.conf.global
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
@@ -80,37 +81,42 @@ object Search {
|
|||||||
val filter: Filter<*>,
|
val filter: Filter<*>,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun setFilter(sourceId: Long, change: FilterChange) {
|
fun setFilter(sourceId: Long, changes: List<FilterChange>) {
|
||||||
val source = getCatalogueSourceOrStub(sourceId)
|
val source = getCatalogueSourceOrStub(sourceId)
|
||||||
val filterList = getFilterListOf(source, false)
|
val filterList = getFilterListOf(source, false)
|
||||||
|
|
||||||
when (val filter = filterList[change.position]) {
|
changes.forEach { change ->
|
||||||
is Filter.Header -> {
|
when (val filter = filterList[change.position]) {
|
||||||
// NOOP
|
is Filter.Header -> {
|
||||||
}
|
// NOOP
|
||||||
is Filter.Separator -> {
|
}
|
||||||
// NOOP
|
is Filter.Separator -> {
|
||||||
}
|
// NOOP
|
||||||
is Filter.Select<*> -> filter.state = change.state.toInt()
|
}
|
||||||
is Filter.Text -> filter.state = change.state
|
is Filter.Select<*> -> filter.state = change.state.toInt()
|
||||||
is Filter.CheckBox -> filter.state = change.state.toBooleanStrict()
|
is Filter.Text -> filter.state = change.state
|
||||||
is Filter.TriState -> filter.state = change.state.toInt()
|
is Filter.CheckBox -> filter.state = change.state.toBooleanStrict()
|
||||||
is Filter.Group<*> -> {
|
is Filter.TriState -> filter.state = change.state.toInt()
|
||||||
val groupChange = jsonMapper.fromJsonString(change.state, FilterChange::class.java)
|
is Filter.Group<*> -> {
|
||||||
|
val groupChange = jsonMapper.fromJsonString(change.state, FilterChange::class.java)
|
||||||
|
|
||||||
when (val groupFilter = filter.state[groupChange.position]) {
|
when (val groupFilter = filter.state[groupChange.position]) {
|
||||||
is Filter.CheckBox -> groupFilter.state = groupChange.state.toBooleanStrict()
|
is Filter.CheckBox -> groupFilter.state = groupChange.state.toBooleanStrict()
|
||||||
is Filter.TriState -> groupFilter.state = groupChange.state.toInt()
|
is Filter.TriState -> groupFilter.state = groupChange.state.toInt()
|
||||||
is Filter.Text -> groupFilter.state = groupChange.state
|
is Filter.Text -> groupFilter.state = groupChange.state
|
||||||
is Filter.Select<*> -> groupFilter.state = groupChange.state.toInt()
|
is Filter.Select<*> -> groupFilter.state = groupChange.state.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Filter.Sort -> {
|
||||||
|
filter.state = jsonMapper.fromJsonString(change.state, Filter.Sort.Selection::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Filter.Sort -> filter.state = jsonMapper.fromJsonString(change.state, Filter.Sort.Selection::class.java)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val jsonMapper by DI.global.instance<JsonMapper>()
|
private val jsonMapper by DI.global.instance<JsonMapper>()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class FilterChange(
|
data class FilterChange(
|
||||||
val position: Int,
|
val position: Int,
|
||||||
val state: String
|
val state: String
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import org.kodein.di.DI
|
|||||||
import org.kodein.di.conf.global
|
import org.kodein.di.conf.global
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
|
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSource
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCatalogueSource
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCatalogueSource
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
|
||||||
@@ -36,8 +36,8 @@ object Source {
|
|||||||
|
|
||||||
fun getSourceList(): List<SourceDataClass> {
|
fun getSourceList(): List<SourceDataClass> {
|
||||||
return transaction {
|
return transaction {
|
||||||
SourceTable.selectAll().map {
|
SourceTable.selectAll().mapNotNull {
|
||||||
val catalogueSource = getCatalogueSourceOrStub(it[SourceTable.id].value)
|
val catalogueSource = getCatalogueSourceOrNull(it[SourceTable.id].value) ?: return@mapNotNull null
|
||||||
val sourceExtension = ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first()
|
val sourceExtension = ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first()
|
||||||
|
|
||||||
SourceDataClass(
|
SourceDataClass(
|
||||||
@@ -54,27 +54,23 @@ object Source {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSource(sourceId: Long): SourceDataClass { // all the data extracted fresh form the source instance
|
fun getSource(sourceId: Long): SourceDataClass? { // all the data extracted fresh form the source instance
|
||||||
return transaction {
|
return transaction {
|
||||||
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
|
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() ?: return@transaction null
|
||||||
val catalogueSource = source?.let { getCatalogueSource(sourceId) }
|
val catalogueSource = getCatalogueSourceOrNull(sourceId) ?: return@transaction null
|
||||||
val extension = source?.let {
|
val extension = ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()
|
||||||
ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceDataClass(
|
SourceDataClass(
|
||||||
sourceId.toString(),
|
sourceId.toString(),
|
||||||
source?.get(SourceTable.name),
|
source[SourceTable.name],
|
||||||
source?.get(SourceTable.lang),
|
source[SourceTable.lang],
|
||||||
source?.let {
|
getExtensionIconUrl(
|
||||||
getExtensionIconUrl(
|
extension[ExtensionTable.apkName]
|
||||||
extension!![ExtensionTable.apkName]
|
),
|
||||||
)
|
catalogueSource.supportsLatest,
|
||||||
},
|
catalogueSource is ConfigurableSource,
|
||||||
catalogueSource?.supportsLatest,
|
source[SourceTable.isNsfw],
|
||||||
catalogueSource?.let { it is ConfigurableSource },
|
catalogueSource.toString()
|
||||||
source?.get(SourceTable.isNsfw),
|
|
||||||
catalogueSource?.toString()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ object PackageTools {
|
|||||||
const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory"
|
const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory"
|
||||||
const val METADATA_NSFW = "tachiyomi.extension.nsfw"
|
const val METADATA_NSFW = "tachiyomi.extension.nsfw"
|
||||||
const val LIB_VERSION_MIN = 1.2
|
const val LIB_VERSION_MIN = 1.2
|
||||||
const val LIB_VERSION_MAX = 1.2
|
const val LIB_VERSION_MAX = 1.3
|
||||||
|
|
||||||
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23" // inorichi's key
|
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23" // inorichi's key
|
||||||
private const val unofficialSignature = "64feb21075ba97ebc9cc981243645b331595c111cef1b0d084236a0403b00581" // ArMor's key
|
private const val unofficialSignature = "64feb21075ba97ebc9cc981243645b331595c111cef1b0d084236a0403b00581" // ArMor's key
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ object GetCatalogueSource {
|
|||||||
private val sourceCache = ConcurrentHashMap<Long, CatalogueSource>()
|
private val sourceCache = ConcurrentHashMap<Long, CatalogueSource>()
|
||||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||||
|
|
||||||
fun getCatalogueSource(sourceId: Long): CatalogueSource? {
|
private fun getCatalogueSource(sourceId: Long): CatalogueSource? {
|
||||||
val cachedResult: CatalogueSource? = sourceCache[sourceId]
|
val cachedResult: CatalogueSource? = sourceCache[sourceId]
|
||||||
if (cachedResult != null) {
|
if (cachedResult != null) {
|
||||||
return cachedResult
|
return cachedResult
|
||||||
@@ -56,8 +56,12 @@ object GetCatalogueSource {
|
|||||||
return sourceCache[sourceId]!!
|
return sourceCache[sourceId]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCatalogueSourceOrNull(sourceId: Long): CatalogueSource? {
|
||||||
|
return runCatching { getCatalogueSource(sourceId) }.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
fun getCatalogueSourceOrStub(sourceId: Long): CatalogueSource {
|
fun getCatalogueSourceOrStub(sourceId: Long): CatalogueSource {
|
||||||
return getCatalogueSource(sourceId) ?: StubSource(sourceId)
|
return getCatalogueSourceOrNull(sourceId) ?: StubSource(sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerCatalogueSource(sourcePair: Pair<Long, CatalogueSource>) {
|
fun registerCatalogueSource(sourcePair: Pair<Long, CatalogueSource>) {
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
|
|||||||
|
|
||||||
data class SourceDataClass(
|
data class SourceDataClass(
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String?,
|
val name: String,
|
||||||
val lang: String?,
|
val lang: String,
|
||||||
val iconUrl: String?,
|
val iconUrl: String,
|
||||||
|
|
||||||
/** The Source provides a latest listing */
|
/** The Source provides a latest listing */
|
||||||
val supportsLatest: Boolean?,
|
val supportsLatest: Boolean,
|
||||||
|
|
||||||
/** The Source implements [ConfigurableSource] */
|
/** The Source implements [ConfigurableSource] */
|
||||||
val isConfigurable: Boolean?,
|
val isConfigurable: Boolean,
|
||||||
|
|
||||||
/** The Source class has a @Nsfw annotation */
|
/** The Source class has a @Nsfw annotation */
|
||||||
val isNsfw: Boolean?,
|
val isNsfw: Boolean,
|
||||||
|
|
||||||
/** A nicer version of [name] */
|
/** A nicer version of [name] */
|
||||||
val displayName: String?,
|
val displayName: String,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -66,7 +66,10 @@ enum class MangaStatus(val value: Int) {
|
|||||||
UNKNOWN(0),
|
UNKNOWN(0),
|
||||||
ONGOING(1),
|
ONGOING(1),
|
||||||
COMPLETED(2),
|
COMPLETED(2),
|
||||||
LICENSED(3);
|
LICENSED(3),
|
||||||
|
PUBLISHING_FINISHED(4),
|
||||||
|
CANCELLED(5),
|
||||||
|
ON_HIATUS(6);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun valueOf(value: Int): MangaStatus = values().find { it.value == value } ?: UNKNOWN
|
fun valueOf(value: Int): MangaStatus = values().find { it.value == value } ?: UNKNOWN
|
||||||
|
|||||||
@@ -54,6 +54,22 @@ object JavalinSetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.enableCorsForAllOrigins()
|
config.enableCorsForAllOrigins()
|
||||||
|
|
||||||
|
config.accessManager { handler, ctx, _ ->
|
||||||
|
fun basicAuthCredentialsValid(): Boolean {
|
||||||
|
val (username, password) = ctx.basicAuthCredentials()
|
||||||
|
return username == serverConfig.basicAuthUsername && password == serverConfig.basicAuthPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverConfig.authType != "none") {
|
||||||
|
if (serverConfig.authType == "basicAuth" && !(ctx.basicAuthCredentialsExist() && basicAuthCredentialsValid())) {
|
||||||
|
ctx.header("WWW-Authenticate", "Basic")
|
||||||
|
ctx.status(401).json("Unauthorized")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handler.handle(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
}.events { event ->
|
}.events { event ->
|
||||||
event.serverStarted {
|
event.serverStarted {
|
||||||
if (serverConfig.initialOpenInBrowserEnabled) {
|
if (serverConfig.initialOpenInBrowserEnabled) {
|
||||||
@@ -83,18 +99,6 @@ object JavalinSetup {
|
|||||||
ctx.result(e.message ?: "Internal Server Error")
|
ctx.result(e.message ?: "Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
app.before { ctx ->
|
|
||||||
fun credentialsValid(): Boolean {
|
|
||||||
val (username, password) = ctx.basicAuthCredentials()
|
|
||||||
return username == serverConfig.basicAuthUsername && password == serverConfig.basicAuthPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverConfig.basicAuthEnabled && !(ctx.basicAuthCredentialsExist() && credentialsValid())) {
|
|
||||||
ctx.header("WWW-Authenticate", "Basic")
|
|
||||||
ctx.status(401).json("Unauthorized")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.routes {
|
app.routes {
|
||||||
path("api/v1/") {
|
path("api/v1/") {
|
||||||
GlobalAPI.defineEndpoints()
|
GlobalAPI.defineEndpoints()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.typesafe.config.Config
|
|||||||
import xyz.nulldev.ts.config.GlobalConfigManager
|
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||||
import xyz.nulldev.ts.config.SystemPropertyOverridableConfigModule
|
import xyz.nulldev.ts.config.SystemPropertyOverridableConfigModule
|
||||||
import xyz.nulldev.ts.config.debugLogsEnabled
|
import xyz.nulldev.ts.config.debugLogsEnabled
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
private const val MODULE_NAME = "server"
|
private const val MODULE_NAME = "server"
|
||||||
class ServerConfig(config: Config, moduleName: String = MODULE_NAME) : SystemPropertyOverridableConfigModule(config, moduleName) {
|
class ServerConfig(config: Config, moduleName: String = MODULE_NAME) : SystemPropertyOverridableConfigModule(config, moduleName) {
|
||||||
@@ -34,6 +35,15 @@ class ServerConfig(config: Config, moduleName: String = MODULE_NAME) : SystemPro
|
|||||||
val electronPath: String by overridableConfig
|
val electronPath: String by overridableConfig
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
|
val authType: String by object {
|
||||||
|
operator fun <R> getValue(thisRef: R, property: KProperty<*>): String {
|
||||||
|
val propValue: String = overridableConfig.getValue(thisRef, property)
|
||||||
|
if (basicAuthEnabled) {
|
||||||
|
return "basicAuth"
|
||||||
|
}
|
||||||
|
return propValue
|
||||||
|
}
|
||||||
|
}
|
||||||
val basicAuthEnabled: Boolean by overridableConfig
|
val basicAuthEnabled: Boolean by overridableConfig
|
||||||
val basicAuthUsername: String by overridableConfig
|
val basicAuthUsername: String by overridableConfig
|
||||||
val basicAuthPassword: String by overridableConfig
|
val basicAuthPassword: String by overridableConfig
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.App
|
|||||||
import eu.kanade.tachiyomi.source.local.LocalSource
|
import eu.kanade.tachiyomi.source.local.LocalSource
|
||||||
import io.javalin.plugin.json.JavalinJackson
|
import io.javalin.plugin.json.JavalinJackson
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.plugin.json.JsonMapper
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.bind
|
import org.kodein.di.bind
|
||||||
@@ -59,6 +60,7 @@ fun applicationSetup() {
|
|||||||
bind<ApplicationDirs>() with singleton { applicationDirs }
|
bind<ApplicationDirs>() with singleton { applicationDirs }
|
||||||
bind<IUpdater>() with singleton { Updater() }
|
bind<IUpdater>() with singleton { Updater() }
|
||||||
bind<JsonMapper>() with singleton { JavalinJackson() }
|
bind<JsonMapper>() with singleton { JavalinJackson() }
|
||||||
|
bind<Json>() with singleton { Json { ignoreUnknownKeys = true } }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ server.webUIInterface = "browser" # "browser" or "electron"
|
|||||||
server.electronPath = ""
|
server.electronPath = ""
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
server.basicAuthEnabled = false
|
server.authType = "none" # "none" or "basicAuth" or "token"
|
||||||
|
server.basicAuthEnabled = false # This is deprecated, use server.authType
|
||||||
server.basicAuthUsername = ""
|
server.basicAuthUsername = ""
|
||||||
server.basicAuthPassword = ""
|
server.basicAuthPassword = ""
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import suwayomi.tachidesk.manga.impl.extension.Extension.uninstallExtension
|
|||||||
import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension
|
import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension
|
||||||
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList
|
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList
|
||||||
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSource
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
|
||||||
import suwayomi.tachidesk.server.applicationSetup
|
import suwayomi.tachidesk.server.applicationSetup
|
||||||
import suwayomi.tachidesk.test.BASE_PATH
|
import suwayomi.tachidesk.test.BASE_PATH
|
||||||
@@ -72,7 +72,7 @@ class TestExtensionCompatibility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sources = getSourceList().map { getCatalogueSource(it.id.toLong())!! as HttpSource }
|
sources = getSourceList().map { getCatalogueSourceOrNull(it.id.toLong())!! as HttpSource }
|
||||||
}
|
}
|
||||||
setLoggingEnabled(true)
|
setLoggingEnabled(true)
|
||||||
File("$BASE_PATH/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.uppercase()} - ${it.id}" })
|
File("$BASE_PATH/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.uppercase()} - ${it.id}" })
|
||||||
|
|||||||
Reference in New Issue
Block a user