]> uap-core.de Git - rssreader.git/commitdiff
implement feed sync
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 27 Aug 2025 15:01:31 +0000 (17:01 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 27 Aug 2025 15:01:31 +0000 (17:01 +0200)
rss-application/pom.xml
rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/Database.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/FeedList.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/Item.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/SyncJob.kt [new file with mode: 0644]

index 5ef160aa76fec0590535e0dd1c53ac52354d5d3d..38fa064c9e7a068f478ade609ea7995099c0b851 100644 (file)
             <artifactId>h2</artifactId>
             <version>2.3.232</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+            <version>7.0.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.rometools</groupId>
+            <artifactId>rome</artifactId>
+            <version>2.1.0</version>
+        </dependency>
     </dependencies>
 
 </project>
index 744fc912cb92aab5c3c49574c0ae0b3bcb9a0c29..a1c52bada07bd34ea2b4f42f48b3987969520bd1 100644 (file)
@@ -1,9 +1,24 @@
 package de.unixwork.rssreader
 
 import de.unixwork.ui.Application
+import de.unixwork.ui.ToolbarPosition
 import de.unixwork.ui.Toolkit
+import de.unixwork.ui.kotlin.toolbarItem
+import de.unixwork.ui.kotlin.addToolbarDefault
 
 class App : Application {
+    init {
+        initToolbar()
+    }
+
+    fun initToolbar() {
+        toolbarItem(name = "reload", icon = "view-refresh") {
+            SyncJob().sync()
+        }
+
+        addToolbarDefault("reload", ToolbarPosition.LEFT)
+    }
+
     override fun startup() {
         val window = MainWindow()
         window.show()
index 84894e850513e74c7f9217f6f7bdcd858c5a9abf..77125e9b2143d7c1daf15a31359ed4c40a7dfecf 100644 (file)
@@ -1,15 +1,28 @@
 package de.unixwork.rssreader
 
+import com.zaxxer.hikari.HikariConfig
+import com.zaxxer.hikari.HikariDataSource
 import de.unixwork.ui.Context
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.IO
+import kotlinx.coroutines.launch
 import java.sql.Connection
 import java.sql.DriverManager
 import java.sql.Statement
 
 object Database {
     val connection: Connection
+    val dataSource: HikariDataSource
 
     init {
         connection = DriverManager.getConnection("jdbc:h2:~/.rssreader/feeds")
+
+        val config = HikariConfig()
+        config.jdbcUrl = "jdbc:h2:~/.rssreader/feeds"
+        config.maximumPoolSize = 16
+        dataSource = HikariDataSource(config)
+
         ensureSchema(connection)
     }
 
@@ -69,7 +82,8 @@ object Database {
                         author VARCHAR,
                         pub_date TIMESTAMP,
                         guid VARCHAR UNIQUE,
-                        content CLOB
+                        contentText CLOB,
+                        contentHTML CLOB
                     )
                 """.trimIndent())
 
@@ -198,9 +212,10 @@ object Database {
                     item.link = rs.getString("link")
                     item.description = rs.getString("description")
                     item.author = rs.getString("author")
-                    item.pubDate = rs.getString("pub_date")
+                    item.pubDate = rs.getDate("pub_date")
                     item.guid = rs.getString("guid")
-                    item.content = rs.getString("content")
+                    item.contentText = rs.getString("contentText")
+                    item.contentHtml = rs.getString("contentHTML")
                     item.feedName = feedCollection.name
                     item.feedUrl = rs.getString("URL")
                     items.add(item)
@@ -209,4 +224,54 @@ object Database {
         }
         return items
     }
+
+    public fun getAllFeeds() : MutableList<Feed> {
+        val feeds = mutableListOf<Feed>()
+
+        connection.prepareStatement("""
+            select * from feeds
+        """.trimIndent()).use { stmt ->
+            stmt.executeQuery().use { rs ->
+                while(rs.next()) {
+                    val id = rs.getInt("feed_id")
+                    val feedCollectionId = rs.getInt("feedcollection_id")
+                    val url = rs.getString("url")
+                    val authUser = rs.getString("auth_user")
+                    val authPassword = rs.getString("auth_password")
+                    val certPath = rs.getString("certpath")
+                    val feed = Feed(id, feedCollectionId, url)
+                    feeds.add(feed)
+                }
+            }
+        }
+
+        return feeds
+    }
+
+    public fun addItems(items: Collection<Item>) {
+        GlobalScope.launch(Dispatchers.IO) {
+            dataSource.connection.use { conn ->
+                conn.prepareStatement("""
+                    insert into items (feed_id, title, link, description, author, pub_date, guid, contentText, contentHTML)
+                    select ?, ?, ?, ?, ?, ?, ?, ?, ? 
+                    where ? not in (select guid from items) 
+                """.trimIndent()).use { stmt ->
+                    items.forEach { item ->
+                        stmt.setInt(1, item.feedId)
+                        stmt.setString(2, item.title)
+                        stmt.setString(3, item.link)
+                        stmt.setString(4, item.description)
+                        stmt.setString(5, item.author)
+                        stmt.setTimestamp(6, item.pubDate?.let { java.sql.Timestamp.from(it.toInstant()) })
+                        stmt.setString(7, item.guid)
+                        stmt.setString(8, item.contentText)
+                        stmt.setString(9, item.contentHtml)
+                        stmt.setString(10, item.guid)
+                        stmt.addBatch()
+                    }
+                    stmt.executeBatch()
+                }
+            }
+        }
+    }
 }
index 1b865fd0b8071e9ddf4052469cfe495032eda7db..af1644e3bf75287dfe3782518d61e381114971ca 100644 (file)
@@ -27,7 +27,23 @@ class FeedList : Document() {
         feedName.setString(item.feedName)
         author.setString(item.author)
         link.set(item.link, item.link)
-        webview.loadContent(null, item.content ?: "", "text/html", "utf-8")
 
+        var mimeType: String? = null
+        var content: String? = null
+        if(item.contentHtml != null) {
+            content = item.contentHtml
+            mimeType = "text/html"
+        } else if(item.contentText != null) {
+            content = item.contentText
+            mimeType = "text/plain"
+        } else if(item.description != null) {
+            content = item.description
+            mimeType = "text/html"
+        } else {
+            content = ""
+            mimeType = "text/plain"
+        }
+
+        webview.loadContent(null, content, mimeType, "utf-8")
     }
 }
\ No newline at end of file
index 0bd14cd1017aa845577eb23090e19ea4377cec50..64c1e18b85ad53585634d9745090bb8ab56d69ac 100644 (file)
@@ -1,5 +1,7 @@
 package de.unixwork.rssreader
 
+import java.util.Date
+
 class Item(id: Int) {
     val id = id
     var feedId = -1
@@ -7,9 +9,10 @@ class Item(id: Int) {
     var link: String? = null
     var description: String? = null
     var author: String? = null
-    var pubDate: String? = null
+    var pubDate: Date? = null
     var guid: String? = null
-    var content: String? = null
+    var contentText: String? = null
+    var contentHtml: String? = null
 
     var feedName: String? = null
     var feedUrl: String? = null
index 386088789e0ba791bcb8e835f37adabc60af47c8..83209e0b21ef29cbab5702ac3fdbbaab68debfee 100644 (file)
@@ -71,7 +71,7 @@ class MainWindow {
                         var result: String? = null
                         when(col) {
                             0 -> result = elm.title
-                            1 -> result = elm.pubDate
+                            1 -> result = elm.pubDate.toString()
                         }
                         result
                     }
diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/SyncJob.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/SyncJob.kt
new file mode 100644 (file)
index 0000000..f4b5d59
--- /dev/null
@@ -0,0 +1,78 @@
+package de.unixwork.rssreader
+
+import com.rometools.rome.io.SyndFeedInput
+import com.rometools.rome.io.XmlReader
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.IO
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.launch
+import java.net.URI
+import java.net.http.HttpClient
+import java.net.http.HttpRequest
+import java.net.http.HttpResponse
+
+class SyncJob {
+    val feeds: List<Feed>
+
+    init {
+        feeds = Database.getAllFeeds()
+    }
+
+    fun sync() {
+        GlobalScope.launch(Dispatchers.IO) {
+            val client = HttpClient.newBuilder().build()
+            val jobs = feeds.map { feed ->
+                async {
+                    try {
+                        val request = HttpRequest.newBuilder()
+                            .uri(URI(feed.uri))
+                            .GET()
+                            .build()
+
+                        val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream())
+                        response.body().use { stream ->
+                            val input = SyndFeedInput()
+                            val syndFeed = input.build(XmlReader(stream))
+                            val items = mutableListOf<Item>()
+                            println("Fetched feed: ${syndFeed.title}")
+                            syndFeed.entries.forEach { entry ->
+                                println("  ${entry.title} - ${entry.link}")
+
+                                val item = Item(0)
+                                item.feedId = feed.id
+                                item.title = entry.title
+                                item.link = entry.link
+                                item.description = entry.description?.value
+                                item.author = entry.author
+                                item.pubDate = entry.publishedDate
+                                item.guid = entry.uri
+                                val contents = entry.contents
+                                contents.forEach { content ->
+                                    if(content.type == null) {
+                                        item.contentText = content.value
+                                    } else {
+                                        if(content.type.startsWith("text/html")) {
+                                            item.contentHtml = content.value
+                                        } else if(content.type.startsWith("text/plain")) {
+                                            item.contentText = content.value
+                                        }
+                                    }
+                                }
+
+                                items.add(item)
+                            }
+                            Database.addItems(items)
+                            syndFeed
+                        }
+                    } catch (e: Exception) {
+                        println("Failed to fetch ${feed.uri}: ${e.message}")
+                        null
+                    }
+                }
+            }
+            jobs.awaitAll()
+        }
+    }
+}
\ No newline at end of file