Compare commits

...

2 Commits

Author SHA1 Message Date
schroda
8fbc8fd3d4 Fix automatic chapter downloads (#2098)
The returned result rows of the inserted chapters did not have the up-to-date "last_modified_at".
This caused "downloadNewChapters" to not be able to correctly detect unread chapters. it included the newly inserted ones, leading to exiting early due to having unread chapters.

Regression 811e15162b

fixes #2097
2026-06-08 14:21:57 -04:00
Constantin Piber
c81020dbb1 CEF: Remove jogl and jogamp deps by implementing a no-op renderer (#2095)
* CEF: Remove jogl and jogamp deps by implementing a no-op renderer

* Update readme
2026-06-08 14:21:47 -04:00
5 changed files with 34 additions and 18 deletions

View File

@@ -68,6 +68,7 @@ import org.cef.handler.CefLoadHandler
import org.cef.handler.CefLoadHandlerAdapter import org.cef.handler.CefLoadHandlerAdapter
import org.cef.handler.CefMessageRouterHandlerAdapter import org.cef.handler.CefMessageRouterHandlerAdapter
import org.cef.handler.CefPermissionHandler import org.cef.handler.CefPermissionHandler
import org.cef.handler.CefRenderHandlerAdapter
import org.cef.handler.CefRequestHandler import org.cef.handler.CefRequestHandler
import org.cef.handler.CefRequestHandlerAdapter import org.cef.handler.CefRequestHandlerAdapter
import org.cef.handler.CefResourceHandler import org.cef.handler.CefResourceHandler
@@ -82,10 +83,13 @@ import org.cef.network.CefPostDataElement
import org.cef.network.CefRequest import org.cef.network.CefRequest
import org.cef.network.CefResponse import org.cef.network.CefResponse
import org.koin.mp.KoinPlatformTools import org.koin.mp.KoinPlatformTools
import java.awt.Rectangle
import java.io.BufferedWriter import java.io.BufferedWriter
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.swing.JPanel
import kotlin.math.min import kotlin.math.min
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredMemberFunctions import kotlin.reflect.full.declaredMemberFunctions
@@ -97,6 +101,7 @@ class KcefWebViewProvider(
private val settings = KcefWebSettings() private val settings = KcefWebSettings()
private var viewClient = WebViewClient() private var viewClient = WebViewClient()
private var chromeClient = WebChromeClient() private var chromeClient = WebChromeClient()
private val renderHandler = RenderHandler()
private val mappings: MutableList<FunctionMapping> = mutableListOf() private val mappings: MutableList<FunctionMapping> = mutableListOf()
private val urlHttpMapping: MutableMap<String, String> = mutableMapOf() private val urlHttpMapping: MutableMap<String, String> = mutableMapOf()
private var initialRequestData: InitialRequestData? = null private var initialRequestData: InitialRequestData? = null
@@ -522,6 +527,21 @@ class KcefWebViewProvider(
} }
} }
private class RenderHandler : CefRenderHandlerAdapter() {
override fun getViewRect(browser: CefBrowser): Rectangle = Rectangle(0, 0, 1280, 2856)
override fun onPaint(
browser: CefBrowser,
popup: Boolean,
dirtyRects: Array<Rectangle>,
buffer: ByteBuffer,
width: Int,
height: Int,
) {
// do nothing
}
}
override fun init( override fun init(
javaScriptInterfaces: Map<String, Any>?, javaScriptInterfaces: Map<String, Any>?,
privateBrowsing: Boolean, privateBrowsing: Boolean,
@@ -617,7 +637,7 @@ class KcefWebViewProvider(
kcefClient!! kcefClient!!
.createBrowser( .createBrowser(
loadUrl, loadUrl,
CefRendering.OFFSCREEN, CefRendering.CefRenderingWithHandler(renderHandler, JPanel()),
false, false,
).apply { ).apply {
// NOTE: Without this, we don't seem to be receiving any events // NOTE: Without this, we don't seem to be receiving any events
@@ -642,7 +662,7 @@ class KcefWebViewProvider(
kcefClient!! kcefClient!!
.createBrowser( .createBrowser(
url, url,
CefRendering.OFFSCREEN, CefRendering.CefRenderingWithHandler(renderHandler, JPanel()),
false, false,
).apply { ).apply {
// NOTE: Without this, we don't seem to be receiving any events // NOTE: Without this, we don't seem to be receiving any events
@@ -676,7 +696,7 @@ class KcefWebViewProvider(
kcefClient!! kcefClient!!
.createBrowser( .createBrowser(
url, url,
CefRendering.OFFSCREEN, CefRendering.CefRenderingWithHandler(renderHandler, JPanel()),
false, false,
).apply { ).apply {
// NOTE: Without this, we don't seem to be receiving any events // NOTE: Without this, we don't seem to be receiving any events

View File

@@ -110,18 +110,13 @@ Download the latest `linux-x64`(x86_64) release from [the releases section](http
WebView support is implemented via [JCEF](https://github.com/JetBrains/jcef). WebView support is implemented via [JCEF](https://github.com/JetBrains/jcef).
This is optional, and is only necessary to support some extensions. This is optional, and is only necessary to support some extensions.
To have a functional WebView, several dependencies are required; aside from X11 libraries necessary for rendering Chromium, some JNI bindings are necessary: gluegen and jogl (found in Ubuntu as `libgluegen2-jni` and `libjogl2-jni`). To have a functional WebView, some X11 dependencies are required for rendering Chromium.
Note that on some systems (e.g. Ubuntu), the JNI libraries are not automatically found, see below. These include `libxrender`, `libxcomposite` `libxdamage`, `libxkbcommon` and `libxtst`.
A CEF server is launched on startup, which loads the X11 libraries. A CEF server is launched on startup, which loads the X11 libraries.
If those are missing, you should see "Could not load 'jcef' library". If those are missing, you should see "Could not load 'jcef' library".
If so, use `ldd ~/.local/share/Tachidesk/bin/kcef/libjcef.so | grep not` to figure out which libraries are not found on your system. If so, use `ldd ~/.local/share/Tachidesk/bin/kcef/libjcef.so | grep not` to figure out which libraries are not found on your system.
The JNI bindings are only loaded when a browser is actually launched.
This is done by extensions that rely on WebView, not by Suwayomi itself.
If there is a problem loading the JNI libraries, you should see a message indicating the library and the search path.
This search path includes the current working directory, if you do not want to modify system directories.
Refer to the [Dockerfile](https://github.com/Suwayomi/Suwayomi-Server-docker/blob/main/Dockerfile) for more details. Refer to the [Dockerfile](https://github.com/Suwayomi/Suwayomi-Server-docker/blob/main/Dockerfile) for more details.
Note that it is required to have an X session active and available to Suwayomi (i.e. `DISPLAY` is set). Note that it is required to have an X session active and available to Suwayomi (i.e. `DISPLAY` is set).

View File

@@ -158,8 +158,6 @@ cronUtils = "com.cronutils:cron-utils:9.2.1"
# Webview # Webview
jcef = { module = "org.jetbrains.intellij.deps.jcef:jcef", version.ref = "jcef" } jcef = { module = "org.jetbrains.intellij.deps.jcef:jcef", version.ref = "jcef" }
gluegen = "org.jogamp.gluegen:gluegen-rt:2.5.0"
jogl = "org.jogamp.jogl:jogl-all:2.5.0"
# User # User
jwt = "com.auth0:java-jwt:4.5.2" jwt = "com.auth0:java-jwt:4.5.2"

View File

@@ -37,10 +37,6 @@ dependencies {
implementation(libs.bundles.shared) implementation(libs.bundles.shared)
testImplementation(libs.bundles.sharedTest) testImplementation(libs.bundles.sharedTest)
// WebView
implementation(libs.gluegen)
implementation(libs.jogl)
// OkHttp // OkHttp
implementation(libs.bundles.okhttp) implementation(libs.bundles.okhttp)
implementation(libs.okio) implementation(libs.okio)

View File

@@ -194,7 +194,7 @@ object Chapter {
} }
// new chapters after they have been added to the database for auto downloads // new chapters after they have been added to the database for auto downloads
val insertedChapters = mutableListOf<ChapterDataClass>() val insertedChapterIds = mutableListOf<Int>()
val chaptersToInsert = mutableListOf<ChapterDataClass>() // do not yet have an ID from the database val chaptersToInsert = mutableListOf<ChapterDataClass>() // do not yet have an ID from the database
val chaptersToUpdate = mutableListOf<ChapterDataClass>() val chaptersToUpdate = mutableListOf<ChapterDataClass>()
@@ -309,7 +309,7 @@ object Chapter {
} }
} }
} }
}.forEach { insertedChapters.add(ChapterTable.toDataClass(it)) } }.forEach { insertedChapterIds.add(it[ChapterTable.id].value) }
} }
if (chaptersToUpdate.isNotEmpty()) { if (chaptersToUpdate.isNotEmpty()) {
@@ -354,6 +354,13 @@ object Chapter {
} }
if (manga.inLibrary) { if (manga.inLibrary) {
// We have to query the inserted chapters to get the up-to-date data. I.e. "last_modified_at" is not returned by the insert statement, due to being set by a DB trigger
val insertedChapters =
transaction {
ChapterTable.selectAll().where { ChapterTable.id inList insertedChapterIds }.map(
ChapterTable::toDataClass,
)
}
downloadNewChapters(mangaId, currentLatestChapterNumber, numberOfCurrentChapters, insertedChapters) downloadNewChapters(mangaId, currentLatestChapterNumber, numberOfCurrentChapters, insertedChapters)
} }