feat: Enhance PlaybackService with proactive buffering and queue management for improved background playback

This commit is contained in:
skalthoff
2026-01-12 13:29:08 -08:00
parent a677ffc602
commit fca832a89b
3 changed files with 58 additions and 11 deletions

View File

@@ -111,17 +111,16 @@ GEM
ffi (>= 1.15.0)
event_emitter (0.2.6)
excon (0.112.0)
faraday (1.10.4)
faraday (1.8.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
@@ -130,13 +129,10 @@ GEM
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
@@ -255,7 +251,8 @@ GEM
mime-types-data (3.2025.0610)
mini_magick (4.13.2)
mini_mime (1.1.5)
minitest (5.25.5)
minitest (6.0.1)
prism (~> 1.5)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.4.1)
@@ -270,6 +267,7 @@ GEM
ffi
os (1.1.4)
plist (3.7.2)
prism (1.7.0)
public_suffix (4.0.7)
rake (13.3.0)
rbnacl (3.4.0)

View File

@@ -3598,7 +3598,7 @@ SPEC CHECKSUMS:
Gifu: 9f7e52357d41c0739709019eb80a71ad9aab1b6d
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
google-cast-sdk: 32f65af50d164e3c475e79ad123db3cc26fbcd37
hermes-engine: 83ac7cadb2a3a158ae6d9e4192417c5232065e99
hermes-engine: 6c2b048484247d03ebf5a9bbe01dd70c28654a1e
MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df
NitroFetch: 660adfb47f84b28db664f97b50e5dc28506ab6c1
NitroMmkv: 8ed7ef6f41b91785fc580c975f68d6d675214767

View File

@@ -1,12 +1,18 @@
import TrackPlayer, { Event } from 'react-native-track-player'
import { CarPlay } from 'react-native-carplay'
import { previous, skip } from '../hooks/player/functions/controls'
import { usePlayerQueueStore } from '../stores/player/queue'
import { optimizePlayerQueue } from './helpers/gapless'
import { PREFETCH_THRESHOLD_SECONDS } from '../configs/gapless.config'
/**
* Jellify Playback Service.
*
* Sets up event listeners for remote control events and
* runs for the duration of the app lifecycle
* runs for the duration of the app lifecycle.
*
* Also handles background queue optimization to ensure tracks
* are buffered before they're needed (fixes iOS background playback).
*/
export async function PlaybackService() {
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
@@ -23,6 +29,49 @@ export async function PlaybackService() {
TrackPlayer.addEventListener(Event.RemoteSeek, async (event) => {
await TrackPlayer.seekTo(event.position)
})
/**
* Monitor playback progress to proactively buffer upcoming tracks.
* This runs even when the app is backgrounded, ensuring the next track
* is ready before the current one ends.
*/
TrackPlayer.addEventListener(Event.PlaybackProgressUpdated, async (event) => {
const { position, duration } = event
if (!duration || duration <= 0) return
const remainingSeconds = duration - position
// When approaching the end of a track, ensure upcoming tracks are in the queue
if (remainingSeconds <= PREFETCH_THRESHOLD_SECONDS && remainingSeconds > 0) {
const queue = usePlayerQueueStore.getState().queue
const currentIndex = usePlayerQueueStore.getState().currentIndex
if (queue.length > 0 && currentIndex !== undefined) {
await optimizePlayerQueue(queue, currentIndex)
}
}
})
/**
* Handle the case where the player queue ends unexpectedly.
* This can happen if iOS throttles network requests for backgrounded apps.
* When the app queue has more tracks, add them and resume playback.
*/
TrackPlayer.addEventListener(Event.PlaybackQueueEnded, async () => {
const queue = usePlayerQueueStore.getState().queue
const currentIndex = usePlayerQueueStore.getState().currentIndex
// If there are more tracks in the app queue that aren't in the player queue
if (currentIndex !== undefined && currentIndex < queue.length - 1) {
const remainingTracks = queue.slice(currentIndex + 1)
if (remainingTracks.length > 0) {
await TrackPlayer.add(remainingTracks)
await TrackPlayer.play()
}
}
})
}
export function registerAutoService(onConnect: () => void, onDisconnect: () => void) {