Skip to content

Commit

Permalink
6.14.2 commit
Browse files Browse the repository at this point in the history
  • Loading branch information
XilinJia committed Nov 18, 2024
1 parent a213284 commit 737577a
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 195 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ An open source podcast instrument, attuned to Puccini ![Puccini](./images/Puccin
[<img src="./images/external/getItf-droid.png" alt="F-Droid" height="50">](https://f-droid.org/packages/ac.mdiq.podcini.R/)
[<img src="./images/external/amazon.png" alt="Amazon" height="40">](https://www.amazon.com/%E8%B4%BE%E8%A5%BF%E6%9E%97-Podcini-R/dp/B0D9WR8P13)

#### The play app of Podcini.R 6.14 allows casting audio-only Youtube media to a Chromecast speaker
#### Podcini.R 6.10 allows creating synthetic podcast and shelving any episdes to any synthetic podcasts
#### Podcini.R 6.5 as a major step forward brings YouTube contents in the app. Channels can be searched, received from share, subscribed. Podcasts, playlists as well as single media from Youtube and YT Music can be shared to Podcini. For more see the Youtube section below or the changelogs
That means finally: [Nessun dorma](https://www.youtube.com/watch?v=cWc7vYjgnTs)
#### For Podcini to show up on car's HUD with Android Auto, please read AnroidAuto.md for instructions.
#### If you need to cast to an external speaker, you should install the "play" apk, not the "free" apk, that's about the difference between the two.
#### Podcini.R requests for permission for unrestricted background activities for uninterrupted background play of a playlist. For more see [this issue](https://github.com/XilinJia/Podcini/issues/88)
#### If you intend to sync through a server, be cautious as it's not well tested with Podcini. Welcome any ideas and contribution on this.
#### If you are migrating from Podcini version 5, please read the migrationTo5.md file for migration 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.

Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ android {
vectorDrawables.useSupportLibrary false
vectorDrawables.generatedDensities = []

versionCode 3020300
versionName "6.14.1"
versionCode 3020301
versionName "6.14.2"

applicationId "ac.mdiq.podcini.R"
def commit = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable

/**
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
* network.
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the network.
*/
abstract class CastEnabledActivity : AppCompatActivity() {
val TAG = this::class.simpleName ?: "Anonymous"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
bufferingUpdateListener = null
}

private fun setAudioStreamType(i: Int) {
val a = exoPlayer!!.audioAttributes
val b = AudioAttributes.Builder()
b.setContentType(i)
b.setFlags(a.flags)
b.setUsage(a.usage)
exoPlayer?.setAudioAttributes(b.build(), true)
}
// private fun setAudioStreamType(i: Int) {
// val a = exoPlayer!!.audioAttributes
// val b = AudioAttributes.Builder()
// b.setContentType(i)
// b.setFlags(a.flags)
// b.setUsage(a.usage)
// exoPlayer?.setAudioAttributes(b.build(), true)
// }

/**
* Starts or prepares playback of the specified Playable object. If another Playable object is already being played, the currently playing
Expand Down Expand Up @@ -208,7 +208,8 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
val metadata = buildMetadata(curMedia!!)
try {
callback.ensureMediaInfoLoaded(curMedia!!)
callback.onMediaChanged(false)
// TODO: test
callback.onMediaChanged(true)
setPlaybackParams(getCurrentPlaybackSpeed(curMedia), isSkipSilence)
CoroutineScope(Dispatchers.IO).launch {
when {
Expand Down Expand Up @@ -484,7 +485,13 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
status = PlayerStatus.STOPPED
return
}
setAudioStreamType(C.AUDIO_CONTENT_TYPE_SPEECH)
val i = (curMedia as? EpisodeMedia)?.episode?.feed?.preferences?.audioType?: C.AUDIO_CONTENT_TYPE_SPEECH
val a = exoPlayer!!.audioAttributes
val b = AudioAttributes.Builder()
b.setContentType(i)
b.setFlags(a.flags)
b.setUsage(a.usage)
exoPlayer?.setAudioAttributes(b.build(), true)
setMediaPlayerListeners()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ abstract class MediaPlayerBase protected constructor(protected val context: Cont
}

@Throws(IllegalArgumentException::class, IllegalStateException::class)
protected fun setDataSource(metadata: MediaMetadata, media: EpisodeMedia) {
protected open fun setDataSource(metadata: MediaMetadata, media: EpisodeMedia) {
Logd(TAG, "setDataSource1 called")
val url = media.getStreamUrl() ?: return
val preferences = media.episodeOrFetch()?.feed?.preferences
Expand Down Expand Up @@ -185,8 +185,7 @@ abstract class MediaPlayerBase protected constructor(protected val context: Cont
private fun setSourceCredentials(user: String?, password: String?) {
if (!user.isNullOrEmpty() && !password.isNullOrEmpty()) {
if (httpDataSourceFactory == null)
httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory)
.setUserAgent(ClientConfig.USER_AGENT)
httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory).setUserAgent(ClientConfig.USER_AGENT)

val requestProperties = HashMap<String, String>()
requestProperties["Authorization"] = HttpCredentialEncoder.encode(user, password, "ISO-8859-1")
Expand All @@ -211,7 +210,7 @@ abstract class MediaPlayerBase protected constructor(protected val context: Cont
* @param preferVideoOnlyStreams if video-only streams should preferred when both video-only streams and normal video streams are available
* @return the sorted list
*/
private fun getSortedStreamVideosList(videoStreams: List<VideoStream>?, videoOnlyStreams: List<VideoStream>?, ascendingOrder: Boolean,
protected fun getSortedStreamVideosList(videoStreams: List<VideoStream>?, videoOnlyStreams: List<VideoStream>?, ascendingOrder: Boolean,
preferVideoOnlyStreams: Boolean): List<VideoStream> {
val videoStreamsOrdered = if (preferVideoOnlyStreams) listOf(videoStreams, videoOnlyStreams) else listOf(videoOnlyStreams, videoStreams)
val allInitialStreams = videoStreamsOrdered.filterNotNull().flatten().toList()
Expand All @@ -228,7 +227,7 @@ abstract class MediaPlayerBase protected constructor(protected val context: Cont
}
}

private fun getFilteredAudioStreams(audioStreams: List<AudioStream>?): List<AudioStream> {
protected fun getFilteredAudioStreams(audioStreams: List<AudioStream>?): List<AudioStream> {
if (audioStreams == null) return listOf()
val collectedStreams = mutableSetOf<AudioStream>()
for (stream in audioStreams) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import ac.mdiq.podcini.util.IntentUtils.sendLocalBroadcast
import ac.mdiq.podcini.util.Logd
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
Expand Down Expand Up @@ -220,8 +221,8 @@ class PlaybackService : MediaLibraryService() {

private val taskManagerCallback: TaskManager.PSTMCallback = object : TaskManager.PSTMCallback {
override fun positionSaverTick() {
Logd(TAG, "positionSaverTick currentPosition: $curPosition, currentPlaybackSpeed: $curSpeed")
if (curPosition != prevPosition) {
// Log.d(TAG, "positionSaverTick currentPosition: $currentPosition, currentPlaybackSpeed: $currentPlaybackSpeed")
if (curMedia != null) EventFlow.postEvent(FlowEvent.PlaybackPositionEvent(curMedia, curPosition, curDuration))
skipEndingIfNecessary()
persistCurrentPosition(true, null, Playable.INVALID_TIME)
Expand Down Expand Up @@ -356,15 +357,6 @@ class PlaybackService : MediaLibraryService() {
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode)) {
Logd(TAG, "onPostPlayback ended: $ended smartMarkAsPlayed: $smartMarkAsPlayed autoSkipped: $autoSkipped skipped: $skipped")
// only mark the item as played if we're not keeping it anyways

// item = setPlayStateSync(PlayState.PLAYED.code, item!!, ended || (skipped && smartMarkAsPlayed), false)
// if (playable is EpisodeMedia && (ended || skipped || playingNext)) {
// item = upsert(item!!) {
// it.media?.playbackCompletionDate = Date()
// }
// EventFlow.postEvent(FlowEvent.HistoryEvent())
// }

if (playable !is EpisodeMedia)
item = setPlayStateSync(PlayState.PLAYED.code, item!!, ended || (skipped && smartMarkAsPlayed), false)
else {
Expand Down Expand Up @@ -784,12 +776,9 @@ class PlaybackService : MediaLibraryService() {
val keycode = intent?.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1) ?: -1
val customAction = intent?.getStringExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION)
val hardwareButton = intent?.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false) == true
val keyEvent: KeyEvent? = if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU)
intent?.getParcelableExtra(EXTRA_KEY_EVENT, KeyEvent::class.java)
else {
@Suppress("DEPRECATION")
intent?.getParcelableExtra(EXTRA_KEY_EVENT)
}
val keyEvent: KeyEvent? = if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) intent?.getParcelableExtra(EXTRA_KEY_EVENT, KeyEvent::class.java)
else intent?.getParcelableExtra(EXTRA_KEY_EVENT)

val playable = curMedia
Log.d(TAG, "onStartCommand flags=$flags startId=$startId keycode=$keycode keyEvent=$keyEvent customAction=$customAction hardwareButton=$hardwareButton action=${intent?.action.toString()} ${playable?.getEpisodeTitle()}")
if (keycode == -1 && playable == null && customAction == null) {
Expand Down Expand Up @@ -817,6 +806,19 @@ class PlaybackService : MediaLibraryService() {
return super.onStartCommand(intent, flags, startId)
}
playable != null -> {
if (Build.VERSION.SDK_INT >= 26) {
val CHANNEL_ID = "podcini playback service"
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
val channel = NotificationChannel(CHANNEL_ID, "Title", NotificationManager.IMPORTANCE_LOW).apply {
setSound(null, null)
enableVibration(false)
}
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("").setContentText("").build()
startForeground(1, notification)
}
recreateMediaSessionIfNeeded()
Logd(TAG, "onStartCommand status: $status")
val allowStreamThisTime = intent?.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false) == true
Expand All @@ -825,7 +827,8 @@ class PlaybackService : MediaLibraryService() {
if (allowStreamAlways) isAllowMobileStreaming = true
startPlaying(allowStreamThisTime)
// return super.onStartCommand(intent, flags, startId)
return START_NOT_STICKY
// return START_NOT_STICKY
return START_STICKY
}
else -> Logd(TAG, "onStartCommand case when not (keycode != -1 and playable != null)")
}
Expand Down Expand Up @@ -1163,7 +1166,7 @@ class PlaybackService : MediaLibraryService() {
} else duration_ = playable?.getDuration() ?: Playable.INVALID_TIME

if (position != Playable.INVALID_TIME && duration_ != Playable.INVALID_TIME && playable != null) {
// Log.d(TAG, "Saving current position to $position $duration")
Logd(TAG, "persistCurrentPosition to $position $duration_ ${playable.getEpisodeTitle()}")
playable.setPosition(position)
playable.setLastPlayedTime(System.currentTimeMillis())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object RealmDB {
SubscriptionLog::class,
Chapter::class))
.name("Podcini.realm")
.schemaVersion(32)
.schemaVersion(33)
.migration({ mContext ->
val oldRealm = mContext.oldRealm // old realm using the previous schema
val newRealm = mContext.newRealm // new realm using the new schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,7 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {

override fun onPlaybackPause(context: Context) {
Logd(TAG, "onPlaybackPause $position $duration")
if (position > startPosition) {
// playedDuration = playedDurationWhenStarted + position - startPosition
// playedDurationWhenStarted = playedDuration
playedDuration = playedDurationWhenStarted + position - startPosition
// playedDurationWhenStarted = playedDuration
}
if (position > startPosition) playedDuration = playedDurationWhenStarted + position - startPosition
timeSpent = timeSpentOnStart + (System.currentTimeMillis() - startTime).toInt()
startPosition = position
}
Expand All @@ -321,9 +316,7 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
override fun setChapters(chapters: List<Chapter>) {
if (episode != null) {
episode!!.chapters.clear()
for (c in chapters) {
c.episode = episode
}
for (c in chapters) c.episode = episode
episode!!.chapters.addAll(chapters)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import ac.mdiq.podcini.playback.base.InTheatre.curQueue
import ac.mdiq.podcini.playback.base.VideoMode
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.model.VolumeAdaptionSetting.Companion.fromInteger
import androidx.media3.common.C
import io.realm.kotlin.ext.realmSetOf
import io.realm.kotlin.types.EmbeddedRealmObject
import io.realm.kotlin.types.RealmSet
import io.realm.kotlin.types.annotations.Ignore

/**
* Contains preferences for a single feed.
*/
class FeedPreferences : EmbeddedRealmObject {

var feedID: Long = 0L
Expand Down Expand Up @@ -50,6 +48,15 @@ class FeedPreferences : EmbeddedRealmObject {
}
var autoDelete: Int = AutoDeleteAction.GLOBAL.code

@Ignore
var audioTypeSetting: AudioType = AudioType.SPEECH
get() = AudioType.fromCode(audioType)
set(value) {
field = value
audioType = field.code
}
var audioType: Int = AudioType.SPEECH.code

@Ignore
var volumeAdaptionSetting: VolumeAdaptionSetting = VolumeAdaptionSetting.OFF
get() = fromInteger(volumeAdaption)
Expand Down Expand Up @@ -132,16 +139,15 @@ class FeedPreferences : EmbeddedRealmObject {
autoDLInclude = value?.includeFilterRaw ?: ""
autoDLExclude = value?.excludeFilterRaw ?: ""
autoDLMinDuration = value?.minimalDurationFilter ?: -1
markExcludedPlayed = value?.markExcludedPlayed ?: false
markExcludedPlayed = value?.markExcludedPlayed == true
}
var autoDLInclude: String? = ""
var autoDLExclude: String? = ""
var autoDLMinDuration: Int = -1
var markExcludedPlayed: Boolean = false

var autoDLMaxEpisodes: Int = 3

var countingPlayed: Boolean = true
var countingPlayed: Boolean = true // relates to autoDLMaxEpisodes

@Ignore
var autoDLPolicy: AutoDownloadPolicy = AutoDownloadPolicy.ONLY_NEW
Expand Down Expand Up @@ -215,6 +221,22 @@ class FeedPreferences : EmbeddedRealmObject {
}
}

enum class AudioType(val code: Int, val tag: String) {
UNKNOWN(C.AUDIO_CONTENT_TYPE_UNKNOWN, "Unknown"),
SPEECH(C.AUDIO_CONTENT_TYPE_SPEECH, "Speech"),
MUSIC(C.AUDIO_CONTENT_TYPE_MUSIC, "Music"),
MOVIE(C.AUDIO_CONTENT_TYPE_MOVIE, "Movie");

companion object {
fun fromCode(code: Int): AudioType {
return enumValues<AudioType>().firstOrNull { it.code == code } ?: SPEECH
}
fun fromTag(tag: String): AudioType {
return enumValues<AudioType>().firstOrNull { it.tag == tag } ?: SPEECH
}
}
}

enum class AVQuality(val code: Int, val tag: String) {
GLOBAL(0, "Global"),
LOW(1, "Low"),
Expand Down
37 changes: 0 additions & 37 deletions app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
Original file line number Diff line number Diff line change
Expand Up @@ -217,40 +217,3 @@ fun NonlazyGrid(columns: Int, itemCount: Int, modifier: Modifier = Modifier, con
}
}
}

@Composable
fun AutoCompleteTextField(suggestions: List<String>) {
var text by remember { mutableStateOf("") }
var filteredSuggestions by remember { mutableStateOf(suggestions) }
var showSuggestions by remember { mutableStateOf(false) }

Column {
TextField(value = text, onValueChange = {
text = it
filteredSuggestions = suggestions.filter { item ->
item.contains(text, ignoreCase = true)
}
showSuggestions = text.isNotEmpty() && filteredSuggestions.isNotEmpty()
},
placeholder = { Text("Type something...") },
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {
}
),
modifier = Modifier.fillMaxWidth()
)

if (showSuggestions) {
LazyColumn(modifier = Modifier.fillMaxWidth().heightIn(min = 0.dp, max = 200.dp)) {
items(filteredSuggestions.size) { index ->
Text(text = filteredSuggestions[index], modifier = Modifier.clickable(onClick = {
text = filteredSuggestions[index]
showSuggestions = false
}).padding(8.dp))

}
}
}
}
}
Loading

0 comments on commit 737577a

Please sign in to comment.