Skip to content

Commit

Permalink
FreshRSS Refresh Items (#477)
Browse files Browse the repository at this point in the history
* Refresh subscriptions, folders

* Fetch all articles

* Sync article statuses

* Fetch missing articles
  • Loading branch information
jocmp authored Nov 7, 2024
1 parent 2909374 commit 31549bd
Show file tree
Hide file tree
Showing 21 changed files with 782 additions and 78 deletions.
8 changes: 6 additions & 2 deletions capy/src/main/java/com/jocmp/capy/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import com.jocmp.capy.accounts.LocalAccountDelegate
import com.jocmp.capy.accounts.LocalOkHttpClient
import com.jocmp.capy.accounts.Source
import com.jocmp.capy.accounts.asOPML
import com.jocmp.capy.accounts.reader.buildFreshRSSDelegate
import com.jocmp.capy.accounts.feedbin.FeedbinAccountDelegate
import com.jocmp.capy.accounts.feedbin.FeedbinOkHttpClient
import com.jocmp.capy.accounts.reader.ReaderAccountDelegate
import com.jocmp.capy.articles.UnreadSortOrder
import com.jocmp.capy.common.TimeHelpers.nowUTC
import com.jocmp.capy.common.sortedByTitle
Expand Down Expand Up @@ -47,7 +47,11 @@ data class Account(
)
)

Source.FRESHRSS -> ReaderAccountDelegate()
Source.FRESHRSS -> buildFreshRSSDelegate(
database = database,
path = cacheDirectory,
preferences = preferences
)
}
) {
internal val articleRecords: ArticleRecords = ArticleRecords(database)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.jocmp.feedbinclient
package com.jocmp.capy.accounts

import okhttp3.Interceptor

class BasicAuthInterceptor(private val credentials: () -> String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val request = chain.request()

if (request.headers("Authorization").isNullOrEmpty()) {
if (request.headers("Authorization").isEmpty()) {
val authenticatedRequest =
request.newBuilder().header("Authorization", credentials()).build()
return chain.proceed(authenticatedRequest)
Expand Down
20 changes: 20 additions & 0 deletions capy/src/main/java/com/jocmp/capy/accounts/WithErrorHandling.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.jocmp.capy.accounts

import com.jocmp.capy.common.UnauthorizedError
import java.net.UnknownHostException

internal suspend fun <T> withErrorHandling(func: suspend () -> T?): Result<T> {
return try {
val result = func()

if (result != null) {
Result.success(result)
} else {
Result.failure(Throwable("Unexpected error"))
}
} catch (e: UnknownHostException) {
return Result.failure(e)
} catch (e: UnauthorizedError) {
return Result.failure(e)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.jocmp.capy.Feed
import com.jocmp.capy.accounts.AddFeedResult
import com.jocmp.capy.accounts.FeedOption
import com.jocmp.capy.accounts.SubscriptionChoice
import com.jocmp.capy.accounts.withErrorHandling
import com.jocmp.capy.common.TimeHelpers
import com.jocmp.capy.common.UnauthorizedError
import com.jocmp.capy.common.host
Expand All @@ -31,7 +32,6 @@ import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import okio.IOException
import org.jsoup.Jsoup
import java.net.UnknownHostException
import java.time.ZonedDateTime

internal class FeedbinAccountDelegate(
Expand Down Expand Up @@ -193,7 +193,7 @@ internal class FeedbinAccountDelegate(

override suspend fun refresh(cutoffDate: ZonedDateTime?): Result<Unit> {
return try {
val since = articleRecords.maxUpdatedAt()
val since = articleRecords.maxUpdatedAt().toString()

refreshFeeds()
refreshTaggings()
Expand All @@ -207,7 +207,7 @@ internal class FeedbinAccountDelegate(
}
}

private suspend fun refreshArticles(since: String = articleRecords.maxUpdatedAt()) {
private suspend fun refreshArticles(since: String = articleRecords.maxUpdatedAt().toString()) {
refreshStarredEntries()
refreshUnreadEntries()
refreshAllArticles(since = since)
Expand Down Expand Up @@ -283,7 +283,7 @@ internal class FeedbinAccountDelegate(
coroutineScope {
ids.chunked(MAX_ENTRY_LIMIT).map { chunkedIDs ->
launch {
fetchPaginatedEntries(ids = chunkedIDs)
fetchPaginatedEntries(ids = chunkedIDs.map { it.toLong() })
}
}
}
Expand Down Expand Up @@ -314,10 +314,10 @@ internal class FeedbinAccountDelegate(
)
}

private fun saveEntries(entries: List<Entry>, updatedAt: ZonedDateTime = TimeHelpers.nowUTC()) {
private fun saveEntries(entries: List<Entry>) {
database.transactionWithErrorHandling {
entries.forEach { entry ->
val updated = updatedAt.toEpochSecond()
val updated = TimeHelpers.nowUTC().toEpochSecond()

database.articlesQueries.create(
id = entry.id.toString(),
Expand Down Expand Up @@ -356,20 +356,4 @@ internal class FeedbinAccountDelegate(
const val MAX_ENTRY_LIMIT = 100
const val MAX_CREATE_UNREAD_LIMIT = 1_000
}

private suspend fun <T> withErrorHandling(func: suspend () -> T?): Result<T> {
return try {
val result = func()

if (result != null) {
Result.success(result)
} else {
Result.failure(Throwable("Unexpected error"))
}
} catch (e: UnknownHostException) {
return Result.failure(e)
} catch (e: UnauthorizedError) {
return Result.failure(e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.jocmp.capy.accounts.feedbin

import com.jocmp.capy.AccountPreferences
import com.jocmp.capy.accounts.httpClientBuilder
import com.jocmp.feedbinclient.BasicAuthInterceptor
import com.jocmp.capy.accounts.BasicAuthInterceptor
import okhttp3.Credentials
import okhttp3.OkHttpClient
import java.net.URI
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.jocmp.capy.accounts.reader

import com.jocmp.capy.AccountDelegate
import com.jocmp.capy.AccountPreferences
import com.jocmp.capy.accounts.httpClientBuilder
import com.jocmp.capy.db.Database
import com.jocmp.readerclient.GoogleReader
import java.net.URI

internal fun buildFreshRSSDelegate(
database: Database,
path: URI,
preferences: AccountPreferences
): AccountDelegate {
val httpClient = ReaderOkHttpClient.forAccount(path, preferences)

return ReaderAccountDelegate(
database = database,
httpClient = httpClientBuilder(cachePath = path).build(),
googleReader = GoogleReader.create(
client = httpClient,
baseURL = preferences.url.get()
)
)
}
Loading

0 comments on commit 31549bd

Please sign in to comment.