@@ -3,12 +3,12 @@ package com.adamratzman.spotify.models
3
3
4
4
import com.adamratzman.spotify.SpotifyApi
5
5
import com.adamratzman.spotify.SpotifyRestAction
6
+ import com.adamratzman.spotify.annotations.SpotifyExperimentalHttpApi
6
7
import com.adamratzman.spotify.http.SpotifyEndpoint
8
+ import com.adamratzman.spotify.models.PagingTraversalType.FORWARDS
7
9
import com.adamratzman.spotify.models.serialization.toCursorBasedPagingObject
8
10
import com.adamratzman.spotify.models.serialization.toPagingObject
9
11
import com.adamratzman.spotify.utils.runBlocking
10
- import kotlin.coroutines.CoroutineContext
11
- import kotlin.reflect.KClass
12
12
import kotlinx.coroutines.Dispatchers
13
13
import kotlinx.coroutines.ExperimentalCoroutinesApi
14
14
import kotlinx.coroutines.flow.Flow
@@ -20,6 +20,8 @@ import kotlinx.coroutines.flow.toList
20
20
import kotlinx.serialization.SerialName
21
21
import kotlinx.serialization.Serializable
22
22
import kotlinx.serialization.Transient
23
+ import kotlin.coroutines.CoroutineContext
24
+ import kotlin.reflect.KClass
23
25
24
26
/*
25
27
Types used in PagingObjects and CursorBasedPagingObjects:
@@ -72,7 +74,7 @@ class PagingObject<T : Any>(
72
74
@Suppress(" UNCHECKED_CAST" )
73
75
override suspend fun getImpl (type : PagingTraversalType ): AbstractPagingObject <T >? {
74
76
val endpointFinal = endpoint!!
75
- return (if (type == PagingTraversalType . FORWARDS ) next else previous)?.let { endpoint!! .get(it) }?.let { json ->
77
+ return (if (type == FORWARDS ) next else previous)?.let { endpoint!! .get(it) }?.let { json ->
76
78
when (itemClazz) {
77
79
SimpleTrack ::class -> json.toPagingObject(SimpleTrack .serializer(), null , endpointFinal, endpointFinal.api.json)
78
80
SpotifyCategory ::class -> json.toPagingObject(SpotifyCategory .serializer(), " categories" , endpointFinal, endpointFinal.api.json)
@@ -88,6 +90,19 @@ class PagingObject<T : Any>(
88
90
}
89
91
}
90
92
93
+ @SpotifyExperimentalHttpApi
94
+ override suspend fun getWithNextImpl (total : Int ): Sequence <AbstractPagingObject <T >> {
95
+ val pagingObjects = mutableListOf<AbstractPagingObject <T >>(this )
96
+
97
+ var nxt = next?.let { getNext() }
98
+ while (pagingObjects.size < total && nxt != null ) {
99
+ pagingObjects.add(nxt)
100
+ nxt = nxt.next?.let { nxt?.getNext() }
101
+ }
102
+
103
+ return pagingObjects.distinctBy { it.href }.asSequence()
104
+ }
105
+
91
106
override suspend fun getAllImpl (): Sequence <AbstractPagingObject <T >> {
92
107
val pagingObjects = mutableListOf<AbstractPagingObject <T >>()
93
108
var prev = previous?.let { getPrevious() }
@@ -115,6 +130,16 @@ class PagingObject<T : Any>(
115
130
@Suppress(" UNCHECKED_CAST" )
116
131
suspend fun getAll () = endpoint!! .toAction { (getAllImpl() as Sequence <PagingObject <T >>).toList() }
117
132
133
+ /* *
134
+ * Synchronously retrieve the next [total] paging objects associated with this [PagingObject], including this [PagingObject].
135
+ *
136
+ * @param total The total amount of [PagingObject] to request, which includes this [PagingObject].
137
+ * @since 3.0.0
138
+ */
139
+ @SpotifyExperimentalHttpApi
140
+ @Suppress(" UNCHECKED_CAST" )
141
+ suspend fun getWithNext (total : Int ) = endpoint!! .toAction { getWithNextImpl(total) }
142
+
118
143
/* *
119
144
* Get all items of type [T] associated with the request
120
145
*/
@@ -151,38 +176,65 @@ class CursorBasedPagingObject<T : Any>(
151
176
getAllImpl() as Sequence <CursorBasedPagingObject <T >>
152
177
}
153
178
179
+ /* *
180
+ * Synchronously retrieve the next [total] paging objects associated with this [CursorBasedPagingObject], including this [CursorBasedPagingObject].
181
+ *
182
+ * @param total The total amount of [CursorBasedPagingObject] to request, which includes this [CursorBasedPagingObject].
183
+ * @since 3.0.0
184
+ */
185
+ @SpotifyExperimentalHttpApi
186
+ @Suppress(" UNCHECKED_CAST" )
187
+ fun getWithNext (total : Int ) = endpoint!! .toAction {
188
+ getWithNextImpl(total) as Sequence <CursorBasedPagingObject <T >>
189
+ }
190
+
154
191
/* *
155
192
* Get all items of type [T] associated with the request
156
193
*/
157
194
override suspend fun getAllItems (context : CoroutineContext ) = endpoint!! .toAction {
158
195
getAll().suspendComplete(context).map { it.items }.flatten().asSequence()
159
196
}
160
197
161
- @Suppress(" UNCHECKED_CAST" )
162
198
override suspend fun getImpl (type : PagingTraversalType ): AbstractPagingObject <T >? {
163
199
require(type != PagingTraversalType .BACKWARDS ) { " CursorBasedPagingObjects only can go forwards" }
164
- return next?.let {
165
- val url = endpoint!! .get(it)
166
- when (itemClazz) {
167
- PlayHistory ::class -> url.toCursorBasedPagingObject(
200
+ return next?.let { getCursorBasedPagingObject(it) }
201
+ }
202
+
203
+ @Suppress(" UNCHECKED_CAST" )
204
+ private suspend fun getCursorBasedPagingObject (url : String ): CursorBasedPagingObject <T >? {
205
+ val json = endpoint!! .get(url)
206
+ return when (itemClazz) {
207
+ PlayHistory ::class -> json.toCursorBasedPagingObject(
168
208
PlayHistory .serializer(),
169
209
null ,
170
210
endpoint!! ,
171
211
endpoint!! .api.json
172
- )
173
- Artist ::class -> url .toCursorBasedPagingObject(
212
+ )
213
+ Artist ::class -> json .toCursorBasedPagingObject(
174
214
Artist .serializer(),
175
215
null ,
176
216
endpoint!! ,
177
217
endpoint!! .api.json
178
- )
179
- else -> throw IllegalArgumentException (" Unknown type in $href " )
180
- } as ? CursorBasedPagingObject <T >
181
- }
218
+ )
219
+ else -> throw IllegalArgumentException (" Unknown type in $href " )
220
+ } as ? CursorBasedPagingObject <T >
182
221
}
183
222
184
223
override suspend fun getAllImpl (): Sequence <AbstractPagingObject <T >> {
185
- return generateSequence(this ) { runBlocking { it.getImpl(PagingTraversalType .FORWARDS ) as ? CursorBasedPagingObject <T > } }
224
+ return generateSequence(this ) { runBlocking { it.getImpl(FORWARDS ) as ? CursorBasedPagingObject <T > } }
225
+ }
226
+
227
+ @SpotifyExperimentalHttpApi
228
+ override suspend fun getWithNextImpl (total : Int ): Sequence <AbstractPagingObject <T >> {
229
+ val pagingObjects = mutableListOf<AbstractPagingObject <T >>(this )
230
+
231
+ var nxt = getNext()
232
+ while (pagingObjects.size < total && nxt != null ) {
233
+ pagingObjects.add(nxt)
234
+ nxt = nxt.next?.let { nxt?.getNext() }
235
+ }
236
+
237
+ return pagingObjects.distinctBy { it.href }.asSequence()
186
238
}
187
239
}
188
240
@@ -223,9 +275,18 @@ abstract class AbstractPagingObject<T : Any>(
223
275
internal abstract suspend fun getImpl (type : PagingTraversalType ): AbstractPagingObject <T >?
224
276
internal abstract suspend fun getAllImpl (): Sequence <AbstractPagingObject <T >>
225
277
278
+ /* *
279
+ * Synchronously retrieve the next [total] paging objects associated with this [AbstractPagingObject], including this [AbstractPagingObject].
280
+ *
281
+ * @param total The total amount of [AbstractPagingObject] to request, which includes this [AbstractPagingObject].
282
+ * @since 3.0.0
283
+ */
284
+ @SpotifyExperimentalHttpApi
285
+ internal abstract suspend fun getWithNextImpl (total : Int ): Sequence <AbstractPagingObject <T >>
286
+
226
287
internal abstract suspend fun getAllItems (context : CoroutineContext = Dispatchers .Default ): SpotifyRestAction <Sequence <T >>
227
288
228
- private suspend fun getNextImpl () = getImpl(PagingTraversalType . FORWARDS )
289
+ private suspend fun getNextImpl () = getImpl(FORWARDS )
229
290
private suspend fun getPreviousImpl () = getImpl(PagingTraversalType .BACKWARDS )
230
291
231
292
suspend fun getNext (): AbstractPagingObject <T >? = getNextImpl()
@@ -259,12 +320,12 @@ abstract class AbstractPagingObject<T : Any>(
259
320
260
321
@ExperimentalCoroutinesApi
261
322
fun flowStartOrdered (): Flow <AbstractPagingObject <T >> =
262
- flow {
263
- if (previous == null ) return @flow
264
- flowBackward().toList().reversed().also {
265
- emitAll(it.asFlow())
266
- }
267
- }.flowOn(Dispatchers .Default )
323
+ flow {
324
+ if (previous == null ) return @flow
325
+ flowBackward().toList().reversed().also {
326
+ emitAll(it.asFlow())
327
+ }
328
+ }.flowOn(Dispatchers .Default )
268
329
269
330
@ExperimentalCoroutinesApi
270
331
fun flowEndOrdered (): Flow <AbstractPagingObject <T >> = flowForward()
0 commit comments