6.7.1 commit
This commit is contained in:
parent
692426d114
commit
daeee36985
|
@ -15,8 +15,8 @@ An open source podcast instrument, attuned to Puccini ![Puccini](./images/Puccin
|
||||||
#### Podcini.R 6.6 introduces the powerful feature of synthetic podcasts, enables the receiving/handling shared single media as well as playlist from Youtube and YT Music, for more see the Youtube section below or the changelogs.
|
#### Podcini.R 6.6 introduces the powerful feature of synthetic podcasts, enables the receiving/handling shared single media as well as playlist from Youtube and YT Music, for more see the Youtube section below or the changelogs.
|
||||||
That means finally: [Nessun dorma](https://www.youtube.com/watch?v=cWc7vYjgnTs)
|
That means finally: [Nessun dorma](https://www.youtube.com/watch?v=cWc7vYjgnTs)
|
||||||
#### Podcini.R version 6.5 as a major step forward brings YouTube channels in the app. They can be searched, received from share, subscribed and played from within Podcini. For more see the Youtube section below or the changelogs
|
#### Podcini.R version 6.5 as a major step forward brings YouTube channels in the app. They can be searched, received from share, subscribed and played from within Podcini. For more see the Youtube section below or the changelogs
|
||||||
#### If you are migrating from Podcini version 5, please read the migrationTo5.md file for migration instructions.
|
|
||||||
#### For Podcini to show up on car's HUD with Android Auto, please read AnroidAuto.md for instructions.
|
#### For Podcini to show up on car's HUD with Android Auto, please read AnroidAuto.md for instructions.
|
||||||
|
#### If you are migrating from Podcini version 5, please read the migrationTo5.md file for migration instructions.
|
||||||
|
|
||||||
This project was developed from a fork of [AntennaPod](<https://github.com/AntennaPod/AntennaPod>) as of Feb 5 2024.
|
This project was developed from a fork of [AntennaPod](<https://github.com/AntennaPod/AntennaPod>) as of Feb 5 2024.
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@ android {
|
||||||
testApplicationId "ac.mdiq.podcini.tests"
|
testApplicationId "ac.mdiq.podcini.tests"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
versionCode 3020253
|
versionCode 3020254
|
||||||
versionName "6.7.0"
|
versionName "6.7.1"
|
||||||
|
|
||||||
applicationId "ac.mdiq.podcini.R"
|
applicationId "ac.mdiq.podcini.R"
|
||||||
def commit = ""
|
def commit = ""
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package ac.mdiq.podcini.net.download.service
|
package ac.mdiq.podcini.net.download.service
|
||||||
|
|
||||||
import ac.mdiq.podcini.net.utils.UrlChecker.prepareUrl
|
import ac.mdiq.podcini.net.utils.UrlChecker.prepareUrl
|
||||||
import ac.mdiq.podcini.storage.model.Feed
|
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
||||||
|
import ac.mdiq.podcini.storage.model.Feed
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.showStackTrace
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
@ -27,6 +26,7 @@ class DownloadRequest private constructor(
|
||||||
var size: Long = 0
|
var size: Long = 0
|
||||||
private var statusMsg = 0
|
private var statusMsg = 0
|
||||||
|
|
||||||
|
// only used in tests
|
||||||
constructor(destination: String, source: String, title: String, feedfileId: Long,
|
constructor(destination: String, source: String, title: String, feedfileId: Long,
|
||||||
feedfileType: Int, username: String?, password: String?, arguments: Bundle?, initiatedByUser: Boolean)
|
feedfileType: Int, username: String?, password: String?, arguments: Bundle?, initiatedByUser: Boolean)
|
||||||
: this(destination, source, title, feedfileId, feedfileType, null, username, password, false, arguments, initiatedByUser)
|
: this(destination, source, title, feedfileId, feedfileType, null, username, password, false, arguments, initiatedByUser)
|
||||||
|
|
|
@ -3,9 +3,9 @@ package ac.mdiq.podcini.net.download.service
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.net.download.DownloadError
|
import ac.mdiq.podcini.net.download.DownloadError
|
||||||
import ac.mdiq.podcini.net.download.service.DownloadRequestCreator.create
|
import ac.mdiq.podcini.net.download.service.DownloadRequestCreator.create
|
||||||
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
|
|
||||||
import ac.mdiq.podcini.net.utils.NetworkUtils.isAllowMobileEpisodeDownload
|
import ac.mdiq.podcini.net.utils.NetworkUtils.isAllowMobileEpisodeDownload
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
|
@ -21,10 +21,10 @@ import ac.mdiq.podcini.storage.utils.ChapterUtils
|
||||||
import ac.mdiq.podcini.storage.utils.MediaMetadataRetrieverCompat
|
import ac.mdiq.podcini.storage.utils.MediaMetadataRetrieverCompat
|
||||||
import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
|
import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
|
||||||
import ac.mdiq.podcini.ui.utils.NotificationUtils
|
import ac.mdiq.podcini.ui.utils.NotificationUtils
|
||||||
import ac.mdiq.podcini.util.Logd
|
|
||||||
import ac.mdiq.podcini.util.config.ClientConfigurator
|
|
||||||
import ac.mdiq.podcini.util.EventFlow
|
import ac.mdiq.podcini.util.EventFlow
|
||||||
import ac.mdiq.podcini.util.FlowEvent
|
import ac.mdiq.podcini.util.FlowEvent
|
||||||
|
import ac.mdiq.podcini.util.Logd
|
||||||
|
import ac.mdiq.podcini.util.config.ClientConfigurator
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
@ -328,7 +328,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
|
||||||
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(item))
|
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(item))
|
||||||
// TODO: should use different event?
|
// TODO: should use different event?
|
||||||
if (broadcastUnreadStateUpdate) EventFlow.postEvent(FlowEvent.EpisodePlayedEvent(item))
|
if (broadcastUnreadStateUpdate) EventFlow.postEvent(FlowEvent.EpisodePlayedEvent(item))
|
||||||
if (needSynch()) {
|
if (isProviderConnected) {
|
||||||
Logd(TAG, "enqueue synch")
|
Logd(TAG, "enqueue synch")
|
||||||
val action = EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD).currentTimestamp().build()
|
val action = EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD).currentTimestamp().build()
|
||||||
SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
|
SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
|
||||||
|
|
|
@ -3,8 +3,8 @@ package ac.mdiq.podcini.net.feed
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.net.download.DownloadError
|
import ac.mdiq.podcini.net.download.DownloadError
|
||||||
import ac.mdiq.podcini.net.download.service.DefaultDownloaderFactory
|
import ac.mdiq.podcini.net.download.service.DefaultDownloaderFactory
|
||||||
import ac.mdiq.podcini.net.download.service.DownloadRequestCreator.create
|
|
||||||
import ac.mdiq.podcini.net.download.service.DownloadRequest
|
import ac.mdiq.podcini.net.download.service.DownloadRequest
|
||||||
|
import ac.mdiq.podcini.net.download.service.DownloadRequestCreator.create
|
||||||
import ac.mdiq.podcini.net.feed.parser.FeedHandler
|
import ac.mdiq.podcini.net.feed.parser.FeedHandler
|
||||||
import ac.mdiq.podcini.net.feed.parser.FeedHandler.FeedHandlerResult
|
import ac.mdiq.podcini.net.feed.parser.FeedHandler.FeedHandlerResult
|
||||||
import ac.mdiq.podcini.net.utils.NetworkUtils.isAllowMobileFeedRefresh
|
import ac.mdiq.podcini.net.utils.NetworkUtils.isAllowMobileFeedRefresh
|
||||||
|
@ -46,8 +46,6 @@ import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import androidx.work.Constraints.Builder
|
import androidx.work.Constraints.Builder
|
||||||
import com.annimon.stream.Collectors
|
|
||||||
import com.annimon.stream.Stream
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.common.util.concurrent.Futures
|
import com.google.common.util.concurrent.Futures
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
|
@ -163,7 +161,7 @@ object FeedUpdateManager {
|
||||||
feedsToUpdate.shuffle() // If the worker gets cancelled early, every feed has a chance to be updated
|
feedsToUpdate.shuffle() // If the worker gets cancelled early, every feed has a chance to be updated
|
||||||
} else {
|
} else {
|
||||||
val feed = Feeds.getFeed(feedId) ?: return Result.success()
|
val feed = Feeds.getFeed(feedId) ?: return Result.success()
|
||||||
Logd(TAG, "doWork feed.downloadUrl: ${feed.downloadUrl}")
|
Logd(TAG, "doWork updating single feed: ${feed.title} ${feed.downloadUrl}")
|
||||||
if (!feed.isLocalFeed) allAreLocal = false
|
if (!feed.isLocalFeed) allAreLocal = false
|
||||||
feedsToUpdate = mutableListOf(feed)
|
feedsToUpdate = mutableListOf(feed)
|
||||||
// feedsToUpdate.add(feed) // Needs to be updatable, so no singletonList
|
// feedsToUpdate.add(feed) // Needs to be updatable, so no singletonList
|
||||||
|
@ -181,14 +179,13 @@ object FeedUpdateManager {
|
||||||
feedsToUpdate.clear()
|
feedsToUpdate.clear()
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
private fun createNotification(toUpdate: List<Feed?>?): Notification {
|
private fun createNotification(titles: List<String>?): Notification {
|
||||||
val context = applicationContext
|
val context = applicationContext
|
||||||
var contentText = ""
|
var contentText = ""
|
||||||
var bigText: String? = ""
|
var bigText: String? = ""
|
||||||
if (toUpdate != null) {
|
if (titles != null) {
|
||||||
contentText = context.resources.getQuantityString(R.plurals.downloads_left,
|
contentText = context.resources.getQuantityString(R.plurals.downloads_left, titles.size, titles.size)
|
||||||
toUpdate.size, toUpdate.size)
|
bigText = titles.map { "• $it" }.joinToString("\n")
|
||||||
bigText = Stream.of(toUpdate).map { feed: Feed? -> "• " + feed!!.title }.collect(Collectors.joining("\n"))
|
|
||||||
}
|
}
|
||||||
return NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID.downloading.name)
|
return NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID.downloading.name)
|
||||||
.setContentTitle(context.getString(R.string.download_notification_title_feeds))
|
.setContentTitle(context.getString(R.string.download_notification_title_feeds))
|
||||||
|
@ -199,6 +196,7 @@ object FeedUpdateManager {
|
||||||
.addAction(R.drawable.ic_cancel, context.getString(R.string.cancel_label), WorkManager.getInstance(context).createCancelPendingIntent(id))
|
.addAction(R.drawable.ic_cancel, context.getString(R.string.cancel_label), WorkManager.getInstance(context).createCancelPendingIntent(id))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
|
override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
|
||||||
return Futures.immediateFuture(ForegroundInfo(R.id.notification_updating_feeds, createNotification(null)))
|
return Futures.immediateFuture(ForegroundInfo(R.id.notification_updating_feeds, createNotification(null)))
|
||||||
}
|
}
|
||||||
|
@ -218,10 +216,11 @@ object FeedUpdateManager {
|
||||||
// Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
// Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val titles = feedsToUpdate.map { it.title ?: "No title" }.toMutableList()
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < feedsToUpdate.size) {
|
while (i < feedsToUpdate.size) {
|
||||||
if (isStopped) return
|
if (isStopped) return
|
||||||
notificationManager.notify(R.id.notification_updating_feeds, createNotification(feedsToUpdate))
|
notificationManager.notify(R.id.notification_updating_feeds, createNotification(titles))
|
||||||
val feed = unmanaged(feedsToUpdate[i++])
|
val feed = unmanaged(feedsToUpdate[i++])
|
||||||
try {
|
try {
|
||||||
Logd(TAG, "updating local feed? ${feed.isLocalFeed} ${feed.title}")
|
Logd(TAG, "updating local feed? ${feed.isLocalFeed} ${feed.title}")
|
||||||
|
@ -236,7 +235,7 @@ object FeedUpdateManager {
|
||||||
val status = DownloadResult(feed.id, feed.title?:"", DownloadError.ERROR_IO_ERROR, false, e.message?:"")
|
val status = DownloadResult(feed.id, feed.title?:"", DownloadError.ERROR_IO_ERROR, false, e.message?:"")
|
||||||
LogsAndStats.addDownloadStatus(status)
|
LogsAndStats.addDownloadStatus(status)
|
||||||
}
|
}
|
||||||
// toUpdate.removeAt(0)
|
titles.removeAt(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun refreshYoutubeFeed(feed: Feed) {
|
private fun refreshYoutubeFeed(feed: Feed) {
|
||||||
|
@ -283,23 +282,23 @@ object FeedUpdateManager {
|
||||||
LogsAndStats.addDownloadStatus(downloader.result)
|
LogsAndStats.addDownloadStatus(downloader.result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val feedSyncTask = FeedSyncTask(applicationContext, request)
|
val feedUpdateTask = FeedUpdateTask(applicationContext, request)
|
||||||
val success = feedSyncTask.run()
|
val success = feedUpdateTask.run()
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Logd(TAG, "update failed: unsuccessful")
|
Logd(TAG, "update failed: unsuccessful")
|
||||||
Feeds.persistFeedLastUpdateFailed(feed, true)
|
Feeds.persistFeedLastUpdateFailed(feed, true)
|
||||||
LogsAndStats.addDownloadStatus(feedSyncTask.downloadStatus)
|
LogsAndStats.addDownloadStatus(feedUpdateTask.downloadStatus)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (request.feedfileId == null) return // No download logs for new subscriptions
|
if (request.feedfileId == null) return // No download logs for new subscriptions
|
||||||
// we create a 'successful' download log if the feed's last refresh failed
|
// we create a 'successful' download log if the feed's last refresh failed
|
||||||
val log = LogsAndStats.getFeedDownloadLog(request.feedfileId)
|
val log = LogsAndStats.getFeedDownloadLog(request.feedfileId)
|
||||||
if (log.isNotEmpty() && !log[0].isSuccessful) LogsAndStats.addDownloadStatus(feedSyncTask.downloadStatus)
|
if (log.isNotEmpty() && !log[0].isSuccessful) LogsAndStats.addDownloadStatus(feedUpdateTask.downloadStatus)
|
||||||
if (!request.source.isNullOrEmpty()) {
|
if (!request.source.isNullOrEmpty()) {
|
||||||
when {
|
when {
|
||||||
!downloader.permanentRedirectUrl.isNullOrEmpty() -> Feeds.updateFeedDownloadURL(request.source, downloader.permanentRedirectUrl!!)
|
!downloader.permanentRedirectUrl.isNullOrEmpty() -> Feeds.updateFeedDownloadURL(request.source, downloader.permanentRedirectUrl!!)
|
||||||
feedSyncTask.redirectUrl.isNotEmpty() && feedSyncTask.redirectUrl != request.source ->
|
feedUpdateTask.redirectUrl.isNotEmpty() && feedUpdateTask.redirectUrl != request.source ->
|
||||||
Feeds.updateFeedDownloadURL(request.source, feedSyncTask.redirectUrl)
|
Feeds.updateFeedDownloadURL(request.source, feedUpdateTask.redirectUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,7 +392,7 @@ object FeedUpdateManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FeedSyncTask(private val context: Context, request: DownloadRequest) {
|
class FeedUpdateTask(private val context: Context, request: DownloadRequest) {
|
||||||
private val task = FeedParserTask(request)
|
private val task = FeedParserTask(request)
|
||||||
private var feedHandlerResult: FeedHandlerResult? = null
|
private var feedHandlerResult: FeedHandlerResult? = null
|
||||||
val downloadStatus: DownloadResult
|
val downloadStatus: DownloadResult
|
||||||
|
|
|
@ -11,10 +11,6 @@ object SynchronizationQueueSink {
|
||||||
// To avoid a dependency loop of every class to SyncService, and from SyncService back to every class.
|
// To avoid a dependency loop of every class to SyncService, and from SyncService back to every class.
|
||||||
private var serviceStarterImpl = Runnable {}
|
private var serviceStarterImpl = Runnable {}
|
||||||
|
|
||||||
fun needSynch() : Boolean {
|
|
||||||
return isProviderConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setServiceStarterImpl(serviceStarter: Runnable) {
|
fun setServiceStarterImpl(serviceStarter: Runnable) {
|
||||||
serviceStarterImpl = serviceStarter
|
serviceStarterImpl = serviceStarter
|
||||||
}
|
}
|
||||||
|
@ -34,7 +30,6 @@ object SynchronizationQueueSink {
|
||||||
|
|
||||||
fun enqueueFeedAddedIfSyncActive(context: Context, downloadUrl: String) {
|
fun enqueueFeedAddedIfSyncActive(context: Context, downloadUrl: String) {
|
||||||
if (!isProviderConnected) return
|
if (!isProviderConnected) return
|
||||||
|
|
||||||
LockingAsyncExecutor.executeLockedAsync {
|
LockingAsyncExecutor.executeLockedAsync {
|
||||||
SynchronizationQueueStorage(context).enqueueFeedAdded(downloadUrl)
|
SynchronizationQueueStorage(context).enqueueFeedAdded(downloadUrl)
|
||||||
syncNow()
|
syncNow()
|
||||||
|
@ -43,7 +38,6 @@ object SynchronizationQueueSink {
|
||||||
|
|
||||||
fun enqueueFeedRemovedIfSyncActive(context: Context, downloadUrl: String) {
|
fun enqueueFeedRemovedIfSyncActive(context: Context, downloadUrl: String) {
|
||||||
if (!isProviderConnected) return
|
if (!isProviderConnected) return
|
||||||
|
|
||||||
LockingAsyncExecutor.executeLockedAsync {
|
LockingAsyncExecutor.executeLockedAsync {
|
||||||
SynchronizationQueueStorage(context).enqueueFeedRemoved(downloadUrl)
|
SynchronizationQueueStorage(context).enqueueFeedRemoved(downloadUrl)
|
||||||
syncNow()
|
syncNow()
|
||||||
|
@ -52,7 +46,6 @@ object SynchronizationQueueSink {
|
||||||
|
|
||||||
fun enqueueEpisodeActionIfSyncActive(context: Context, action: EpisodeAction) {
|
fun enqueueEpisodeActionIfSyncActive(context: Context, action: EpisodeAction) {
|
||||||
if (!isProviderConnected) return
|
if (!isProviderConnected) return
|
||||||
|
|
||||||
LockingAsyncExecutor.executeLockedAsync {
|
LockingAsyncExecutor.executeLockedAsync {
|
||||||
SynchronizationQueueStorage(context).enqueueEpisodeAction(action)
|
SynchronizationQueueStorage(context).enqueueEpisodeAction(action)
|
||||||
syncNow()
|
syncNow()
|
||||||
|
@ -61,7 +54,6 @@ object SynchronizationQueueSink {
|
||||||
|
|
||||||
fun enqueueEpisodePlayedIfSyncActive(context: Context, media: EpisodeMedia, completed: Boolean) {
|
fun enqueueEpisodePlayedIfSyncActive(context: Context, media: EpisodeMedia, completed: Boolean) {
|
||||||
if (!isProviderConnected) return
|
if (!isProviderConnected) return
|
||||||
|
|
||||||
val item_ = media.episodeOrFetch()
|
val item_ = media.episodeOrFetch()
|
||||||
if (item_?.feed?.isLocalFeed == true) return
|
if (item_?.feed?.isLocalFeed == true) return
|
||||||
if (media.startPosition < 0 || (!completed && media.startPosition >= media.getPosition())) return
|
if (media.startPosition < 0 || (!completed && media.startPosition >= media.getPosition())) return
|
||||||
|
|
|
@ -3,9 +3,9 @@ package ac.mdiq.podcini.storage.database
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.net.download.service.DownloadServiceInterface
|
import ac.mdiq.podcini.net.download.service.DownloadServiceInterface
|
||||||
import ac.mdiq.podcini.net.feed.LocalFeedUpdater.updateFeed
|
import ac.mdiq.podcini.net.feed.LocalFeedUpdater.updateFeed
|
||||||
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
|
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre.curState
|
import ac.mdiq.podcini.playback.base.InTheatre.curState
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre.writeNoMediaPlaying
|
import ac.mdiq.podcini.playback.base.InTheatre.writeNoMediaPlaying
|
||||||
|
@ -164,7 +164,7 @@ object Episodes {
|
||||||
// Do full update of this feed to get rid of the episode
|
// Do full update of this feed to get rid of the episode
|
||||||
if (episode.feed != null) updateFeed(episode.feed!!, context.applicationContext, null)
|
if (episode.feed != null) updateFeed(episode.feed!!, context.applicationContext, null)
|
||||||
} else {
|
} else {
|
||||||
if (needSynch()) {
|
if (isProviderConnected) {
|
||||||
// Gpodder: queue delete action for synchronization
|
// Gpodder: queue delete action for synchronization
|
||||||
val action = EpisodeAction.Builder(episode, EpisodeAction.DELETE).currentTimestamp().build()
|
val action = EpisodeAction.Builder(episode, EpisodeAction.DELETE).currentTimestamp().build()
|
||||||
SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
|
SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package ac.mdiq.podcini.storage.database
|
package ac.mdiq.podcini.storage.database
|
||||||
|
|
||||||
import ac.mdiq.podcini.net.download.DownloadError
|
import ac.mdiq.podcini.net.download.DownloadError
|
||||||
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
|
|
||||||
import ac.mdiq.podcini.playback.base.VideoMode
|
import ac.mdiq.podcini.playback.base.VideoMode
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDelete
|
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDelete
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDeleteLocal
|
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDeleteLocal
|
||||||
|
@ -196,142 +196,6 @@ object Feeds {
|
||||||
* I.e. episodes are removed from the database if they are not in this episode list.
|
* I.e. episodes are removed from the database if they are not in this episode list.
|
||||||
* @return The updated Feed from the database if it already existed, or the new Feed from the parameters otherwise.
|
* @return The updated Feed from the database if it already existed, or the new Feed from the parameters otherwise.
|
||||||
*/
|
*/
|
||||||
// @Synchronized
|
|
||||||
// fun updateFeed0(context: Context, newFeed: Feed, removeUnlistedItems: Boolean): Feed? {
|
|
||||||
// Logd(TAG, "updateFeed called")
|
|
||||||
// var resultFeed: Feed?
|
|
||||||
// val unlistedItems: MutableList<Episode> = ArrayList()
|
|
||||||
//
|
|
||||||
// // Look up feed in the feedslist
|
|
||||||
// val savedFeed = searchFeedByIdentifyingValueOrID(newFeed, true)
|
|
||||||
// if (savedFeed == null) {
|
|
||||||
// Logd(TAG, "Found no existing Feed with title ${newFeed.title}. Adding as new one.")
|
|
||||||
// Logd(TAG, "newFeed.episodes: ${newFeed.episodes.size}")
|
|
||||||
// resultFeed = newFeed
|
|
||||||
// try {
|
|
||||||
// addNewFeedsSync(context, newFeed)
|
|
||||||
// // Update with default values that are set in database
|
|
||||||
// resultFeed = searchFeedByIdentifyingValueOrID(newFeed)
|
|
||||||
// if (removeUnlistedItems) runBlocking { deleteEpisodes(context, unlistedItems).join() }
|
|
||||||
// } catch (e: InterruptedException) { e.printStackTrace()
|
|
||||||
// } catch (e: ExecutionException) { e.printStackTrace() }
|
|
||||||
// return resultFeed
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Logd(TAG, "Feed with title " + newFeed.title + " already exists. Syncing new with existing one.")
|
|
||||||
// newFeed.episodes.sortWith(EpisodePubdateComparator())
|
|
||||||
// if (newFeed.pageNr == savedFeed.pageNr) {
|
|
||||||
// if (savedFeed.compareWithOther(newFeed)) {
|
|
||||||
// Logd(TAG, "Feed has updated attribute values. Updating old feed's attributes")
|
|
||||||
// savedFeed.updateFromOther(newFeed)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Logd(TAG, "New feed has a higher page number.")
|
|
||||||
// savedFeed.nextPageLink = newFeed.nextPageLink
|
|
||||||
// }
|
|
||||||
//// appears not useful
|
|
||||||
//// if (savedFeed.preferences != null && savedFeed.preferences!!.compareWithOther(newFeed.preferences)) {
|
|
||||||
//// Logd(TAG, "Feed has updated preferences. Updating old feed's preferences")
|
|
||||||
//// savedFeed.preferences!!.updateFromOther(newFeed.preferences)
|
|
||||||
//// }
|
|
||||||
// val priorMostRecent = savedFeed.mostRecentItem
|
|
||||||
// val priorMostRecentDate: Date? = priorMostRecent?.getPubDate()
|
|
||||||
// var idLong = Feed.newId()
|
|
||||||
// // Look for new or updated Items
|
|
||||||
// for (idx in newFeed.episodes.indices) {
|
|
||||||
// val episode = newFeed.episodes[idx]
|
|
||||||
// val possibleDuplicate = EpisodeAssistant.searchEpisodeGuessDuplicate(newFeed.episodes, episode)
|
|
||||||
// if (!newFeed.isLocalFeed && possibleDuplicate != null && episode !== possibleDuplicate) {
|
|
||||||
// // Canonical episode is the first one returned (usually oldest)
|
|
||||||
// addDownloadStatus(DownloadResult(savedFeed.id, episode.title ?: "", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
|
||||||
// """
|
|
||||||
// The podcast host appears to have added the same episode twice. Podcini still refreshed the feed and attempted to repair it.
|
|
||||||
//
|
|
||||||
// Original episode:
|
|
||||||
// ${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
|
||||||
//
|
|
||||||
// Second episode that is also in the feed:
|
|
||||||
// ${EpisodeAssistant.duplicateEpisodeDetails(possibleDuplicate)}
|
|
||||||
// """.trimIndent()))
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// var oldItem = searchEpisodeByIdentifyingValue(savedFeed.episodes, episode)
|
|
||||||
// if (!newFeed.isLocalFeed && oldItem == null) {
|
|
||||||
// oldItem = EpisodeAssistant.searchEpisodeGuessDuplicate(savedFeed.episodes, episode)
|
|
||||||
// if (oldItem != null) {
|
|
||||||
// Logd(TAG, "Repaired duplicate: $oldItem, $episode")
|
|
||||||
// addDownloadStatus(DownloadResult(savedFeed.id,
|
|
||||||
// episode.title ?: "", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
|
||||||
// """
|
|
||||||
// The podcast host changed the ID of an existing episode instead of just updating the episode itself. Podcini still refreshed the feed and attempted to repair it.
|
|
||||||
//
|
|
||||||
// Original episode:
|
|
||||||
// ${EpisodeAssistant.duplicateEpisodeDetails(oldItem)}
|
|
||||||
//
|
|
||||||
// Now the feed contains:
|
|
||||||
// ${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
|
||||||
// """.trimIndent()))
|
|
||||||
// oldItem.identifier = episode.identifier
|
|
||||||
// if (needSynch() && oldItem.isPlayed() && oldItem.media != null) {
|
|
||||||
// val durs = oldItem.media!!.getDuration() / 1000
|
|
||||||
// val action = EpisodeAction.Builder(oldItem, EpisodeAction.PLAY)
|
|
||||||
// .currentTimestamp()
|
|
||||||
// .started(durs)
|
|
||||||
// .position(durs)
|
|
||||||
// .total(durs)
|
|
||||||
// .build()
|
|
||||||
// SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (oldItem != null) oldItem.updateFromOther(episode)
|
|
||||||
// else {
|
|
||||||
// Logd(TAG, "Found new episode: ${episode.title}")
|
|
||||||
// episode.feed = savedFeed
|
|
||||||
// episode.id = idLong++
|
|
||||||
// episode.feedId = savedFeed.id
|
|
||||||
// if (episode.media != null) {
|
|
||||||
// episode.media!!.id = episode.id
|
|
||||||
// if (!savedFeed.hasVideoMedia && episode.media!!.getMediaType() == MediaType.VIDEO) savedFeed.hasVideoMedia = true
|
|
||||||
// }
|
|
||||||
// if (idx >= savedFeed.episodes.size) savedFeed.episodes.add(episode)
|
|
||||||
// else savedFeed.episodes.add(idx, episode)
|
|
||||||
//
|
|
||||||
// val pubDate = episode.getPubDate()
|
|
||||||
// if (pubDate == null || priorMostRecentDate == null || priorMostRecentDate.before(pubDate) || priorMostRecentDate == pubDate) {
|
|
||||||
// Logd(TAG, "Marking episode published on $pubDate new, prior most recent date = $priorMostRecentDate")
|
|
||||||
// episode.setNew()
|
|
||||||
// if (savedFeed.preferences?.autoAddNewToQueue == true) {
|
|
||||||
// val q = savedFeed.preferences?.queue
|
|
||||||
// if (q != null) runOnIOScope { addToQueueSync(false, episode, q) }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // identify episodes to be removed
|
|
||||||
// if (removeUnlistedItems) {
|
|
||||||
// val it = savedFeed.episodes.toMutableList().iterator()
|
|
||||||
// while (it.hasNext()) {
|
|
||||||
// val feedItem = it.next()
|
|
||||||
// if (searchEpisodeByIdentifyingValue(newFeed.episodes, feedItem) == null) {
|
|
||||||
// unlistedItems.add(feedItem)
|
|
||||||
// it.remove()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // update attributes
|
|
||||||
// savedFeed.lastUpdate = newFeed.lastUpdate
|
|
||||||
// savedFeed.type = newFeed.type
|
|
||||||
// savedFeed.lastUpdateFailed = false
|
|
||||||
// resultFeed = savedFeed
|
|
||||||
// try {
|
|
||||||
// upsertBlk(savedFeed) {}
|
|
||||||
// if (removeUnlistedItems) runBlocking { deleteEpisodes(context, unlistedItems).join() }
|
|
||||||
// } catch (e: InterruptedException) { e.printStackTrace()
|
|
||||||
// } catch (e: ExecutionException) { e.printStackTrace() }
|
|
||||||
// return resultFeed
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun updateFeed(context: Context, newFeed: Feed, removeUnlistedItems: Boolean): Feed? {
|
fun updateFeed(context: Context, newFeed: Feed, removeUnlistedItems: Boolean): Feed? {
|
||||||
Logd(TAG, "updateFeed called")
|
Logd(TAG, "updateFeed called")
|
||||||
|
@ -365,11 +229,6 @@ object Feeds {
|
||||||
Logd(TAG, "New feed has a higher page number.")
|
Logd(TAG, "New feed has a higher page number.")
|
||||||
savedFeed.nextPageLink = newFeed.nextPageLink
|
savedFeed.nextPageLink = newFeed.nextPageLink
|
||||||
}
|
}
|
||||||
// appears not useful
|
|
||||||
// if (savedFeed.preferences != null && savedFeed.preferences!!.compareWithOther(newFeed.preferences)) {
|
|
||||||
// Logd(TAG, "Feed has updated preferences. Updating old feed's preferences")
|
|
||||||
// savedFeed.preferences!!.updateFromOther(newFeed.preferences)
|
|
||||||
// }
|
|
||||||
val priorMostRecent = savedFeed.mostRecentItem
|
val priorMostRecent = savedFeed.mostRecentItem
|
||||||
val priorMostRecentDate: Date? = priorMostRecent?.getPubDate()
|
val priorMostRecentDate: Date? = priorMostRecent?.getPubDate()
|
||||||
var idLong = Feed.newId()
|
var idLong = Feed.newId()
|
||||||
|
@ -381,21 +240,6 @@ object Feeds {
|
||||||
// Look for new or updated Items
|
// Look for new or updated Items
|
||||||
for (idx in newFeed.episodes.indices) {
|
for (idx in newFeed.episodes.indices) {
|
||||||
val episode = newFeed.episodes[idx]
|
val episode = newFeed.episodes[idx]
|
||||||
// val possibleDuplicate = EpisodeAssistant.searchEpisodeGuessDuplicate(newFeed.episodes, episode)
|
|
||||||
// if (!newFeed.isLocalFeed && possibleDuplicate != null && episode !== possibleDuplicate) {
|
|
||||||
// // Canonical episode is the first one returned (usually oldest)
|
|
||||||
// addDownloadStatus(DownloadResult(savedFeed.id, episode.title ?: "", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
|
||||||
// """
|
|
||||||
// The podcast host appears to have added the same episode twice. Podcini still refreshed the feed and attempted to repair it.
|
|
||||||
//
|
|
||||||
// Original episode:
|
|
||||||
// ${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
|
||||||
//
|
|
||||||
// Second episode that is also in the feed:
|
|
||||||
// ${EpisodeAssistant.duplicateEpisodeDetails(possibleDuplicate)}
|
|
||||||
// """.trimIndent()))
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
var oldItem = savedFeedAssistant.searchEpisodeByIdentifyingValue(episode)
|
var oldItem = savedFeedAssistant.searchEpisodeByIdentifyingValue(episode)
|
||||||
if (!newFeed.isLocalFeed && oldItem == null) {
|
if (!newFeed.isLocalFeed && oldItem == null) {
|
||||||
oldItem = savedFeedAssistant.searchEpisodeGuessDuplicate(episode)
|
oldItem = savedFeedAssistant.searchEpisodeGuessDuplicate(episode)
|
||||||
|
@ -413,7 +257,7 @@ object Feeds {
|
||||||
${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
||||||
""".trimIndent()))
|
""".trimIndent()))
|
||||||
oldItem.identifier = episode.identifier
|
oldItem.identifier = episode.identifier
|
||||||
if (needSynch() && oldItem.isPlayed() && oldItem.media != null) {
|
if (isProviderConnected && oldItem.isPlayed() && oldItem.media != null) {
|
||||||
val durs = oldItem.media!!.getDuration() / 1000
|
val durs = oldItem.media!!.getDuration() / 1000
|
||||||
val action = EpisodeAction.Builder(oldItem, EpisodeAction.PLAY)
|
val action = EpisodeAction.Builder(oldItem, EpisodeAction.PLAY)
|
||||||
.currentTimestamp()
|
.currentTimestamp()
|
||||||
|
@ -616,16 +460,22 @@ object Feeds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FeedAssistant(val feed: Feed, val feedId: Long = 0L) {
|
// savedFeedId == 0L means saved feed
|
||||||
|
class FeedAssistant(val feed: Feed, val savedFeedId: Long = 0L) {
|
||||||
val map = mutableMapOf<String, Episode>()
|
val map = mutableMapOf<String, Episode>()
|
||||||
|
val tag: String = if (savedFeedId == 0L) "Saved feed" else "New feed"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for (e in feed.episodes) {
|
val iterator = feed.episodes.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val e = iterator.next()
|
||||||
if (!e.identifier.isNullOrEmpty()) {
|
if (!e.identifier.isNullOrEmpty()) {
|
||||||
if (map.containsKey(e.identifier!!)) {
|
if (map.containsKey(e.identifier!!)) {
|
||||||
// TODO: add addDownloadStatus
|
Logd(TAG, "FeedAssistant init $tag identifier duplicate: ${e.identifier} ${e.title}")
|
||||||
Logd(TAG, "FeedAssistant init identifier duplicate: ${e.identifier} ${e.title}")
|
if (savedFeedId > 0L) {
|
||||||
addDownloadStatus(e, map[e.identifier!!]!!)
|
addDownloadStatus(e, map[e.identifier!!]!!)
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
map[e.identifier!!] = e
|
map[e.identifier!!] = e
|
||||||
|
@ -633,9 +483,11 @@ object Feeds {
|
||||||
val idv = e.identifyingValue
|
val idv = e.identifyingValue
|
||||||
if (idv != e.identifier && !idv.isNullOrEmpty()) {
|
if (idv != e.identifier && !idv.isNullOrEmpty()) {
|
||||||
if (map.containsKey(idv)) {
|
if (map.containsKey(idv)) {
|
||||||
// TODO: add addDownloadStatus
|
Logd(TAG, "FeedAssistant init $tag identifyingValue duplicate: $idv ${e.title}")
|
||||||
Logd(TAG, "FeedAssistant init identifyingValue duplicate: $idv ${e.title}")
|
if (savedFeedId > 0L) {
|
||||||
addDownloadStatus(e, map[idv]!!)
|
addDownloadStatus(e, map[idv]!!)
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
map[idv] = e
|
map[idv] = e
|
||||||
|
@ -643,9 +495,11 @@ object Feeds {
|
||||||
val url = e.media?.getStreamUrl()
|
val url = e.media?.getStreamUrl()
|
||||||
if (url != idv && !url.isNullOrEmpty()) {
|
if (url != idv && !url.isNullOrEmpty()) {
|
||||||
if (map.containsKey(url)) {
|
if (map.containsKey(url)) {
|
||||||
// TODO: add addDownloadStatus
|
Logd(TAG, "FeedAssistant init $tag url duplicate: $url ${e.title}")
|
||||||
Logd(TAG, "FeedAssistant init url duplicate: $url ${e.title}")
|
if (savedFeedId > 0L) {
|
||||||
addDownloadStatus(e, map[url]!!)
|
addDownloadStatus(e, map[url]!!)
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
map[url] = e
|
map[url] = e
|
||||||
|
@ -653,14 +507,16 @@ object Feeds {
|
||||||
val title = canonicalizeTitle(e.title)
|
val title = canonicalizeTitle(e.title)
|
||||||
if (title != idv && title.isNotEmpty()) {
|
if (title != idv && title.isNotEmpty()) {
|
||||||
if (map.containsKey(title)) {
|
if (map.containsKey(title)) {
|
||||||
// TODO: add addDownloadStatus
|
|
||||||
val episode = map[title]
|
val episode = map[title]
|
||||||
if (episode != null) {
|
if (episode != null) {
|
||||||
val media1 = episode.media
|
val media1 = episode.media
|
||||||
val media2 = e.media
|
val media2 = e.media
|
||||||
if (media1 != null && media2 != null && datesLookSimilar(episode, e) && durationsLookSimilar(media1, media2) && mimeTypeLooksSimilar(media1, media2)) {
|
if (media1 != null && media2 != null && datesLookSimilar(episode, e) && durationsLookSimilar(media1, media2) && mimeTypeLooksSimilar(media1, media2)) {
|
||||||
Logd(TAG, "FeedAssistant init title duplicate: $title ${e.title}")
|
Logd(TAG, "FeedAssistant init $tag title duplicate: $title ${e.title}")
|
||||||
addDownloadStatus(e, episode)
|
if (savedFeedId > 0L) {
|
||||||
|
addDownloadStatus(e, episode)
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -670,25 +526,21 @@ object Feeds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDownloadStatus(episode: Episode, possibleDuplicate: Episode) {
|
private fun addDownloadStatus(episode: Episode, possibleDuplicate: Episode) {
|
||||||
val feedId_ = if (feedId > 0) feedId else feed.id
|
addDownloadStatus(DownloadResult(savedFeedId, episode.title ?: "", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
||||||
addDownloadStatus(DownloadResult(feedId_, episode.title ?: "", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
|
||||||
"""
|
"""
|
||||||
The podcast host appears to have added the same episode twice. Podcini still refreshed the feed and attempted to repair it.
|
The podcast host appears to have added the same episode twice. Podcini still refreshed the feed and attempted to repair it.
|
||||||
|
|
||||||
Original episode:
|
Original episode:
|
||||||
${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
${EpisodeAssistant.duplicateEpisodeDetails(episode)}
|
||||||
|
|
||||||
Second episode that is also in the feed:
|
Second episode that is also in the feed:
|
||||||
${EpisodeAssistant.duplicateEpisodeDetails(possibleDuplicate)}
|
${EpisodeAssistant.duplicateEpisodeDetails(possibleDuplicate)}
|
||||||
""".trimIndent()))
|
""".trimIndent()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchEpisodeByIdentifyingValue(item: Episode): Episode? {
|
fun searchEpisodeByIdentifyingValue(item: Episode): Episode? {
|
||||||
return map[item.identifyingValue]
|
return map[item.identifyingValue]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchEpisodeGuessDuplicate(item: Episode): Episode? {
|
fun searchEpisodeGuessDuplicate(item: Episode): Episode? {
|
||||||
var episode = map[item.identifier]
|
var episode = map[item.identifier]
|
||||||
if (episode != null) return episode
|
if (episode != null) return episode
|
||||||
|
@ -709,7 +561,6 @@ object Feeds {
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
map.clear()
|
map.clear()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
||||||
import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
|
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre
|
import ac.mdiq.podcini.playback.base.InTheatre
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre.curState
|
import ac.mdiq.podcini.playback.base.InTheatre.curState
|
||||||
|
@ -145,7 +144,7 @@ object EpisodeMenuHandler {
|
||||||
if (selectedItem.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) {
|
if (selectedItem.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) {
|
||||||
val media: EpisodeMedia? = selectedItem.media
|
val media: EpisodeMedia? = selectedItem.media
|
||||||
// not all items have media, Gpodder only cares about those that do
|
// not all items have media, Gpodder only cares about those that do
|
||||||
if (needSynch() && media != null) {
|
if (isProviderConnected && media != null) {
|
||||||
val actionPlay: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.PLAY)
|
val actionPlay: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.PLAY)
|
||||||
.currentTimestamp()
|
.currentTimestamp()
|
||||||
.started(media.getDuration() / 1000)
|
.started(media.getDuration() / 1000)
|
||||||
|
@ -159,7 +158,7 @@ object EpisodeMenuHandler {
|
||||||
R.id.mark_unread_item -> {
|
R.id.mark_unread_item -> {
|
||||||
// selectedItem.setPlayed(false)
|
// selectedItem.setPlayed(false)
|
||||||
setPlayState(Episode.PlayState.UNPLAYED.code, false, selectedItem)
|
setPlayState(Episode.PlayState.UNPLAYED.code, false, selectedItem)
|
||||||
if (needSynch() && selectedItem.feed?.isLocalFeed != true && selectedItem.media != null) {
|
if (isProviderConnected && selectedItem.feed?.isLocalFeed != true && selectedItem.media != null) {
|
||||||
val actionNew: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
|
val actionNew: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
|
||||||
.currentTimestamp()
|
.currentTimestamp()
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
# 6.7.1
|
||||||
|
|
||||||
|
* ensured duplicate episodes are removed from secondary checking during refresh
|
||||||
|
* refresh progress is updated in notification
|
||||||
|
|
||||||
# 6.7.0
|
# 6.7.0
|
||||||
|
|
||||||
* largely improved efficiency of podcasts refresh, no more massive list searches
|
* largely improved efficiency of podcasts refresh, no more massive list searches
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Version 6.7.1:
|
||||||
|
|
||||||
|
* ensured duplicate episodes are removed from secondary checking during refresh
|
||||||
|
* refresh progress is updated in notification
|
Loading…
Reference in New Issue