Skip to content

Commit

Permalink
6.11.0 commit
Browse files Browse the repository at this point in the history
  • Loading branch information
XilinJia committed Oct 13, 2024
1 parent 9ce9b3f commit 54e9708
Show file tree
Hide file tree
Showing 51 changed files with 1,722 additions and 1,200 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ android {
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

versionCode 3020269
versionName "6.10.0"
versionCode 3020270
versionName "6.11.0"

applicationId "ac.mdiq.podcini.R"
def commit = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,10 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
val retrying = !isLastRunAttempt && !isImmediateFail
if (episodeTitle.length > 20) episodeTitle = episodeTitle.substring(0, 19) + ""

EventFlow.postEvent(FlowEvent.MessageEvent(applicationContext.getString(
if (retrying) R.string.download_error_retrying else R.string.download_error_not_retrying,
episodeTitle), { ctx: Context -> MainActivityStarter(ctx).withDownloadLogsOpen().start() }, applicationContext.getString(
R.string.download_error_details)))
EventFlow.postEvent(FlowEvent.MessageEvent(
applicationContext.getString(if (retrying) R.string.download_error_retrying else R.string.download_error_not_retrying, episodeTitle),
{ ctx: Context -> MainActivityStarter(ctx).withDownloadLogsOpen().start() },
applicationContext.getString(R.string.download_error_details)))
}
private fun getDownloadLogsIntent(context: Context): PendingIntent {
val intent = MainActivityStarter(context).withDownloadLogsOpen().getIntent()
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import ac.mdiq.vista.extractor.stream.StreamInfoItem
import android.content.Context
import android.util.Log
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.types.RealmList
import kotlinx.coroutines.*
import org.jsoup.Jsoup
import java.io.File
Expand Down Expand Up @@ -276,6 +275,10 @@ class FeedBuilder(val context: Context, val showError: (String?, String)->Unit)
media?.episode = item
}
val fo = updateFeed(context, feed, false)
// if (fo?.downloadUrl != null || fo?.link != null) {
// val fLog = SubscriptionLog(fo.id, fo.title?:"", fo.downloadUrl?:"", fo.link?:"", SubscriptionLog.Type.Feed.name)
// upsertBlk(fLog) {}
// }
Logd(TAG, "fo.id: ${fo?.id} feed.id: ${feed.id}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.utils.EpisodeUtil.hasAlmostEnded
import ac.mdiq.podcini.storage.model.EpisodeSortOrder
import ac.mdiq.podcini.storage.model.Rating
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent
Expand Down Expand Up @@ -333,7 +334,7 @@ import kotlin.math.min
it.media!!.setPosition(action.position * 1000)
it.media!!.playedDuration = action.playedDuration * 1000
it.media!!.setLastPlayedTime(action.timestamp!!.time)
it.rating = if (action.isFavorite) Episode.Rating.FAVORITE.code else Episode.Rating.NEUTRAL.code
it.rating = if (action.isFavorite) Rating.FAVORITE.code else Rating.UNRATED.code
it.playState = action.playState
if (hasAlmostEnded(it.media!!)) {
Logd(TAG, "Marking as played")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object UrlChecker {
var url = url_
url = url.trim { it <= ' ' }
val lowerCaseUrl = url.lowercase() // protocol names are case insensitive
Logd(TAG, "prepareUrl lowerCaseUrl: $lowerCaseUrl")
// Logd(TAG, "prepareUrl lowerCaseUrl: $lowerCaseUrl")
return when {
lowerCaseUrl.startsWith("feed://") -> prepareUrl(url.substring("feed://".length))
lowerCaseUrl.startsWith("pcast://") -> prepareUrl(url.substring("pcast://".length))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.EpisodeSortOrder
import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.utils.EpisodeUtil.hasAlmostEnded
import ac.mdiq.podcini.storage.utils.FileNameGenerator.generateFileName
import ac.mdiq.podcini.storage.utils.FilesUtils.getDataFolder
Expand All @@ -35,7 +32,6 @@ import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.text.format.Formatter
import android.util.Log
import android.util.Rational
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
Expand Down Expand Up @@ -942,7 +938,7 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
it.media!!.setPosition(action.position * 1000)
it.media!!.playedDuration = action.playedDuration * 1000
it.media!!.setLastPlayedTime(action.timestamp!!.time)
it.rating = if (action.isFavorite) Episode.Rating.FAVORITE.code else Episode.Rating.NEUTRAL.code
it.rating = if (action.isFavorite) Rating.FAVORITE.code else Rating.UNRATED.code
it.playState = action.playState
if (hasAlmostEnded(it.media!!)) {
Logd(TAG, "Marking as played: $action")
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ object Feeds {
}
}

fun setRating(feed: Feed, rating: Int) {
Logd(TAG, "setRating called $rating")
// return runOnIOScope {
val result = upsertBlk(feed) { it.rating = rating }
// val slog = realm.query(SubscriptionLog::class).query("itemId == $0", feed.id).first().find()
// if (slog != null) upsertBlk(slog) { it.rating = rating }
// EventFlow.postEvent(FlowEvent.RatingEvent(result, result.rating))
// }
}

fun updateFeedDownloadURL(original: String, updated: String) : Job {
Logd(TAG, "updateFeedDownloadURL(original: $original, updated: $updated)")
return runOnIOScope {
Expand Down
56 changes: 55 additions & 1 deletion app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import io.realm.kotlin.dynamic.DynamicRealmObject
import io.realm.kotlin.dynamic.getValue
import io.realm.kotlin.ext.isManaged
import io.realm.kotlin.migration.AutomaticSchemaMigration
import io.realm.kotlin.types.BaseRealmObject
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.TypedRealmObject
import kotlinx.coroutines.*
import kotlin.coroutines.ContinuationInterceptor
import kotlin.reflect.KClass

object RealmDB {
private val TAG: String = RealmDB::class.simpleName ?: "Anonymous"
Expand All @@ -38,13 +41,15 @@ object RealmDB {
PlayQueue::class,
DownloadResult::class,
ShareLog::class,
SubscriptionLog::class,
Chapter::class))
.name("Podcini.realm")
.schemaVersion(25)
.schemaVersion(26)
.migration({ mContext ->
val oldRealm = mContext.oldRealm // old realm using the previous schema
val newRealm = mContext.newRealm // new realm using the new schema
if (oldRealm.schemaVersion() < 25) {
Logd(TAG, "migrating DB from below 25")
mContext.enumerate(className = "Episode") { oldObject: DynamicRealmObject, newObject: DynamicMutableRealmObject? ->
newObject?.run {
set(
Expand All @@ -54,6 +59,55 @@ object RealmDB {
}
}
}
if (oldRealm.schemaVersion() < 26) {
Logd(TAG, "migrating DB from below 26")
mContext.enumerate(className = "Episode") { oldObject: DynamicRealmObject, newObject: DynamicMutableRealmObject? ->
newObject?.run {
if (oldObject.getValue<Long>(fieldName = "rating") == 0L) set("rating", -3L)
}
}
// val feeds = oldRealm.query(className = "Feed").query("id > 10000").find()
// for (f in feeds) {
// val id = f.getValue(propertyName = "id", Long::class)
// val url = f.getNullableValue(propertyName = "downloadUrl", String::class)
// val link = f.getNullableValue(propertyName = "link", String::class)
// val title = f.getNullableValue(propertyName = "eigenTitle", String::class)
// val subLog = newRealm.copyToRealm(
// DynamicMutableRealmObject.create(
// type = "SubscriptionLog",
// mapOf(
// "id" to id/100,
// "itemId" to id,
// "url" to url,
// "link" to link,
// "type" to "Feed",
// "title" to title,
// )
// )
// )
// }
// val episodes = oldRealm.query(className = "Episode").query("feedId < 100").find()
// for (e in episodes) {
// val id = e.getValue(propertyName = "id", Long::class)
// val media = oldRealm.query(className = "EpisodeMedia").query("id == $id").first().find()
// val url = media?.getNullableValue(propertyName = "downloadUrl", String::class) ?:""
// val link = e.getNullableValue(propertyName = "link", String::class)
// val title = e.getNullableValue(propertyName = "title", String::class)
// val subLog = newRealm.copyToRealm(
// DynamicMutableRealmObject.create(
// type = "SubscriptionLog",
// mapOf(
// "id" to id/100,
// "itemId" to id,
// "url" to url,
// "link" to link,
// "type" to "Media",
// "title" to title,
// )
// )
// )
// }
}
})
.build()
realm = Realm.open(config)
Expand Down
17 changes: 1 addition & 16 deletions app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package ac.mdiq.podcini.storage.model

import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.database.Feeds.getFeed
import ac.mdiq.vista.extractor.Vista
import ac.mdiq.vista.extractor.stream.StreamInfo
Expand Down Expand Up @@ -84,7 +83,7 @@ class Episode : RealmObject {
*/
var chapters: RealmList<Chapter> = realmListOf()

var rating: Int = Rating.NEUTRAL.code
var rating: Int = Rating.UNRATED.code

@Ignore
var isFavorite: Boolean = (rating == 2)
Expand Down Expand Up @@ -320,20 +319,6 @@ class Episode : RealmObject {
return if (nr <= Rating.FAVORITE.code) nr else Rating.TRASH.code
}

enum class Rating(val code: Int, val res: Int) {
TRASH(-2, R.drawable.ic_delete),
BAD(-1, androidx.media3.session.R.drawable.media3_icon_thumb_down_filled),
NEUTRAL(0, R.drawable.ic_questionmark),
GOOD(1, androidx.media3.session.R.drawable.media3_icon_thumb_up_filled),
FAVORITE(2, R.drawable.ic_star);

companion object {
fun fromCode(code: Int): Rating {
return enumValues<Rating>().firstOrNull { it.code == code } ?: NEUTRAL
}
}
}

enum class PlayState(val code: Int) {
UNSPECIFIED(-2),
NEW(-1),
Expand Down
38 changes: 22 additions & 16 deletions app/src/main/kotlin/ac/mdiq/podcini/storage/model/Feed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ class Feed : RealmObject {

var hasVideoMedia: Boolean = false

var rating: Int = Rating.NEUTRAL.code

var comment: String = ""

/**
* Returns the value that uniquely identifies this Feed. If the
* feedIdentifier attribute is not null, it will be returned. Else it will
Expand Down Expand Up @@ -182,30 +186,32 @@ class Feed : RealmObject {

/**
* This constructor is used for requesting a feed download (it must not be used for anything else!). It should NOT be
* used if the title of the feed is already known.
* used if the title of the feed is already known. TODO:
*/
constructor(url: String?, lastUpdate: String?) {
constructor(url: String?, lastUpdate: String?, title: String? = null, username: String? = null, password: String? = null) {
this.lastUpdate = lastUpdate
fileUrl = null
this.downloadUrl = url
}

/**
* This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
* used if the title of the feed is already known.
*/
constructor(url: String?, lastUpdate: String?, title: String?) : this(url, lastUpdate) {
this.eigenTitle = title
}

/**
* This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
* used if the title of the feed is already known.
*/
constructor(url: String?, lastUpdate: String?, title: String?, username: String?, password: String?) : this(url, lastUpdate, title) {
preferences = FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password)
}

// /**
// * This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
// * used if the title of the feed is already known.
// */
// constructor(url: String?, lastUpdate: String?, title: String?) : this(url, lastUpdate) {
// this.eigenTitle = title
// }
//
// /**
// * This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
// * used if the title of the feed is already known.
// */
// constructor(url: String?, lastUpdate: String?, title: String?, username: String?, password: String?) : this(url, lastUpdate, title) {
// preferences = FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password)
// }

fun setCustomTitle1(value: String?) {
customTitle = if (value == null || value == eigenTitle) null else value
}
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/kotlin/ac/mdiq/podcini/storage/model/Rating.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ac.mdiq.podcini.storage.model

import ac.mdiq.podcini.R

enum class Rating(val code: Int, val res: Int) {
UNRATED(-3, R.drawable.ic_questionmark),
TRASH(-2, R.drawable.ic_delete),
BAD(-1, androidx.media3.session.R.drawable.media3_icon_thumb_down_filled),
NEUTRAL(0, R.drawable.baseline_sentiment_neutral_24),
GOOD(1, androidx.media3.session.R.drawable.media3_icon_thumb_up_filled),
FAVORITE(2, R.drawable.ic_star);

companion object {
fun fromCode(code: Int): Rating {
return enumValues<Rating>().firstOrNull { it.code == code } ?: NEUTRAL
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ac.mdiq.podcini.storage.model

import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.util.Logd
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey
import java.util.*

class SubscriptionLog: RealmObject {
@PrimaryKey
var id: Long = 0L // this is the Date().time

// this can be that of a feed or a synthetic episode
// var itemId: Long = 0L

var url: String? = null

var link: String? = null

var title: String = ""

var type: String? = null

var cancelDate: Long = 0

var rating: Int = Rating.UNRATED.code

var comment: String = ""

constructor() {}

constructor(itemId: Long, title: String, url: String, link: String, type: String) {
// this.itemId = itemId
this.title = title
this.url = url
this.link = link
this.type = type

// itemId being either feed.id or episode.id is 100 times the creation time
id = itemId / 100
}

enum class Type {
Feed,
Media,
}

companion object {
val TAG: String = SubscriptionLog::class.simpleName ?: "Anonymous"

var feedLogsMap: Map<String, SubscriptionLog>? = null
get() {
if (field == null) field = getFeedLogMap()
return field
}

fun getFeedLogMap(): Map<String, SubscriptionLog> {
val logs = realm.query(SubscriptionLog::class).query("type == $0", "Feed").find()
val map = mutableMapOf<String, SubscriptionLog>()
for (l in logs) {
Logd(TAG, "getFeedLogMap ${l.title} ${l.url}")
if (!l.url.isNullOrEmpty()) map[l.url!!] = l
if (!l.link.isNullOrEmpty()) map[l.link!!] = l
}
return map.toMap()
}
}
}
Loading

0 comments on commit 54e9708

Please sign in to comment.